/*-------------------------------------------------------------------------
 *
 * 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: /usr/local/cvsroot/pgsql/src/backend/libpq/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;
}
*/
