/*
**	net.c	- 
**
**
** Copyright (c) 1993-95  David J. Hughes
** Copyright (c) 1995  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
** ID = "$Id:"
**
*/


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>

#ifndef EINTR
#  define EINTR 0
#endif

#if defined(_OS_WIN32)
#  include <winsock.h>
#  define NET_READ(fd,b,l) recv(fd,b,l,0)
#  define NET_WRITE(fd,b,l) send(fd,b,l,0)
#else
#  define NET_READ(fd,b,l) read(fd,b,l)
#  define NET_WRITE(fd,b,l) write(fd,b,l)
#endif

#include <common/portability.h>
#include "msql_priv.h"


static 	u_char	packetBuf[PKT_LEN + 4];
static	int	readTimeout;
u_char	*packet = NULL;
int	__MSQL_SERVER = 0;


void intToBuf(cp,val)
        u_char  *cp;
        int     val;
{
        *cp++ = (unsigned int)(val & 0x000000ff);
        *cp++ = (unsigned int)(val & 0x0000ff00) >> 8;
        *cp++ = (unsigned int)(val & 0x00ff0000) >> 16;
        *cp++ = (unsigned int)(val & 0xff000000) >> 24;
}


int bufToInt(cp)
        u_char  *cp;
{
        int val;
 
        val = 0;
        val = *cp++;
        val += ((int) *cp++) << 8 ;
        val += ((int) *cp++) << 16;
        val += ((int) *cp++) << 24;
        return(val);
}


#if defined(_OS_WIN32)
void initWinsock()
{	
	WORD	wVersion;
	WSADATA	wsaData;

	wVersion = MAKEWORD(1,1);
	WSAStartup(wVersion, &wsaData);
}
#endif


void initNet()
{
	packet = (u_char *)packetBuf + 4;
#if defined(_OS_WIN32)
	initWinsock();
#endif
}



int writePkt(fd)
	int	fd;
{
	int	len,
		offset,
		remain,
		numBytes;

	len = strlen((char *)packet);
	intToBuf(packetBuf,len);
	offset = 0;
	remain = len+4;
	while(remain > 0)
	{
		numBytes = NET_WRITE(fd,packetBuf + offset, remain);
		if (numBytes == -1)
		{
			return(-1);
		}
		offset += numBytes;
		remain -= numBytes;
	}
	return(0);
}



int readPkt(fd)
	int	fd;
{
	u_char	 buf[4];
	int	len,
		remain,
		offset,
		numBytes;
	fd_set	fdset;
	struct	timeval timeout;

	remain = 4;
	offset = 0;
	readTimeout = 0;
	while(remain > 0)
	{
		/* 
		** We can't just set an alarm here as on lots of boxes
		** both read and recv are non-interuptable.  So, we 
		** wait till there something to read before we start
		** reading in the server (not the client)
		*/
		if (__MSQL_SERVER == 1)
		{
			FD_ZERO(&fdset);
			FD_SET(fd, &fdset);
			timeout.tv_sec = 10;
			timeout.tv_usec = 0;
			if (select(fd+1, &fdset,NULL,NULL,&timeout) == 0)
				return(-1);
		}
		if (!readTimeout)
		{
			numBytes = NET_READ(fd,buf + offset,remain);
			if (numBytes < 0 && errno != EINTR)
			{
				fprintf(stderr,"Socket read on %d for length failed : ",fd);
				perror("");
			}
			if (numBytes <= 0)
         			return(-1);
		}
		if (readTimeout)
			break;
		remain -= numBytes;
		offset += numBytes;
		
	}
	len = bufToInt(buf);
	if (len > PKT_LEN)
	{
		fprintf(stderr,"Packet too large (%d)\n", len);
		return(-1);
	}
	if (len < 0)
	{
		fprintf(stderr,"Malformed packet\n");
		return(-1);
	}
	remain = len;
	offset = 0;
	while(remain > 0)
	{
		if (__MSQL_SERVER == 1)
		{
			FD_ZERO(&fdset);
			FD_SET(fd, &fdset);
			timeout.tv_sec = 10;
			timeout.tv_usec = 0;
			if (select(fd+1, &fdset,NULL,NULL,&timeout) == 0)
				return(-1);
		}
		numBytes = NET_READ(fd,packet+offset,remain);
		if (numBytes <= 0)
		{
         		return(-1);
		}
		remain -= numBytes;
		offset += numBytes;
	}
	*(packet + len) = 0;
        return(len);
}



/***********************************************************************
 * 
 * This section of code contains machine-specific code for handling integers.
 * Msql supports 32 bit 2's complement integers.  To hide the details of
 * converting integers for specific machines, the routines packInt32() and
 * unpackInt32() were written.  If you have a machine that has native ints
 * other than 32 bit 2's complement, you must either write your own versions
 * of packInt32() and unpackInt32(), or modify the supplied ones.  For any
 * machine using 2's complement ints, simple changes to the
 * BYTES_PER_INT, HIGH_BITS, HIGH_BITS_MASK, and SIGN_BIT_MASK macros should
 * make your code work.  If you have something else, you're on your own ...
 *
 ************************************************************************/

#if _CRAY
#define BYTES_PER_INT	8
#endif

#ifndef BYTES_PER_INT
#define BYTES_PER_INT	4		/* default.  most boxes fit here */
#endif

#if BYTES_PER_INT == 8
#define BIG_INTS	1
#define HIGH_BITS	32			/* bits-per-int minus 32 */
#define HIGH_BITS_MASK	0xffffffff00000000	/* mask of the high bits */
#define SIGN_BIT_MASK	0x0000000080000000      /* mask of your sign bit */
#endif

#if BYTES_PER_INT == 4
#define BIG_INTS      	0
#endif

/*
 * Pack a native integer into a character buffer.  The buffer is assumed
 * to be at least 4 bytes long.
 */

int
packInt32(num, buf)
int	num;
char	*buf;
{
#if BIG_INTS
	num <<= HIGH_BITS;
#endif

	bcopy4((char *)&num, buf);
	return 0;
}

/*
 * Extract a native integer from a character buffer.  The buffer is assumed
 * to have been formatted using the packInt32() routine.
 */

int
unpackInt32(buf)
char	*buf;
{
	int	num;

	bcopy4(buf, (char *)&num);

#if BIG_INTS
	num >>= HIGH_BITS;

	if (num & SIGN_BIT_MASK) {
		num |= HIGH_BITS_MASK;
	}
#endif

	return num;
}
