317 lines
7.2 KiB
C
317 lines
7.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pqpacket.c--
|
|
* routines for reading and writing data packets sent/received by
|
|
* POSTGRES clients and servers
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.8 1997/09/08 21:43:52 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/* NOTES
|
|
* This is the module that understands the lowest-level part
|
|
* of the communication protocol. All of the trickiness in
|
|
* this module is for making sure that non-blocking I/O in
|
|
* the Postmaster works correctly. Check the notes in PacketRecv
|
|
* on non-blocking I/O.
|
|
*
|
|
* Data Structures:
|
|
* Port has two important functions. (1) It records the
|
|
* sock/addr used in communication. (2) It holds partially
|
|
* read in messages. This is especially important when
|
|
* we haven't seen enough to construct a complete packet
|
|
* header.
|
|
*
|
|
* PacketBuf -- None of the clients of this module should know
|
|
* what goes into a packet hdr (although they know how big
|
|
* it is). This routine is in charge of host to net order
|
|
* conversion for headers. Data conversion is someone elses
|
|
* responsibility.
|
|
*
|
|
* IMPORTANT: these routines are called by backends, clients, and
|
|
* the Postmaster.
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <postgres.h>
|
|
#include <miscadmin.h>
|
|
#include <storage/ipc.h>
|
|
#include <libpq/libpq.h>
|
|
|
|
/*
|
|
* PacketReceive -- receive a packet on a port.
|
|
*
|
|
* RETURNS: connection id of the packet sender, if one
|
|
* is available.
|
|
*
|
|
*/
|
|
int
|
|
PacketReceive(Port *port, /* receive port */
|
|
PacketBuf *buf, /* MAX_PACKET_SIZE-worth of buffer space */
|
|
bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */
|
|
{
|
|
PacketLen max_size = sizeof(PacketBuf);
|
|
PacketLen cc; /* character count -- bytes recvd */
|
|
PacketLen packetLen; /* remaining packet chars to read */
|
|
Addr tmp; /* curr recv buf pointer */
|
|
int addrLen = sizeof(struct sockaddr_in);
|
|
int hdrLen;
|
|
int flag;
|
|
int decr;
|
|
|
|
hdrLen = sizeof(buf->len);
|
|
|
|
if (nonBlocking == NON_BLOCKING)
|
|
{
|
|
flag = MSG_PEEK;
|
|
decr = 0;
|
|
}
|
|
else
|
|
{
|
|
flag = 0;
|
|
decr = hdrLen;
|
|
}
|
|
|
|
/*
|
|
* Assume port->nBytes is zero unless we were interrupted during
|
|
* non-blocking I/O. This first recvfrom() is to get the hdr
|
|
* information so we know how many bytes to read. Life would be very
|
|
* complicated if we read too much data (buffering).
|
|
*/
|
|
tmp = ((Addr) buf) + port->nBytes;
|
|
|
|
if (port->nBytes >= hdrLen)
|
|
{
|
|
packetLen = ntohl(buf->len) - port->nBytes;
|
|
}
|
|
else
|
|
{
|
|
/* peeking into the incoming message */
|
|
cc = recvfrom(port->sock, (char *) &(buf->len), hdrLen, flag,
|
|
(struct sockaddr *) & (port->raddr), &addrLen);
|
|
if (cc < hdrLen)
|
|
{
|
|
/* if cc is negative, the system call failed */
|
|
if (cc < 0)
|
|
{
|
|
return (STATUS_ERROR);
|
|
}
|
|
|
|
/*
|
|
* cc == 0 means the connection was broken at the other end.
|
|
*/
|
|
else if (!cc)
|
|
{
|
|
return (STATUS_INVALID);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Worst case. We didn't even read in enough data to get
|
|
* the header length. since we are using a data stream,
|
|
* this happens only if the client is mallicious.
|
|
*
|
|
* Don't save the number of bytes we've read so far. Since we
|
|
* only peeked at the incoming message, the kernel is
|
|
* going to keep it for us.
|
|
*/
|
|
return (STATUS_NOT_DONE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* This is an attempt to shield the Postmaster from mallicious
|
|
* attacks by placing tighter restrictions on the reported
|
|
* packet length.
|
|
*
|
|
* Check for negative packet length
|
|
*/
|
|
if ((buf->len) <= 0)
|
|
{
|
|
return (STATUS_INVALID);
|
|
}
|
|
|
|
/*
|
|
* Check for oversize packet
|
|
*/
|
|
if ((ntohl(buf->len)) > max_size)
|
|
{
|
|
return (STATUS_INVALID);
|
|
}
|
|
|
|
/*
|
|
* great. got the header. now get the true length (including
|
|
* header size).
|
|
*/
|
|
packetLen = ntohl(buf->len);
|
|
|
|
/*
|
|
* if someone is sending us junk, close the connection
|
|
*/
|
|
if (packetLen > max_size)
|
|
{
|
|
port->nBytes = packetLen;
|
|
return (STATUS_BAD_PACKET);
|
|
}
|
|
packetLen -= decr;
|
|
tmp += decr - port->nBytes;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that we know how big it is, read the packet. We read the
|
|
* entire packet, since the last call was just a peek.
|
|
*/
|
|
while (packetLen)
|
|
{
|
|
cc = recvfrom(port->sock, tmp, packetLen, 0,
|
|
(struct sockaddr *) & (port->raddr), &addrLen);
|
|
if (cc < 0)
|
|
return (STATUS_ERROR);
|
|
|
|
/*
|
|
* cc == 0 means the connection was broken at the other end.
|
|
*/
|
|
else if (!cc)
|
|
return (STATUS_INVALID);
|
|
|
|
/*
|
|
fprintf(stderr,"expected packet of %d bytes, got %d bytes\n",
|
|
packetLen, cc);
|
|
*/
|
|
tmp += cc;
|
|
packetLen -= cc;
|
|
|
|
/* if non-blocking, we're done. */
|
|
if (nonBlocking && packetLen)
|
|
{
|
|
port->nBytes += cc;
|
|
return (STATUS_NOT_DONE);
|
|
}
|
|
}
|
|
|
|
port->nBytes = 0;
|
|
return (STATUS_OK);
|
|
}
|
|
|
|
/*
|
|
* PacketSend -- send a single-packet message.
|
|
*
|
|
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
|
|
* SIDE_EFFECTS: may block.
|
|
* NOTES: Non-blocking writes would significantly complicate
|
|
* buffer management. For now, we're not going to do it.
|
|
*
|
|
*/
|
|
int
|
|
PacketSend(Port *port,
|
|
PacketBuf *buf,
|
|
PacketLen len,
|
|
bool nonBlocking)
|
|
{
|
|
PacketLen totalLen;
|
|
int addrLen = sizeof(struct sockaddr_in);
|
|
|
|
Assert(!nonBlocking);
|
|
Assert(buf);
|
|
|
|
totalLen = len;
|
|
|
|
len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
|
|
(struct sockaddr *) & (port->raddr), addrLen);
|
|
|
|
if (len < totalLen)
|
|
{
|
|
sprintf(PQerrormsg,
|
|
"FATAL: PacketSend: couldn't send complete packet: errno=%d\n",
|
|
errno);
|
|
fputs(PQerrormsg, stderr);
|
|
return (STATUS_ERROR);
|
|
}
|
|
|
|
return (STATUS_OK);
|
|
}
|
|
|
|
/*
|
|
* StartupInfo2PacketBuf -
|
|
* convert the fields of the StartupInfo to a PacketBuf
|
|
*
|
|
*/
|
|
/* moved to src/libpq/fe-connect.c */
|
|
/*
|
|
PacketBuf*
|
|
StartupInfo2PacketBuf(StartupInfo* s)
|
|
{
|
|
PacketBuf* res;
|
|
char* tmp;
|
|
|
|
res = (PacketBuf*)malloc(sizeof(PacketBuf));
|
|
res->len = htonl(sizeof(PacketBuf));
|
|
res->data[0] = '\0';
|
|
|
|
tmp= res->data;
|
|
|
|
strncpy(tmp, s->database, sizeof(s->database));
|
|
tmp += sizeof(s->database);
|
|
strncpy(tmp, s->user, sizeof(s->user));
|
|
tmp += sizeof(s->user);
|
|
strncpy(tmp, s->options, sizeof(s->options));
|
|
tmp += sizeof(s->options);
|
|
strncpy(tmp, s->execFile, sizeof(s->execFile));
|
|
tmp += sizeof(s->execFile);
|
|
strncpy(tmp, s->tty, sizeof(s->execFile));
|
|
|
|
return res;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* PacketBuf2StartupInfo -
|
|
* convert the fields of the StartupInfo to a PacketBuf
|
|
*
|
|
*/
|
|
/* moved to postmaster.c
|
|
StartupInfo*
|
|
PacketBuf2StartupInfo(PacketBuf* p)
|
|
{
|
|
StartupInfo* res;
|
|
char* tmp;
|
|
|
|
res = (StartupInfo*)malloc(sizeof(StartupInfo));
|
|
|
|
res->database[0]='\0';
|
|
res->user[0]='\0';
|
|
res->options[0]='\0';
|
|
res->execFile[0]='\0';
|
|
res->tty[0]='\0';
|
|
|
|
tmp= p->data;
|
|
strncpy(res->database,tmp,sizeof(res->database));
|
|
tmp += sizeof(res->database);
|
|
strncpy(res->user,tmp, sizeof(res->user));
|
|
tmp += sizeof(res->user);
|
|
strncpy(res->options,tmp, sizeof(res->options));
|
|
tmp += sizeof(res->options);
|
|
strncpy(res->execFile,tmp, sizeof(res->execFile));
|
|
tmp += sizeof(res->execFile);
|
|
strncpy(res->tty,tmp, sizeof(res->tty));
|
|
|
|
return res;
|
|
}
|
|
*/
|