1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Second round of FE/BE protocol changes. Frontend->backend messages now

have length counts, and COPY IN data is packetized into messages.
This commit is contained in:
Tom Lane
2003-04-19 00:02:30 +00:00
parent 54b38d293e
commit bd8d441775
24 changed files with 1337 additions and 982 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,6 +37,7 @@
static void sendAuthRequest(Port *port, AuthRequest areq);
static void auth_failed(Port *port, int status);
static char *recv_password_packet(Port *port);
static int recv_and_check_password_packet(Port *port);
char *pg_krb_server_keyfile;
@@ -539,11 +540,9 @@ sendAuthRequest(Port *port, AuthRequest areq)
*/
static int
pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr)
pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
struct pam_response ** resp, void *appdata_ptr)
{
StringInfoData buf;
int32 len;
if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
{
switch (msg[0]->msg_style)
@@ -574,23 +573,20 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
*/
if (strlen(appdata_ptr) == 0)
{
char *passwd;
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
passwd = recv_password_packet(pam_port_cludge);
if (passwd == NULL)
return PAM_CONV_ERR; /* client didn't want to send password */
initStringInfo(&buf);
if (pq_getstr_bounded(&buf, 1000) == EOF)
return PAM_CONV_ERR; /* EOF while reading password */
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received PAM packet");
if (strlen(buf.data) == 0)
if (strlen(passwd) == 0)
{
elog(LOG, "pam_passwd_conv_proc: no password");
return PAM_CONV_ERR;
}
appdata_ptr = buf.data;
appdata_ptr = passwd;
}
/*
@@ -601,8 +597,6 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
if (!*resp)
{
elog(LOG, "pam_passwd_conv_proc: Out of memory!");
if (buf.data)
pfree(buf.data);
return PAM_CONV_ERR;
}
@@ -708,42 +702,87 @@ CheckPAMAuth(Port *port, char *user, char *password)
/*
* Called when we have received the password packet.
* Collect password response packet from frontend.
*
* Returns NULL if couldn't get password, else palloc'd string.
*/
static int
recv_and_check_password_packet(Port *port)
static char *
recv_password_packet(Port *port)
{
StringInfoData buf;
int32 len;
int result;
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
return STATUS_EOF; /* client didn't want to send password */
if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
{
/* Expect 'p' message type */
int mtype;
mtype = pq_getbyte();
if (mtype != 'p')
{
/*
* If the client just disconnects without offering a password,
* don't make a log entry. This is legal per protocol spec and
* in fact commonly done by psql, so complaining just clutters
* the log.
*/
if (mtype != EOF)
elog(COMMERROR, "Expected password response, got %c", mtype);
return NULL; /* EOF or bad message type */
}
}
else
{
/* For pre-3.0 clients, avoid log entry if they just disconnect */
if (pq_peekbyte() == EOF)
return NULL; /* EOF */
}
initStringInfo(&buf);
if (pq_getstr_bounded(&buf, 1000) == EOF) /* receive password */
if (pq_getmessage(&buf, 1000)) /* receive password */
{
/* EOF - pq_getmessage already logged a suitable message */
pfree(buf.data);
return STATUS_EOF;
return NULL;
}
/*
* We don't actually use the password packet length the frontend sent
* us; however, it's a reasonable sanity check to ensure that we
* actually read as much data as we expected to.
*
* The password packet size is the length of the buffer, plus the size
* field itself (4 bytes), plus a 1-byte terminator.
* Apply sanity check: password packet length should agree with length
* of contained string. Note it is safe to use strlen here because
* StringInfo is guaranteed to have an appended '\0'.
*/
if (len != (buf.len + 4 + 1))
elog(LOG, "unexpected password packet size: read %d, expected %d",
buf.len + 4 + 1, len);
if (strlen(buf.data) + 1 != buf.len)
elog(COMMERROR, "bogus password packet size");
/* Do not echo password to logs, for security. */
elog(DEBUG5, "received password packet");
result = md5_crypt_verify(port, port->user_name, buf.data);
/*
* Return the received string. Note we do not attempt to do any
* character-set conversion on it; since we don't yet know the
* client's encoding, there wouldn't be much point.
*/
return buf.data;
}
/*
* Called when we have sent an authorization request for a password.
* Get the response and check it.
*/
static int
recv_and_check_password_packet(Port *port)
{
char *passwd;
int result;
passwd = recv_password_packet(port);
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
result = md5_crypt_verify(port, port->user_name, passwd);
pfree(passwd);
pfree(buf.data);
return result;
}

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.29 2003/04/10 23:03:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.30 2003/04/19 00:02:29 tgl Exp $
*
* Since the server static private key ($DataDir/server.key)
* will normally be stored unencrypted so that the database
@@ -110,13 +110,6 @@
extern void ExitPostmaster(int);
extern void postmaster_error(const char *fmt,...);
int secure_initialize(void);
void secure_destroy(void);
int secure_open_server(Port *);
void secure_close(Port *);
ssize_t secure_read(Port *, void *ptr, size_t len);
ssize_t secure_write(Port *, void *ptr, size_t len);
#ifdef USE_SSL
static DH *load_dh_file(int keylength);
static DH *load_dh_buffer(const char *, size_t);

View File

@@ -6,8 +6,8 @@
* These routines handle the low-level details of communication between
* frontend and backend. They just shove data across the communication
* channel, and are ignorant of the semantics of the data --- or would be,
* except for major brain damage in the design of the COPY OUT protocol.
* Unfortunately, COPY OUT is designed to commandeer the communication
* except for major brain damage in the design of the old COPY OUT protocol.
* Unfortunately, COPY OUT was designed to commandeer the communication
* channel (it just transfers data without wrapping it into messages).
* No other messages can be sent while COPY OUT is in progress; and if the
* copy is aborted by an elog(ERROR), we need to close out the copy so that
@@ -29,7 +29,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqcomm.c,v 1.149 2003/04/02 00:49:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,12 +48,13 @@
* low-level I/O:
* pq_getbytes - get a known number of bytes from connection
* pq_getstring - get a null terminated string from connection
* pq_getmessage - get a message with length word from connection
* pq_getbyte - get next byte from connection
* pq_peekbyte - peek at next byte from connection
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
* pq_flush - flush pending output
*
* message-level I/O (and COPY OUT cruft):
* message-level I/O (and old-style-COPY-OUT cruft):
* pq_putmessage - send a normal message (suppressed in COPY OUT mode)
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
* pq_endcopyout - end a COPY OUT transfer
@@ -85,9 +86,6 @@
#include "miscadmin.h"
#include "storage/ipc.h"
extern void secure_close(Port *);
extern ssize_t secure_read(Port *, void *, size_t);
extern ssize_t secure_write(Port *, const void *, size_t);
static void pq_close(void);
@@ -562,8 +560,10 @@ pq_recvbuf(void)
}
if (r == 0)
{
/* as above, only write to postmaster log */
elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
/*
* EOF detected. We used to write a log message here, but it's
* better to expect the ultimate caller to do that.
*/
return EOF;
}
/* r contains number of bytes read, so just incr length */
@@ -636,35 +636,29 @@ pq_getbytes(char *s, size_t len)
/* --------------------------------
* pq_getstring - get a null terminated string from connection
*
* The return value is placed in an expansible StringInfo.
* Note that space allocation comes from the current memory context!
* The return value is placed in an expansible StringInfo, which has
* already been initialized by the caller.
*
* If maxlen is not zero, it is an upper limit on the length of the
* string we are willing to accept. We abort the connection (by
* returning EOF) if client tries to send more than that. Note that
* since we test maxlen in the outer per-bufferload loop, the limit
* is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
* specified. This is fine for the intended purpose, which is just
* to prevent DoS attacks from not-yet-authenticated clients.
*
* NOTE: this routine does not do any character set conversion,
* even though it is presumably useful only for text, because
* no code in this module should depend on the encoding.
* See pq_getstr_bounded in pqformat.c for that.
* This is used only for dealing with old-protocol clients. The idea
* is to produce a StringInfo that looks the same as we would get from
* pq_getmessage() with a newer client; we will then process it with
* pq_getmsgstring. Therefore, no character set conversion is done here,
* even though this is presumably useful only for text.
*
* returns 0 if OK, EOF if trouble
* --------------------------------
*/
int
pq_getstring(StringInfo s, int maxlen)
pq_getstring(StringInfo s)
{
int i;
/* Reset string to empty */
s->len = 0;
s->data[0] = '\0';
s->cursor = 0;
/* Read until we get the terminating '\0' or overrun maxlen */
/* Read until we get the terminating '\0' */
for (;;)
{
while (PqRecvPointer >= PqRecvLength)
@@ -677,9 +671,9 @@ pq_getstring(StringInfo s, int maxlen)
{
if (PqRecvBuffer[i] == '\0')
{
/* does not copy the \0 */
/* include the '\0' in the copy */
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
i - PqRecvPointer);
i - PqRecvPointer + 1);
PqRecvPointer = i + 1; /* advance past \0 */
return 0;
}
@@ -689,14 +683,73 @@ pq_getstring(StringInfo s, int maxlen)
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
PqRecvLength - PqRecvPointer);
PqRecvPointer = PqRecvLength;
/* If maxlen is specified, check for overlength input. */
if (maxlen > 0 && s->len > maxlen)
return EOF;
}
}
/* --------------------------------
* pq_getmessage - get a message with length word from connection
*
* The return value is placed in an expansible StringInfo, which has
* already been initialized by the caller.
* Only the message body is placed in the StringInfo; the length word
* is removed. Also, s->cursor is initialized to zero for convenience
* in scanning the message contents.
*
* If maxlen is not zero, it is an upper limit on the length of the
* message we are willing to accept. We abort the connection (by
* returning EOF) if client tries to send more than that.
*
* returns 0 if OK, EOF if trouble
* --------------------------------
*/
int
pq_getmessage(StringInfo s, int maxlen)
{
int32 len;
/* Reset message buffer to empty */
s->len = 0;
s->data[0] = '\0';
s->cursor = 0;
/* Read message length word */
if (pq_getbytes((char *) &len, 4) == EOF)
{
elog(COMMERROR, "unexpected EOF within message length word");
return EOF;
}
len = ntohl(len);
len -= 4; /* discount length itself */
if (len < 0 ||
(maxlen > 0 && len > maxlen))
{
elog(COMMERROR, "invalid message length");
return EOF;
}
if (len > 0)
{
/* Allocate space for message */
enlargeStringInfo(s, len);
/* And grab the message */
if (pq_getbytes(s->data, len) == EOF)
{
elog(COMMERROR, "incomplete client message");
return EOF;
}
s->len = len;
/* Place a trailing null per StringInfo convention */
s->data[len] = '\0';
}
return 0;
}
/* --------------------------------
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
*
@@ -781,34 +834,10 @@ pq_flush(void)
}
/*
* Return EOF if the connection has been broken, else 0.
*/
int
pq_eof(void)
{
char x;
int res;
res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
if (res < 0)
{
/* can log to postmaster log only */
elog(COMMERROR, "pq_eof: recv() failed: %m");
return EOF;
}
if (res == 0)
return EOF;
else
return 0;
}
/* --------------------------------
* Message-level I/O routines begin here.
*
* These routines understand about COPY OUT protocol.
* These routines understand about the old-style COPY OUT protocol.
* --------------------------------
*/
@@ -840,7 +869,8 @@ pq_putmessage(char msgtype, const char *s, size_t len)
}
/* --------------------------------
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
* pq_startcopyout - inform libpq that an old-style COPY OUT transfer
* is beginning
* --------------------------------
*/
void

