mirror of https://github.com/postgres/postgres
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:
parent
152ce7a490
commit
6bdb7aa4db
|
@ -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,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
|
||||
freeaddrinfo2(int hint_ai_family, struct addrinfo *ai)
|
||||
freeaddrinfo2(struct addrinfo *ai)
|
||||
{
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
if (hint_ai_family == AF_UNIX)
|
||||
if (ai != NULL)
|
||||
{
|
||||
struct addrinfo *p;
|
||||
|
||||
while (ai != NULL)
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
if (ai->ai_family == AF_UNIX)
|
||||
{
|
||||
p = ai;
|
||||
ai = ai->ai_next;
|
||||
free(p->ai_addr);
|
||||
free(p);
|
||||
while (ai != NULL)
|
||||
{
|
||||
struct addrinfo *p = ai;
|
||||
|
||||
ai = ai->ai_next;
|
||||
free(p->ai_addr);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
#endif /* HAVE_UNIX_SOCKETS */
|
||||
freeaddrinfo(ai);
|
||||
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;
|
||||
}
|
||||
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)
|
||||
free(crypt_pwd);
|
||||
return ret;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
||||
msgLen = htonl(msgLen);
|
||||
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
|
||||
/* 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)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
{
|
||||
printfPQExpBuffer(&conn->errorMessage,
|
||||
libpq_gettext("could not establish SSL connection: %s\n"),
|
||||
SSLerrmessage());
|
||||
close_SSL(conn);
|
||||
return -1;
|
||||
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("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 */
|
||||
|
@ -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.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* 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 stuff common to fe and be */
|
||||
#include "getaddrinfo.h"
|
||||
#include "libpq/pqcomm.h"
|
||||
#include "lib/dllist.h"
|
||||
/* 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>
|
||||
#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.
|
||||
*/
|
||||
|
||||
#define PQERRORMSG_LENGTH 1024
|
||||
#define CMDSTATUS_LEN 40
|
||||
|
||||
|
@ -96,20 +83,22 @@ typedef struct pgresAttDesc
|
|||
int atttypmod; /* type-specific modifier info */
|
||||
} PGresAttDesc;
|
||||
|
||||
/* 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
|
||||
null (zero) byte after whatever the backend sends us. This is only
|
||||
particularly useful for text tuples ... with a binary value, the
|
||||
value might have embedded nulls, so the application can't use C string
|
||||
operators on it. But we add a null anyway for consistency.
|
||||
Note that the value itself does not contain a length word.
|
||||
|
||||
A NULL attribute is a special case in two ways: its len field is NULL_LEN
|
||||
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).
|
||||
/*
|
||||
* 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
|
||||
* null (zero) byte after whatever the backend sends us. This is only
|
||||
* particularly useful for text tuples ... with a binary value, the
|
||||
* value might have embedded nulls, so the application can't use C string
|
||||
* operators on it. But we add a null anyway for consistency.
|
||||
* Note that the value itself does not contain a length word.
|
||||
*
|
||||
* A NULL attribute is a special case in two ways: its len field is NULL_LEN
|
||||
* 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 */
|
||||
|
@ -135,15 +124,6 @@ struct pg_result
|
|||
int binary; /* binary tuple values if binary == 1,
|
||||
* 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
|
||||
* 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 */
|
||||
} 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. */
|
||||
typedef struct pgLobjfuncs
|
||||
{
|
||||
|
@ -207,7 +216,8 @@ typedef struct pgLobjfuncs
|
|||
Oid fn_lo_write; /* OID of backend function LOwrite */
|
||||
} 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.
|
||||
*/
|
||||
struct pg_conn
|
||||
|
@ -254,12 +264,21 @@ struct pg_conn
|
|||
SockAddr laddr; /* Local address */
|
||||
SockAddr raddr; /* 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 */
|
||||
int be_pid; /* PID of backend --- needed for cancels */
|
||||
int be_key; /* key of backend --- needed for cancels */
|
||||
char md5Salt[4]; /* password salt received from backend */
|
||||
char cryptSalt[2]; /* password salt received from backend */
|
||||
pgParameterStatus *pstatus; /* ParameterStatus data */
|
||||
int client_encoding; /* encoding id */
|
||||
PGlobjfuncs *lobjfuncs; /* private state for large-object access
|
||||
* fns */
|
||||
|
@ -279,7 +298,8 @@ struct pg_conn
|
|||
int outCount; /* number of chars waiting in buffer */
|
||||
|
||||
/* 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) */
|
||||
|
||||
/* Status for asynchronous result construction */
|
||||
|
@ -324,10 +344,46 @@ extern int pqPacketSend(PGconn *conn, char pack_type,
|
|||
/* === in fe-exec.c === */
|
||||
|
||||
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 char *pqResultStrdup(PGresult *res, const char *str);
|
||||
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 === */
|
||||
|
||||
|
@ -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 pqGetInt(int *result, 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 pqReadData(PGconn *conn);
|
||||
extern int pqFlush(PGconn *conn);
|
||||
|
@ -359,21 +415,14 @@ extern int pqWriteReady(PGconn *conn);
|
|||
|
||||
extern int pqsecure_initialize(PGconn *);
|
||||
extern void pqsecure_destroy(void);
|
||||
extern int pqsecure_open_client(PGconn *);
|
||||
extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
|
||||
extern void pqsecure_close(PGconn *);
|
||||
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
|
||||
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
|
||||
|
||||
/* bits in a byte */
|
||||
#define BYTELEN 8
|
||||
|
||||
/* 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 ""
|
||||
/* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */
|
||||
#define PGDONOTICE(conn,message) \
|
||||
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
|
||||
|
||||
/*
|
||||
* this is so that we can check is a connection is non-blocking internally
|
||||
|
|
Loading…
Reference in New Issue