Commit 6bdb7aa4 authored by Tom Lane's avatar Tom Lane

libpq can now talk to either 3.0 or 2.0 protocol servers. It first tries

protocol 3, then falls back to 2 if postmaster rejects the startup packet
with an old-format error message.  A side benefit of the rewrite is that
SSL-encrypted connections can now be made without blocking.  (I think,
anyway, but do not have a good way to test.)
parent 152ce7a4
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.7 2003/04/22 03:52:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.8 2003/06/08 17:42:59 tgl Exp $
*
* This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
......@@ -72,19 +72,20 @@ getaddrinfo2(const char *hostname, const char *servname,
/*
* freeaddrinfo2 - free IPv6 addrinfo structures
* freeaddrinfo2 - free addrinfo structures for IPv4, IPv6, or Unix
*/
void
freeaddrinfo2(int hint_ai_family, struct addrinfo *ai)
freeaddrinfo2(struct addrinfo *ai)
{
if (ai != NULL)
{
#ifdef HAVE_UNIX_SOCKETS
if (hint_ai_family == AF_UNIX)
if (ai->ai_family == AF_UNIX)
{
struct addrinfo *p;
while (ai != NULL)
{
p = ai;
struct addrinfo *p = ai;
ai = ai->ai_next;
free(p->ai_addr);
free(p);
......@@ -93,6 +94,7 @@ freeaddrinfo2(int hint_ai_family, struct addrinfo *ai)
else
#endif /* HAVE_UNIX_SOCKETS */
freeaddrinfo(ai);
}
}
......
......@@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.154 2003/05/29 19:15:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.155 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -242,8 +242,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: getaddrinfo2(): %s",
gai_strerror(ret));
if (addrs != NULL)
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
......@@ -251,7 +250,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: socket(): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
......@@ -262,7 +261,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: setsockopt(SO_REUSEADDR): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
}
......@@ -279,7 +278,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
sock_path);
else
elog(LOG, "\tIf not, wait a few seconds and retry.");
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
......@@ -288,7 +287,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
if (Setup_AF_UNIX() != STATUS_OK)
{
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
}
......@@ -308,12 +307,12 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: listen(): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
*fdP = fd;
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_OK;
}
......
......@@ -5,7 +5,7 @@
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $Id: ip.h,v 1.3 2003/04/02 00:49:28 tgl Exp $
* $Id: ip.h,v 1.4 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,7 +19,7 @@
extern int getaddrinfo2(const char *hostname, const char *servname,
const struct addrinfo *hintp,
struct addrinfo **result);
extern void freeaddrinfo2(int hint_ai_family, struct addrinfo *ai);
extern void freeaddrinfo2(struct addrinfo *ai);
extern char *SockAddr_ntop(const SockAddr *sa, char *dst, size_t cnt,
int v4conv);
......
......@@ -4,7 +4,7 @@
#
# Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.79 2003/05/10 02:05:50 momjian Exp $
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.80 2003/06/08 17:43:00 tgl Exp $
#
#-------------------------------------------------------------------------
......@@ -21,7 +21,7 @@ SO_MINOR_VERSION= 1
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
pqexpbuffer.o pqsignal.o fe-secure.o \
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
dllist.o md5.o ip.o wchar.o encnames.o \
$(filter crypt.o getaddrinfo.o inet_aton.o snprintf.o strerror.o path.o, $(LIBOBJS))
......
......@@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.78 2003/05/16 04:58:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.79 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -559,7 +559,11 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
default:
return STATUS_ERROR;
}
/* Packet has a message type as of protocol 3.0 */
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
else
ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
if (areq == AUTH_REQ_MD5)
free(crypt_pwd);
return ret;
......
This diff is collapsed.
This diff is collapsed.
......@@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.91 2003/04/25 01:24:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.92 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -58,8 +58,6 @@
#include "pqsignal.h"
#include "mb/pg_wchar.h"
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
static int pqSendSome(PGconn *conn, int len);
......@@ -227,7 +225,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
(unsigned long) bytes);
DONOTICE(conn, noticeBuf);
PGDONOTICE(conn, noticeBuf);
return EOF;
}
......@@ -265,7 +263,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
(unsigned long) bytes);
DONOTICE(conn, noticeBuf);
PGDONOTICE(conn, noticeBuf);
return EOF;
}
......@@ -401,38 +399,57 @@ pqCheckInBufferSpace(int bytes_needed, PGconn *conn)
* msg_type is the message type byte, or 0 for a message without type byte
* (only startup messages have no type byte)
*
* force_len forces the message to have a length word; otherwise, we add
* a length word if protocol 3.
*
* Returns 0 on success, EOF on error
*
* The idea here is that we construct the message in conn->outBuffer,
* beginning just past any data already in outBuffer (ie, at
* outBuffer+outCount). We enlarge the buffer as needed to hold the message.
* When the message is complete, we fill in the length word and then advance
* outCount past the message, making it eligible to send. The state
* variable conn->outMsgStart points to the incomplete message's length word
* (it is either outCount or outCount+1 depending on whether there is a
* type byte). The state variable conn->outMsgEnd is the end of the data
* collected so far.
* When the message is complete, we fill in the length word (if needed) and
* then advance outCount past the message, making it eligible to send.
*
* The state variable conn->outMsgStart points to the incomplete message's
* length word: it is either outCount or outCount+1 depending on whether
* there is a type byte. If we are sending a message without length word
* (pre protocol 3.0 only), then outMsgStart is -1. The state variable
* conn->outMsgEnd is the end of the data collected so far.
*/
int
pqPutMsgStart(char msg_type, PGconn *conn)
pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
{
int lenPos;
int endPos;
/* where the message length word will go */
/* allow room for message type byte */
if (msg_type)
lenPos = conn->outCount + 1;
endPos = conn->outCount + 1;
else
lenPos = conn->outCount;
/* make sure there is room for it */
if (pqCheckOutBufferSpace(lenPos + 4, conn))
endPos = conn->outCount;
/* do we want a length word? */
if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
{
lenPos = endPos;
/* allow room for message length */
endPos += 4;
}
else
{
lenPos = -1;
}
/* make sure there is room for message header */
if (pqCheckOutBufferSpace(endPos, conn))
return EOF;
/* okay, save the message type byte if any */
if (msg_type)
conn->outBuffer[conn->outCount] = msg_type;
/* set up the message pointers */
conn->outMsgStart = lenPos;
conn->outMsgEnd = lenPos + 4;
/* length word will be filled in by pqPutMsgEnd */
conn->outMsgEnd = endPos;
/* length word, if needed, will be filled in by pqPutMsgEnd */
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg %c\n",
......@@ -472,14 +489,20 @@ pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
int
pqPutMsgEnd(PGconn *conn)
{
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
msgLen);
conn->outMsgEnd - conn->outCount);
/* Fill in length word if needed */
if (conn->outMsgStart >= 0)
{
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
msgLen = htonl(msgLen);
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
}
/* Make message eligible to send */
conn->outCount = conn->outMsgEnd;
if (conn->outCount >= 8192)
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.22 2003/04/10 23:03:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.23 2003/06/08 17:43:00 tgl Exp $
*
* NOTES
* The client *requires* a valid server certificate. Since
......@@ -132,7 +132,7 @@ static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static int initialize_SSL(PGconn *);
static void destroy_SSL(void);
static int open_client_SSL(PGconn *);
static PostgresPollingStatusType open_client_SSL(PGconn *);
static void close_SSL(PGconn *);
static const char *SSLerrmessage(void);
#endif
......@@ -231,16 +231,30 @@ pqsecure_destroy(void)
/*
* Attempt to negotiate secure session.
*/
int
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
int r = 0;
#ifdef USE_SSL
r = open_client_SSL(conn);
/* First time through? */
if (conn->ssl == NULL)
{
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
}
/* Begin or continue the actual handshake */
return open_client_SSL(conn);
#else
/* shouldn't get here */
return PGRES_POLLING_FAILED;
#endif
return r;
}
/*
......@@ -273,8 +287,15 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
n = 0;
break;
case SSL_ERROR_WANT_WRITE:
/* XXX to support nonblock I/O, we should return 0 here */
/*
* Returning 0 here would cause caller to wait for read-ready,
* which is not correct since what SSL wants is wait for
* write-ready. The former could get us stuck in an infinite
* wait, so don't risk it; busy-loop instead.
*/
goto rloop;
case SSL_ERROR_SYSCALL:
if (n == -1)
......@@ -322,16 +343,22 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
#ifdef USE_SSL
if (conn->ssl)
{
wloop:
n = SSL_write(conn->ssl, ptr, len);
switch (SSL_get_error(conn->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
/*
* Returning 0 here causes caller to wait for write-ready,
* which is not really the right thing, but it's the best
* we can do.
*/
n = 0;
break;
case SSL_ERROR_WANT_WRITE:
/* XXX to support nonblock I/O, we should return 0 here */
goto wloop;
n = 0;
break;
case SSL_ERROR_SYSCALL:
if (n == -1)
printfPQExpBuffer(&conn->errorMessage,
......@@ -802,23 +829,45 @@ destroy_SSL(void)
/*
* Attempt to negotiate SSL connection.
*/
static int
static PostgresPollingStatusType
open_client_SSL(PGconn *conn)
{
#ifdef NOT_USED
int r;
#endif
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
r = SSL_connect(conn->ssl);
if (r <= 0)
{
switch (SSL_get_error(conn->ssl, r))
{
case SSL_ERROR_WANT_READ:
return PGRES_POLLING_READING;
case SSL_ERROR_WANT_WRITE:
return PGRES_POLLING_WRITING;
case SSL_ERROR_SYSCALL:
if (r == -1)
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
libpq_gettext("SSL SYSCALL error: %s\n"),
SOCK_STRERROR(SOCK_ERRNO));
else
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL SYSCALL error: EOF detected\n"));
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
case SSL_ERROR_SSL:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), SSLerrmessage());
close_SSL(conn);
return PGRES_POLLING_FAILED;
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("Unknown SSL error code\n"));
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
}
/* check the certificate chain of the server */
......@@ -836,7 +885,7 @@ open_client_SSL(PGconn *conn)
libpq_gettext("certificate could not be validated: %s\n"),
X509_verify_cert_error_string(r));
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
#endif
......@@ -848,7 +897,7 @@ open_client_SSL(PGconn *conn)
libpq_gettext("certificate could not be obtained: %s\n"),
SSLerrmessage());
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
X509_NAME_oneline(X509_get_subject_name(conn->peer),
......@@ -871,11 +920,12 @@ open_client_SSL(PGconn *conn)
if (verify_peer(conn) == -1)
{
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
#endif
return 0;
/* SSL handshake is complete */
return PGRES_POLLING_OK;
}
/*
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-fe.h,v 1.92 2003/04/19 00:02:30 tgl Exp $
* $Id: libpq-fe.h,v 1.93 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -38,9 +38,9 @@ extern "C"
typedef enum
{
/*
* Although you may decide to change this list in some way, values
* which become unused should never be removed, nor should constants
* be redefined - that would break compatibility with existing code.
* Although it is okay to add to this list, values which become unused
* should never be removed, nor should constants be redefined - that would
* break compatibility with existing code.
*/
CONNECTION_OK,
CONNECTION_BAD,
......@@ -56,7 +56,9 @@ typedef enum
* postmaster. */
CONNECTION_AUTH_OK, /* Received authentication; waiting for
* backend startup. */
CONNECTION_SETENV /* Negotiating environment. */
CONNECTION_SETENV, /* Negotiating environment. */
CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
CONNECTION_NEEDED /* Internal state: connect() needed */
} ConnStatusType;
typedef enum
......@@ -71,7 +73,7 @@ typedef enum
typedef enum
{
PGRES_EMPTY_QUERY = 0,
PGRES_EMPTY_QUERY = 0, /* empty query string was executed */
PGRES_COMMAND_OK, /* a query command that doesn't return
* anything was executed properly by the
* backend */
......@@ -82,8 +84,8 @@ typedef enum
PGRES_COPY_IN, /* Copy In data transfer in progress */
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
* the backend */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR
PGRES_NONFATAL_ERROR, /* notice or warning message */
PGRES_FATAL_ERROR /* query failed */
} ExecStatusType;
/* PGconn encapsulates a connection to the backend.
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment