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.)
This commit is contained in:
Tom Lane 2003-06-08 17:43:00 +00:00
parent 152ce7a490
commit 6bdb7aa4db
13 changed files with 3334 additions and 1554 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@ -72,27 +72,29 @@ getaddrinfo2(const char *hostname, const char *servname,
/* /*
* freeaddrinfo2 - free IPv6 addrinfo structures * freeaddrinfo2 - free addrinfo structures for IPv4, IPv6, or Unix
*/ */
void void
freeaddrinfo2(int hint_ai_family, struct addrinfo *ai) freeaddrinfo2(struct addrinfo *ai)
{ {
#ifdef HAVE_UNIX_SOCKETS if (ai != NULL)
if (hint_ai_family == AF_UNIX)
{ {
struct addrinfo *p; #ifdef HAVE_UNIX_SOCKETS
if (ai->ai_family == AF_UNIX)
while (ai != NULL)
{ {
p = ai; while (ai != NULL)
ai = ai->ai_next; {
free(p->ai_addr); struct addrinfo *p = ai;
free(p);
ai = ai->ai_next;
free(p->ai_addr);
free(p);
}
} }
} else
else
#endif /* HAVE_UNIX_SOCKETS */ #endif /* HAVE_UNIX_SOCKETS */
freeaddrinfo(ai); freeaddrinfo(ai);
}
} }

View File

@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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", elog(LOG, "server socket failure: getaddrinfo2(): %s",
gai_strerror(ret)); gai_strerror(ret));
if (addrs != NULL) freeaddrinfo2(addrs);
freeaddrinfo2(hint.ai_family, addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
@ -251,7 +250,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{ {
elog(LOG, "server socket failure: socket(): %s", elog(LOG, "server socket failure: socket(): %s",
strerror(errno)); strerror(errno));
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
@ -262,7 +261,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{ {
elog(LOG, "server socket failure: setsockopt(SO_REUSEADDR): %s", elog(LOG, "server socket failure: setsockopt(SO_REUSEADDR): %s",
strerror(errno)); strerror(errno));
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
} }
@ -279,7 +278,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
sock_path); sock_path);
else else
elog(LOG, "\tIf not, wait a few seconds and retry."); elog(LOG, "\tIf not, wait a few seconds and retry.");
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
@ -288,7 +287,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{ {
if (Setup_AF_UNIX() != STATUS_OK) if (Setup_AF_UNIX() != STATUS_OK)
{ {
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
} }
@ -308,12 +307,12 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{ {
elog(LOG, "server socket failure: listen(): %s", elog(LOG, "server socket failure: listen(): %s",
strerror(errno)); strerror(errno));
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_ERROR; return STATUS_ERROR;
} }
*fdP = fd; *fdP = fd;
freeaddrinfo2(hint.ai_family, addrs); freeaddrinfo2(addrs);
return STATUS_OK; return STATUS_OK;
} }

View File

@ -5,7 +5,7 @@
* *
* Copyright (c) 2003, PostgreSQL Global Development Group * 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, extern int getaddrinfo2(const char *hostname, const char *servname,
const struct addrinfo *hintp, const struct addrinfo *hintp,
struct addrinfo **result); 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, extern char *SockAddr_ntop(const SockAddr *sa, char *dst, size_t cnt,
int v4conv); int v4conv);

View File

@ -4,7 +4,7 @@
# #
# Copyright (c) 1994, Regents of the University of California # 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)"' 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 \ 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 \ 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)) $(filter crypt.o getaddrinfo.o inet_aton.o snprintf.o strerror.o path.o, $(LIBOBJS))

View File

