Skip to content
Snippets Groups Projects
  • Marc G. Fournier's avatar
    809ae06a
    Patch for: · 809ae06a
    Marc G. Fournier authored
    The following patch to src/backend/libpq/pqpacket.c provides additional
    checking for bad packet length data. It was tested with the Linux telnet
    client, with netcat using the numbers.txt and by dumping random numbers
    into the port.
    
    Patch by: Alvaro Martinez Echevarria <alvaro@lander.es>
    809ae06a
    History
    Patch for:
    Marc G. Fournier authored
    The following patch to src/backend/libpq/pqpacket.c provides additional
    checking for bad packet length data. It was tested with the Linux telnet
    client, with netcat using the numbers.txt and by dumping random numbers
    into the port.
    
    Patch by: Alvaro Martinez Echevarria <alvaro@lander.es>
pqpacket.c 7.53 KiB
/*-------------------------------------------------------------------------
 *
 * 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.3 1997/02/13 08:06:36 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 <sys/types.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#else
#include <winsock.h>
#endif /*WIN32 */
#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) {
	(void) 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;
}
*/