View File

@@ -8,15 +8,17 @@
* formatting/conversion routines that are needed to produce valid messages.
* Note in particular the distinction between "raw data" and "text"; raw data
* is message protocol characters and binary values that are not subject to
* character set conversion, while text is converted by character encoding rules.
* character set conversion, while text is converted by character encoding
* rules.
*
* Incoming messages are read directly off the wire, as it were, but there
* are still data-conversion tasks to be performed.
* Incoming messages are similarly read into a StringInfo buffer, via
* pq_getmessage, and then parsed and converted from that using the routines
* in this module.
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqformat.c,v 1.26 2003/04/02 00:49:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,12 +39,13 @@
* Special-case message output:
* pq_puttextmessage - generate a character set-converted message in one step
*
* Message input:
* pq_getint - get an integer from connection
* pq_getstr_bounded - get a null terminated string from connection
* pq_getstr_bounded performs character set conversion on the collected
* string. Use the raw pqcomm.c routines pq_getstring or pq_getbytes
* to fetch data without conversion.
* Message parsing after input:
* pq_getmsgbyte - get a raw byte from a message buffer
* pq_getmsgint - get a binary integer from a message buffer
* pq_getmsgbytes - get raw data from a message buffer
* pq_copymsgbytes - copy raw data from a message buffer
* pq_getmsgstring - get a null-terminated text string (with conversion)
* pq_getmsgend - verify message fully consumed
*/
#include "postgres.h"
@@ -206,16 +209,29 @@ pq_puttextmessage(char msgtype, const char *str)
return pq_putmessage(msgtype, str, slen + 1);
}
/* --------------------------------
* pq_getint - get an integer from connection
*
* returns 0 if OK, EOF if trouble
* pq_getmsgbyte - get a raw byte from a message buffer
* --------------------------------
*/
int
pq_getint(int *result, int b)
pq_getmsgbyte(StringInfo msg)
{
int status;
if (msg->cursor >= msg->len)
elog(ERROR, "pq_getmsgbyte: no data left in message");
return (unsigned char) msg->data[msg->cursor++];
}
/* --------------------------------
* pq_getmsgint - get a binary integer from a message buffer
*
* Values are treated as unsigned.
* --------------------------------
*/
unsigned int
pq_getmsgint(StringInfo msg, int b)
{
unsigned int result;
unsigned char n8;
uint16 n16;
uint32 n32;
@@ -223,59 +239,93 @@ pq_getint(int *result, int b)
switch (b)
{
case 1:
status = pq_getbytes((char *) &n8, 1);
*result = (int) n8;
pq_copymsgbytes(msg, (char *) &n8, 1);
result = n8;
break;
case 2:
status = pq_getbytes((char *) &n16, 2);
*result = (int) (ntohs(n16));
pq_copymsgbytes(msg, (char *) &n16, 2);
result = ntohs(n16);
break;
case 4:
status = pq_getbytes((char *) &n32, 4);
*result = (int) (ntohl(n32));
pq_copymsgbytes(msg, (char *) &n32, 4);
result = ntohl(n32);
break;
default:
/*
* if we elog(ERROR) here, we will lose sync with the
* frontend, so just complain to postmaster log instead...
*/
elog(COMMERROR, "pq_getint: unsupported size %d", b);
status = EOF;
*result = 0;
elog(ERROR, "pq_getmsgint: unsupported size %d", b);
result = 0; /* keep compiler quiet */
break;
}
return status;
return result;
}
/* --------------------------------
* pq_getstr_bounded - get a null terminated string from connection
* pq_getmsgbytes - get raw data from a message buffer
*
* The return value is placed in an expansible StringInfo.
* Note that space allocation comes from the current memory context!
*
* The maxlen parameter is interpreted as per pq_getstring.
*
* returns 0 if OK, EOF if trouble
* Returns a pointer directly into the message buffer; note this
* may not have any particular alignment.
* --------------------------------
*/
int
pq_getstr_bounded(StringInfo s, int maxlen)
const char *
pq_getmsgbytes(StringInfo msg, int datalen)
{
int result;
char *p;
result = pq_getstring(s, maxlen);
p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
if (p != s->data) /* actual conversion has been done? */
{
/* reset s to empty, and append the new string p */
s->len = 0;
s->data[0] = '\0';
appendBinaryStringInfo(s, p, strlen(p));
pfree(p);
}
const char *result;
if (datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
result = &msg->data[msg->cursor];
msg->cursor += datalen;
return result;
}
/* --------------------------------
* pq_copymsgbytes - copy raw data from a message buffer
*
* Same as above, except data is copied to caller's buffer.
* --------------------------------
*/
void
pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
{
if (datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
memcpy(buf, &msg->data[msg->cursor], datalen);
msg->cursor += datalen;
}
/* --------------------------------
* pq_getmsgstring - get a null-terminated text string (with conversion)
*
* May return a pointer directly into the message buffer, or a pointer
* to a palloc'd conversion result.
* --------------------------------
*/
const char *
pq_getmsgstring(StringInfo msg)
{
char *str;
int slen;
str = &msg->data[msg->cursor];
/*
* It's safe to use strlen() here because a StringInfo is guaranteed
* to have a trailing null byte. But check we found a null inside
* the message.
*/
slen = strlen(str);
if (msg->cursor + slen >= msg->len)
elog(ERROR, "pq_getmsgstring: invalid string in message");
msg->cursor += slen + 1;
return (const char *) pg_client_to_server((unsigned char *) str, slen);
}
/* --------------------------------
* pq_getmsgend - verify message fully consumed
* --------------------------------
*/
void
pq_getmsgend(StringInfo msg)
{
if (msg->cursor != msg->len)
elog(ERROR, "pq_getmsgend: invalid message format");
}