diff options
Diffstat (limited to 'src/backend/libpq/pqpacket.c')
| -rw-r--r-- | src/backend/libpq/pqpacket.c | 356 |
1 files changed, 116 insertions, 240 deletions
diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c index b62eb3ee36..0a00a97ec6 100644 --- a/src/backend/libpq/pqpacket.c +++ b/src/backend/libpq/pqpacket.c @@ -8,36 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.12 1997/12/09 03:10:51 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.13 1998/01/26 01:41:12 scrappy 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 <unistd.h> +#include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> @@ -50,260 +28,158 @@ #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. - * + * Set up a packet read for the postmaster event loop. */ -int -PacketReceive(Port *port, /* receive port */ - PacketBuf *buf, /* MAX_PACKET_SIZE-worth of buffer space */ - bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */ + +void PacketReceiveSetup(Packet *pkt, void (*iodone)(), char *arg) { - 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 hdrLen; - int flag; - int decr; + pkt->nrtodo = sizeof (pkt->len); + pkt->ptr = (char *)&pkt->len; + pkt->iodone = iodone; + pkt->arg = arg; + pkt->state = ReadingPacketLength; +} - hdrLen = sizeof(buf->len); - if (nonBlocking == NON_BLOCKING) - { - flag = MSG_PEEK; - decr = 0; - } - else - { - flag = 0; - decr = hdrLen; - } +/* + * Read a packet fragment. Return STATUS_OK if the connection should stay + * open. + */ - /* - * Assume port->nBytes is zero unless we were interrupted during - * non-blocking I/O. This first recv() 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; +int PacketReceiveFragment(Packet *pkt, int sock) +{ + int got; - if (port->nBytes >= hdrLen) + if ((got = read(sock,pkt->ptr,pkt->nrtodo)) > 0) { - packetLen = ntohl(buf->len) - port->nBytes; - } - else - { - /* peeking into the incoming message */ - cc = recv(port->sock, (char *) &(buf->len), hdrLen, flag); - if (cc < hdrLen) + pkt->nrtodo -= got; + pkt->ptr += got; + + /* See if we have got what we need for the packet length. */ + + if (pkt->nrtodo == 0 && pkt->state == ReadingPacketLength) { - /* if cc is negative, the system call failed */ - if (cc < 0) - { - return (STATUS_ERROR); - } + pkt->len = ntohl(pkt->len); - /* - * cc == 0 means the connection was broken at the other end. - */ - else if (!cc) + if (pkt->len < sizeof (pkt->len) || + pkt->len > sizeof (pkt->len) + sizeof (pkt->pkt)) { - return (STATUS_INVALID); + PacketSendError(pkt,"Invalid packet length"); + return STATUS_OK; } - 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); - } + /* Set up for the rest of the packet. */ + + pkt->nrtodo = pkt->len - sizeof (pkt->len); + pkt->ptr = (char *)&pkt->pkt; + pkt->state = ReadingPacket; } - 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); - } + /* See if we have got what we need for the packet. */ - /* - * Check for oversize packet - */ - if ((ntohl(buf->len)) > max_size) - { - return (STATUS_INVALID); - } + if (pkt->nrtodo == 0 && pkt->state == ReadingPacket) + { + pkt->state = Idle; - /* - * great. got the header. now get the true length (including - * header size). - */ - packetLen = ntohl(buf->len); + /* Special case to close the connection. */ - /* - * 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; + if (pkt->iodone == NULL) + return STATUS_ERROR; + + (*pkt->iodone)(pkt->arg, pkt->len - sizeof (pkt->len), + (char *)&pkt->pkt); } + + return STATUS_OK; } - /* - * 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 = read(port->sock, tmp, packetLen); - if (cc < 0) - return (STATUS_ERROR); + if (got == 0) + return STATUS_ERROR; - /* - * cc == 0 means the connection was broken at the other end. - */ - else if (!cc) - return (STATUS_INVALID); + if (errno == EINTR) + return STATUS_OK; -/* - 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); - } - } + fprintf(stderr, "read() system call failed\n"); - port->nBytes = 0; - return (STATUS_OK); + return STATUS_ERROR; } + /* - * 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. - * + * Set up a packet write for the postmaster event loop. */ -int -PacketSend(Port *port, - PacketBuf *buf, - PacketLen len, - bool nonBlocking) + +void PacketSendSetup(Packet *pkt, int nbytes, void (*iodone)(), char *arg) { - PacketLen doneLen; + pkt->nrtodo = nbytes; + pkt->ptr = (char *)&pkt->pkt; + pkt->iodone = iodone; + pkt->arg = arg; + pkt->state = WritingPacket; +} + + +/* + * Write a packet fragment. Return STATUS_OK if the connection should stay + * open. + */ - Assert(!nonBlocking); - Assert(buf); +int PacketSendFragment(Packet *pkt, int sock) +{ + int done; - doneLen = write(port->sock, buf, len); - if (doneLen < len) + if ((done = write(sock,pkt->ptr,pkt->nrtodo)) > 0) { - sprintf(PQerrormsg, - "FATAL: PacketSend: couldn't send complete packet: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - return (STATUS_ERROR); + pkt->nrtodo -= done; + pkt->ptr += done; + + /* See if we have written the whole packet. */ + + if (pkt->nrtodo == 0) + { + pkt->state = Idle; + + /* Special case to close the connection. */ + + if (pkt->iodone == NULL) + return STATUS_ERROR; + + (*pkt->iodone)(pkt->arg); + } + + return STATUS_OK; } - return (STATUS_OK); -} + if (done == 0) + return STATUS_ERROR; -/* - * 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*)palloc(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; + if (errno == EINTR) + return STATUS_OK; + + fprintf(stderr, "write() system call failed\n"); + + return STATUS_ERROR; } -*/ + /* - * PacketBuf2StartupInfo - - * convert the fields of the StartupInfo to a PacketBuf - * + * Send an error message from the postmaster to the frontend. */ -/* moved to postmaster.c -StartupInfo* -PacketBuf2StartupInfo(PacketBuf* p) + +void PacketSendError(Packet *pkt, char *errormsg) { - StartupInfo* res; - char* tmp; - - res = (StartupInfo*)palloc(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; + fprintf(stderr, "%s\n", errormsg); + + pkt->pkt.em.data[0] = 'E'; + StrNCpy(&pkt->pkt.em.data[1], errormsg, sizeof (pkt->pkt.em.data) - 2); + + /* + * The NULL i/o callback will cause the connection to be broken when + * the error message has been sent. + */ + + PacketSendSetup(pkt, strlen(pkt->pkt.em.data) + 1, NULL, NULL); } -*/ |