@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes). * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
* *
* IDENTIFICATION * 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: default:
return STATUS_ERROR; return STATUS_ERROR;
} }
ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1); /* 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) if (areq == AUTH_REQ_MD5)
free(crypt_pwd); free(crypt_pwd);
return ret; return ret;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 "pqsignal.h"
#include "mb/pg_wchar.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 pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
static int pqSendSome(PGconn *conn, int len); static int pqSendSome(PGconn *conn, int len);
@ -227,7 +225,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf), snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqGetInt\n"), libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
(unsigned long) bytes); (unsigned long) bytes);
DONOTICE(conn, noticeBuf); PGDONOTICE(conn, noticeBuf);
return EOF; return EOF;
} }
@ -265,7 +263,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf), snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqPutInt\n"), libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
(unsigned long) bytes); (unsigned long) bytes);
DONOTICE(conn, noticeBuf); PGDONOTICE(conn, noticeBuf);
return EOF; 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 * msg_type is the message type byte, or 0 for a message without type byte
* (only startup messages have no 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 * Returns 0 on success, EOF on error
* *
* The idea here is that we construct the message in conn->outBuffer, * The idea here is that we construct the message in conn->outBuffer,
* beginning just past any data already in outBuffer (ie, at * beginning just past any data already in outBuffer (ie, at
* outBuffer+outCount). We enlarge the buffer as needed to hold the message. * 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 * When the message is complete, we fill in the length word (if needed) and
* outCount past the message, making it eligible to send. The state * then advance outCount past the message, making it eligible to send.
* variable conn->outMsgStart points to the incomplete message's length word *
* (it is either outCount or outCount+1 depending on whether there is a * The state variable conn->outMsgStart points to the incomplete message's
* type byte). The state variable conn->outMsgEnd is the end of the data * length word: it is either outCount or outCount+1 depending on whether
* collected so far. * 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 int
pqPutMsgStart(char msg_type, PGconn *conn) pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
{ {
int lenPos; int lenPos;
int endPos;
/* where the message length word will go */ /* allow room for message type byte */
if (msg_type) if (msg_type)
lenPos = conn->outCount + 1; endPos = conn->outCount + 1;
else else
lenPos = conn->outCount; endPos = conn->outCount;
/* make sure there is room for it */
if (pqCheckOutBufferSpace(lenPos + 4, conn)) /* 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; return EOF;
/* okay, save the message type byte if any */ /* okay, save the message type byte if any */
if (msg_type) if (msg_type)
conn->outBuffer[conn->outCount] = msg_type; conn->outBuffer[conn->outCount] = msg_type;
/* set up the message pointers */ /* set up the message pointers */
conn->outMsgStart = lenPos; conn->outMsgStart = lenPos;
conn->outMsgEnd = lenPos + 4; conn->outMsgEnd = endPos;
/* length word will be filled in by pqPutMsgEnd */ /* length word, if needed, will be filled in by pqPutMsgEnd */
if (conn->Pfdebug) if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg %c\n", fprintf(conn->Pfdebug, "To backend> Msg %c\n",
@ -472,14 +489,20 @@ pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
int int
pqPutMsgEnd(PGconn *conn) pqPutMsgEnd(PGconn *conn)
{ {
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
if (conn->Pfdebug) if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n", fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
msgLen); conn->outMsgEnd - conn->outCount);
msgLen = htonl(msgLen); /* Fill in length word if needed */
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4); 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; conn->outCount = conn->outMsgEnd;
if (conn->outCount >= 8192) if (conn->outCount >= 8192)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* The client *requires* a valid server certificate. Since * 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 client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static int initialize_SSL(PGconn *); static int initialize_SSL(PGconn *);
static void destroy_SSL(void); static void destroy_SSL(void);
static int open_client_SSL(PGconn *); static PostgresPollingStatusType open_client_SSL(PGconn *);
static void close_SSL(PGconn *); static void close_SSL(PGconn *);
static const char *SSLerrmessage(void); static const char *SSLerrmessage(void);
#endif #endif
@ -231,16 +231,30 @@ pqsecure_destroy(void)
/* /*
* Attempt to negotiate secure session. * Attempt to negotiate secure session.
*/ */
int PostgresPollingStatusType
pqsecure_open_client(PGconn *conn) pqsecure_open_client(PGconn *conn)
{ {
int r = 0;
#ifdef USE_SSL #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 #endif
return r;
} }
/* /*
@ -273,8 +287,15 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
break; break;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
n = 0;
break;
case SSL_ERROR_WANT_WRITE: 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; goto rloop;
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
if (n == -1) if (n == -1)
@ -322,16 +343,22 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
#ifdef USE_SSL #ifdef USE_SSL
if (conn->ssl) if (conn->ssl)
{ {
wloop:
n = SSL_write(conn->ssl, ptr, len); n = SSL_write(conn->ssl, ptr, len);
switch (SSL_get_error(conn->ssl, n)) switch (SSL_get_error(conn->ssl, n))
{ {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
break; break;
case SSL_ERROR_WANT_READ: 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: case SSL_ERROR_WANT_WRITE:
/* XXX to support nonblock I/O, we should return 0 here */ n = 0;
goto wloop; break;
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
if (n == -1) if (n == -1)
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
@ -802,23 +829,45 @@ destroy_SSL(void)
/* /*
* Attempt to negotiate SSL connection. * Attempt to negotiate SSL connection.
*/ */
static int static PostgresPollingStatusType
open_client_SSL(PGconn *conn) open_client_SSL(PGconn *conn)
{ {
#ifdef NOT_USED
int r; int r;
#endif
if (!(conn->ssl = SSL_new(SSL_context)) || r = SSL_connect(conn->ssl);
!SSL_set_app_data(conn->ssl, conn) || if (r <= 0)
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
{ {
printfPQExpBuffer(&conn->errorMessage, switch (SSL_get_error(conn->ssl, r))
libpq_gettext("could not establish SSL connection: %s\n"), {
SSLerrmessage()); case SSL_ERROR_WANT_READ:
close_SSL(conn); return PGRES_POLLING_READING;
return -1;
case SSL_ERROR_WANT_WRITE:
return PGRES_POLLING_WRITING;
case SSL_ERROR_SYSCALL:
if (r == -1)
printfPQExpBuffer(&conn->errorMessage,
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 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 */ /* 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"), libpq_gettext("certificate could not be validated: %s\n"),
X509_verify_cert_error_string(r)); X509_verify_cert_error_string(r));
close_SSL(conn); close_SSL(conn);
return -1; return PGRES_POLLING_FAILED;
} }
#endif #endif
@ -848,7 +897,7 @@ open_client_SSL(PGconn *conn)
libpq_gettext("certificate could not be obtained: %s\n"), libpq_gettext("certificate could not be obtained: %s\n"),
SSLerrmessage()); SSLerrmessage());
close_SSL(conn); close_SSL(conn);
return -1; return PGRES_POLLING_FAILED;
} }
X509_NAME_oneline(X509_get_subject_name(conn->peer), X509_NAME_oneline(X509_get_subject_name(conn->peer),
@ -871,11 +920,12 @@ open_client_SSL(PGconn *conn)
if (verify_peer(conn) == -1) if (verify_peer(conn) == -1)
{ {
close_SSL(conn); close_SSL(conn);
return -1; return PGRES_POLLING_FAILED;
} }
#endif #endif
return 0; /* SSL handshake is complete */
return PGRES_POLLING_OK;
} }
/* /*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 typedef enum
{ {
/* /*
* Although you may decide to change this list in some way, values * Although it is okay to add to this list, values which become unused
* which become unused should never be removed, nor should constants * should never be removed, nor should constants be redefined - that would
* be redefined - that would break compatibility with existing code. * break compatibility with existing code.
*/ */
CONNECTION_OK, CONNECTION_OK,
CONNECTION_BAD, CONNECTION_BAD,
@ -56,7 +56,9 @@ typedef enum
* postmaster. */ * postmaster. */
CONNECTION_AUTH_OK, /* Received authentication; waiting for CONNECTION_AUTH_OK, /* Received authentication; waiting for
* backend startup. */ * backend startup. */
CONNECTION_SETENV /* Negotiating environment. */ CONNECTION_SETENV, /* Negotiating environment. */
CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
CONNECTION_NEEDED /* Internal state: connect() needed */
} ConnStatusType; } ConnStatusType;
typedef enum typedef enum
@ -71,7 +73,7 @@ typedef enum
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 PGRES_COMMAND_OK, /* a query command that doesn't return
* anything was executed properly by the * anything was executed properly by the
* backend */ * backend */
@ -82,8 +84,8 @@ typedef enum
PGRES_COPY_IN, /* Copy In data transfer in progress */ PGRES_COPY_IN, /* Copy In data transfer in progress */
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
* the backend */ * the backend */
PGRES_NONFATAL_ERROR, PGRES_NONFATAL_ERROR, /* notice or warning message */
PGRES_FATAL_ERROR PGRES_FATAL_ERROR /* query failed */
} ExecStatusType; } ExecStatusType;
/* PGconn encapsulates a connection to the backend. /* PGconn encapsulates a connection to the backend.

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-int.h,v 1.70 2003/05/08 18:33:39 tgl Exp $ * $Id: libpq-int.h,v 1.71 2003/06/08 17:43:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -35,6 +35,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
#include "postgres_fe.h" #include "postgres_fe.h"
/* include stuff common to fe and be */ /* include stuff common to fe and be */
#include "getaddrinfo.h"
#include "libpq/pqcomm.h" #include "libpq/pqcomm.h"
#include "lib/dllist.h" #include "lib/dllist.h"
/* include stuff found in fe only */ /* include stuff found in fe only */
@ -45,23 +46,9 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
#include <openssl/err.h> #include <openssl/err.h>
#endif #endif
/* libpq supports this version of the frontend/backend protocol.
*
* NB: we used to use PG_PROTOCOL_LATEST from the backend pqcomm.h file,
* but that's not really the right thing: just recompiling libpq
* against a more recent backend isn't going to magically update it
* for most sorts of protocol changes. So, when you change libpq
* to support a different protocol revision, you have to change this
* constant too. PG_PROTOCOL_EARLIEST and PG_PROTOCOL_LATEST in
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,0)
/* /*
* POSTGRES backend dependent Constants. * POSTGRES backend dependent Constants.
*/ */
#define PQERRORMSG_LENGTH 1024 #define PQERRORMSG_LENGTH 1024
#define CMDSTATUS_LEN 40 #define CMDSTATUS_LEN 40
@ -96,20 +83,22 @@ typedef struct pgresAttDesc
int atttypmod; /* type-specific modifier info */ int atttypmod; /* type-specific modifier info */
} PGresAttDesc; } PGresAttDesc;
/* Data for a single attribute of a single tuple */ /*
* Data for a single attribute of a single tuple
/* We use char* for Attribute values. *
The value pointer always points to a null-terminated area; we add a * We use char* for Attribute values.
null (zero) byte after whatever the backend sends us. This is only *
particularly useful for text tuples ... with a binary value, the * The value pointer always points to a null-terminated area; we add a
value might have embedded nulls, so the application can't use C string * null (zero) byte after whatever the backend sends us. This is only
operators on it. But we add a null anyway for consistency. * particularly useful for text tuples ... with a binary value, the
Note that the value itself does not contain a length word. * value might have embedded nulls, so the application can't use C string
* operators on it. But we add a null anyway for consistency.
A NULL attribute is a special case in two ways: its len field is NULL_LEN * Note that the value itself does not contain a length word.
and its value field points to null_field in the owning PGresult. All the *
NULL attributes in a query result point to the same place (there's no need * A NULL attribute is a special case in two ways: its len field is NULL_LEN
to store a null string separately for each one). * and its value field points to null_field in the owning PGresult. All the
* NULL attributes in a query result point to the same place (there's no need
* to store a null string separately for each one).
*/ */
#define NULL_LEN (-1) /* pg_result len for NULL value */ #define NULL_LEN (-1) /* pg_result len for NULL value */
@ -135,15 +124,6 @@ struct pg_result
int binary; /* binary tuple values if binary == 1, int binary; /* binary tuple values if binary == 1,
* otherwise text */ * otherwise text */
/*
* The conn link in PGresult is no longer used by any libpq code. It
* should be removed entirely, because it could be a dangling link
* (the application could keep the PGresult around longer than it
* keeps the PGconn!) But there may be apps out there that depend on
* it, so we will leave it here at least for a release or so.
*/
PGconn *xconn; /* connection we did the query on, if any */
/* /*
* These fields are copied from the originating PGconn, so that * These fields are copied from the originating PGconn, so that
* operations on the PGresult don't have to reference the PGconn. * operations on the PGresult don't have to reference the PGconn.
@ -194,6 +174,35 @@ typedef enum
PGASYNC_COPY_OUT /* Copy Out data transfer in progress */ PGASYNC_COPY_OUT /* Copy Out data transfer in progress */
} PGAsyncStatusType; } PGAsyncStatusType;
/* PGSetenvStatusType defines the state of the PQSetenv state machine */
/* (this is used only for 2.0-protocol connections) */
typedef enum
{
SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
SETENV_STATE_QUERY1_SEND, /* About to send a status query */
SETENV_STATE_QUERY1_WAIT, /* Waiting for query to complete */
SETENV_STATE_QUERY2_SEND, /* About to send a status query */
SETENV_STATE_QUERY2_WAIT, /* Waiting for query to complete */
SETENV_STATE_IDLE
} PGSetenvStatusType;
/* Typedef for the EnvironmentOptions[] array */
typedef struct PQEnvironmentOption
{
const char *envName, /* name of an environment variable */
*pgName; /* name of corresponding SET variable */
} PQEnvironmentOption;
/* Typedef for parameter-status list entries */
typedef struct pgParameterStatus
{
struct pgParameterStatus *next; /* list link */
char *name; /* parameter name */
char *value; /* parameter value */
/* Note: name and value are stored in same malloc block as struct is */
} pgParameterStatus;
/* large-object-access data ... allocated only if large-object code is used. */ /* large-object-access data ... allocated only if large-object code is used. */
typedef struct pgLobjfuncs typedef struct pgLobjfuncs
{ {
@ -207,7 +216,8 @@ typedef struct pgLobjfuncs
Oid fn_lo_write; /* OID of backend function LOwrite */ Oid fn_lo_write; /* OID of backend function LOwrite */
} PGlobjfuncs; } PGlobjfuncs;
/* PGconn stores all the state data associated with a single connection /*
* PGconn stores all the state data associated with a single connection
* to a backend. * to a backend.
*/ */
struct pg_conn struct pg_conn
@ -254,12 +264,21 @@ struct pg_conn
SockAddr laddr; /* Local address */ SockAddr laddr; /* Local address */
SockAddr raddr; /* Remote address */ SockAddr raddr; /* Remote address */
int raddr_len; /* Length of remote address */ int raddr_len; /* Length of remote address */
ProtocolVersion pversion; /* FE/BE protocol version in use */
char sversion[8]; /* The first few bytes of server version */
/* Transient state needed while establishing connection */
struct addrinfo *addrlist; /* list of possible backend addresses */
struct addrinfo *addr_cur; /* the one currently being tried */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
const PQEnvironmentOption *next_eo;
/* Miscellaneous stuff */ /* Miscellaneous stuff */
int be_pid; /* PID of backend --- needed for cancels */ int be_pid; /* PID of backend --- needed for cancels */
int be_key; /* key of backend --- needed for cancels */ int be_key; /* key of backend --- needed for cancels */
char md5Salt[4]; /* password salt received from backend */ char md5Salt[4]; /* password salt received from backend */
char cryptSalt[2]; /* password salt received from backend */ char cryptSalt[2]; /* password salt received from backend */
pgParameterStatus *pstatus; /* ParameterStatus data */
int client_encoding; /* encoding id */ int client_encoding; /* encoding id */
PGlobjfuncs *lobjfuncs; /* private state for large-object access PGlobjfuncs *lobjfuncs; /* private state for large-object access
* fns */ * fns */
@ -279,7 +298,8 @@ struct pg_conn
int outCount; /* number of chars waiting in buffer */ int outCount; /* number of chars waiting in buffer */
/* State for constructing messages in outBuffer */ /* State for constructing messages in outBuffer */
int outMsgStart; /* offset to msg start (length word) */ int outMsgStart; /* offset to msg start (length word);
* if -1, msg has no length word */
int outMsgEnd; /* offset to msg end (so far) */ int outMsgEnd; /* offset to msg end (so far) */
/* Status for asynchronous result construction */ /* Status for asynchronous result construction */
@ -324,10 +344,46 @@ extern int pqPacketSend(PGconn *conn, char pack_type,
/* === in fe-exec.c === */ /* === in fe-exec.c === */
extern void pqSetResultError(PGresult *res, const char *msg); extern void pqSetResultError(PGresult *res, const char *msg);
extern void pqCatenateResultError(PGresult *res, const char *msg);
extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
extern char *pqResultStrdup(PGresult *res, const char *str); extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn); extern void pqClearAsyncResult(PGconn *conn);
extern int pqGetErrorNotice(PGconn *conn, bool isError); extern void pqSaveErrorResult(PGconn *conn);
extern PGresult *pqPrepareAsyncResult(PGconn *conn);
extern int pqAddTuple(PGresult *res, PGresAttValue *tup);
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
const char *value);
extern const char *pqGetParameterStatus(PGconn *conn, const char *name);
extern void pqHandleSendFailure(PGconn *conn);
/* === in fe-protocol2.c === */
extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn);
extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput2(PGconn *conn);
extern int pqGetline2(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy2(PGconn *conn);
extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid,
int *result_buf, int *actual_result_len,
int result_is_int,
const PQArgBlock *args, int nargs);
/* === in fe-protocol3.c === */
extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput3(PGconn *conn);
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy3(PGconn *conn);
extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
int *result_buf, int *actual_result_len,
int result_is_int,
const PQArgBlock *args, int nargs);
/* === in fe-misc.c === */ /* === in fe-misc.c === */
@ -345,7 +401,7 @@ extern int pqGetnchar(char *s, size_t len, PGconn *conn);
extern int pqPutnchar(const char *s, size_t len, PGconn *conn); extern int pqPutnchar(const char *s, size_t len, PGconn *conn);
extern int pqGetInt(int *result, size_t bytes, PGconn *conn); extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
extern int pqPutInt(int value, size_t bytes, PGconn *conn); extern int pqPutInt(int value, size_t bytes, PGconn *conn);
extern int pqPutMsgStart(char msg_type, PGconn *conn); extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn);
extern int pqPutMsgEnd(PGconn *conn); extern int pqPutMsgEnd(PGconn *conn);
extern int pqReadData(PGconn *conn); extern int pqReadData(PGconn *conn);
extern int pqFlush(PGconn *conn); extern int pqFlush(PGconn *conn);
@ -359,21 +415,14 @@ extern int pqWriteReady(PGconn *conn);
extern int pqsecure_initialize(PGconn *); extern int pqsecure_initialize(PGconn *);
extern void pqsecure_destroy(void); extern void pqsecure_destroy(void);
extern int pqsecure_open_client(PGconn *); extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
extern void pqsecure_close(PGconn *); extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len); extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
/* bits in a byte */ /* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */
#define BYTELEN 8 #define PGDONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
/* fall back options if they are not specified by arguments or defined
by environment variables */
#define DefaultHost "localhost"
#define DefaultTty ""
#define DefaultOption ""
#define DefaultAuthtype ""
#define DefaultPassword ""
/* /*
* this is so that we can check is a connection is non-blocking internally * this is so that we can check is a connection is non-blocking internally