/**********************************************************************
 * Copyright (c) 2005, 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: RASocket.c,v 1.7 2009/07/10 00:11:31 jwest Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

/** Platform specifics */
#ifdef _WIN32									/* Win32 */
#include <winsock2.h> /* 9707 */
#include <io.h>
#include <stdio.h>
#else											/* else */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifndef MVS
#include <netinet/tcp.h>
#endif
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#define SOCKET_ERROR	-1
#endif

#include "RASocket.h"

#ifdef _WIN32
#define READ(fd, buffer, len) recv(fd, buffer, len, 0)
#define WRITE(fd, buffer, len) send(fd, buffer, len, 0)
#include <io.h>
#else
#define READ read
#define WRITE write
#endif

#define DATA_PORT_NUM_CLIENT	10004
#define MESSAGE_BUFFER_INCREMENT  1024

/* The message processor signature */
typedef int (*ra_messageProcessor)(ra_message_t *message,
								   struct sockaddr *peer_address,
								   int peer_address_length);

/**
  * Function Prototypes.
  */
void *UDPServer(void *args);
void *TCPServer(void *args);
static BOOL validateRecvBuffer(unsigned char *buffer, int length);
static ra_uint_t errorRecoveryScan(unsigned char *buffer, int length);
#ifdef _WIN32
DWORD WINAPI win32BoundUDPServerProxy(LPVOID args);
DWORD WINAPI win32TCPServerProxy(LPVOID args);
#endif



/** PROCESS_ACKNOWLEDGEMENT_MESSAGE  ********************************
  * Called by processMessage for acknowledgement messages.
  */
static void processAcknowledgementMessage(ra_serverInfo_t *storage,
										  ra_message_t *message) {
	if(storage->acknowledgementHandler) {
		(*storage->acknowledgementHandler)(message->ticket);
	}
}

/** PROCESS_CONTROL_MESSAGE  ****************************************
  * Called by processMessage for JVMPI_CTL_MSG messages.
  * Breaks the message up into individual commands and forwards each
  * command to the registered _jvmpicomm_notify_message method.
  */
static void processControlMessage(ra_serverInfo_t *storage,
								  ra_message_t *message) {
	ra_command_list_node_t *current;
	/* Process each command in turn */
	current=message->commands.head;

	while(current != NULL ) {
		/* Intercept the scoping information */
		if(current->command->tag==RA_AGENT_SCOPING_INFORMATION) {
			ra_copyRASTRING(&storage->nodeUUID, &current->command->info.agent_scoping_information.nodeUUID);
			ra_copyRASTRING(&storage->processUUID, &current->command->info.agent_scoping_information.processUUID);
		}
		else {
			/* Call the user defined message handler */
			if(storage->commandHandler) {
				(*storage->commandHandler)(current->command);
			}
		}
		current=current->next;
	}
}


static void messageProcessor(ra_serverInfo_t *storage,
							ra_message_t *message,
							struct sockaddr *peer_address,
							int peer_address_length) {

	if(message->type == RA_ACKNOWLEDGEMENT_MESSAGE) {
		processAcknowledgementMessage(storage, message);
	}
	else if(message->type == RA_CONTROL_MESSAGE) {
		processControlMessage(storage, message);
	}
}

/** CREATE_BOUND_UDP_SERVER *****************************************
  * Creates a UDP socket server on a separate thread.  Once the server
  * is created the file descriptor and the thread ID of the server are
  * returned in a Server_info structure.
  * @param   portNum - the port you wish the server to be bound to.
  * @param resultSet - The ra_serverInfo_t structure with the
  *                    socket file descriptor, thread ID of the server,
  *                    and the function that will be called to process
  *                    incomming messages. The prototype of the fuction
  *                    must be of the type ra_messageProcessor.
  *					   THIS MEMORY MUST BE VALID FOR THE LIFE OF THE SERVER.
  * @returns  0 - create server was successful.
  *         <>0 - create failed, call ra_getlastErrorMajor/Minor
  *               to get the failure details.
  *  IPV4-ONLY
  */
int ra_createBoundUDPServer(short portNum,
							ra_serverInfo_t *result) {
	struct hostent *hp;
	struct sockaddr_in serverAddress;
	char localhost[MAX_HOST_NAME_LENGTH+1];
	struct linger linger;
	HANDLE handle;

	BZERO(result, sizeof(result));
	BZERO(&serverAddress, sizeof(serverAddress));

#ifdef _WIN32
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

	/* Get the local host informantion */
	gethostname(localhost,MAX_HOST_NAME_LENGTH);
	if((hp=gethostbyname((char*)localhost))==NULL) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(GETLOCALHOST_FAILED, error);
		return -1;
	}

	/* Load the host info and address into the address struct */
	/* Don't need to bind to a specific IP address
	memcpy(&(serverAddress.sin_addr), hp->h_addr_list[0], hp->h_length);
	*/
	serverAddress.sin_family=hp->h_addrtype;
	serverAddress.sin_port=htons(portNum);

	/* Set the socket up for connections using specified protocol */
	if((result->sockfd=socket(hp->h_addrtype, SOCK_DGRAM, 0))<0) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(SOCKET_CREATE_FAILED, error);
		return -1;
	}

	/* Bind the server socket */
#ifndef _WIN32
	errno=0;
#endif

#ifdef __OS400__
	if(bind(result->sockfd, (struct sockaddr*)&serverAddress, sizeof(struct sockaddr_in))<0) {
#else
	if(bind(result->sockfd, (const struct sockaddr*)&serverAddress, sizeof(struct sockaddr_in))<0) {
#endif
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(SOCKET_BIND_FAILED, error);
		ra_closeSocket(result->sockfd);
		return -1;
	}

	/* Wait for up to three seconds to finish reading/writing information to the socket */
	linger.l_onoff=1;
	linger.l_linger=3;
	setsockopt(result->sockfd,
			   SOL_SOCKET,
			   SO_LINGER,
#ifdef __OS400__
			   (char*)&linger,
#else
			   (const char*)&linger,
#endif
			   sizeof(linger));


	/* Run the server on a separate thread and return the tid */
#if defined(_WIN32)
	if(!(errno=(SetHandleInformation((HANDLE)result->sockfd, HANDLE_FLAG_INHERIT, 0)))) {
		ra_setLastError(HANDLE_NO_INHERIT_FAILED, errno);
		return -1;
	}
	handle = CreateThread(NULL,						/* default security attributes */
						 0,							/* same stack size as current thread */
						 win32BoundUDPServerProxy,	/* Thread entry point */
						 (LPVOID)result,			/* No params */
						 0,							/* start executing immediately */
						 &(result->tid));			/* the thread ID */
	CloseHandle(handle);
#elif defined(_AIX)
	errno=0;
	pthread_attr_t thread_attr;

	pthread_attr_init(&thread_attr);
	pthread_attr_setstacksize( &thread_attr, 4194304 );
	int presult = pthread_create(&(result->tid),
			  &thread_attr,
			  UDPServer,
			  result);
	if(presult!=0) {
		ra_setLastError(THREAD_CREATE_FAILED, errno);
		return -1;
	}
#else
	errno=0;
	int presult = pthread_create(&(result->tid),
			  NULL,
			  UDPServer,
			  result);
	if(presult!=0) {
		ra_setLastError(THREAD_CREATE_FAILED, errno);
		return -1;
	}
#endif

	return 0;
}

/** UDP_SERVER *****************************************************
  * The actual server.  Processes requests on a separate thread
  * and delegates thier processing to PROCESS_MESSAGE
  */
void* UDPServer(void *args) {
#ifdef MVS
	size_t  byte, client_len;
#elif __OS400__
	int client_len;
	int byte;
#else
	unsigned long  client_len;
	int byte;
#endif
	struct sockaddr_in ca;
	char request[MESSAGE_BUFFER_INCREMENT];
	char *correctBuffer;
	char acknowlegementBuffer[16];

	/* Initialize buffers/structures */
	BZERO(&ca, sizeof(ca));
	BZERO(request, MESSAGE_BUFFER_INCREMENT);


	client_len=sizeof(ca);

	/* Run forever */
	while(TRUE) {
		ra_message_t *message;
#ifndef _WIN32
		errno=0;
#endif
		/* Peek at the data first */
#ifdef _HPUX
		byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, request, sizeof(request), MSG_PEEK, (struct sockaddr*)&ca, (int *)&client_len);
#else
		byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, request, sizeof(request), MSG_PEEK, (struct sockaddr*)&ca, &client_len);
#endif
#ifdef _WIN32
		if(byte==SOCKET_ERROR) {
			long error=WSAGetLastError();
#else
		if(byte==sizeof(request) || byte<0) {
			int error=errno;
#endif
			/* Is this greater then MAX_MESSAGE_LENGTH? */
#ifdef _WIN32
			if(error==WSAEMSGSIZE) {
#else
			if(byte==sizeof(request)) {
#endif
				char *overflowBuffer=request;
				long overflowBufferSize=MESSAGE_BUFFER_INCREMENT;
#ifdef _WIN32
				while(error==WSAEMSGSIZE) {
#else
				while(byte==overflowBufferSize) {
#endif
					char *temp1, *temp=(char*)malloc(overflowBufferSize+MESSAGE_BUFFER_INCREMENT);
					temp1=overflowBuffer;
					overflowBuffer=temp;
					if(temp1 != request) {
						free(temp1);
					}
					overflowBufferSize+=MESSAGE_BUFFER_INCREMENT;
#ifdef _HPUX
					byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, overflowBuffer, overflowBufferSize, MSG_PEEK, (struct sockaddr*)&ca, (int *)&client_len);
#else
					byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, overflowBuffer, overflowBufferSize, MSG_PEEK, (struct sockaddr*)&ca, &client_len);
#endif
#ifdef _WIN32
					error=WSAGetLastError();
#else
					error=errno;
#endif
			}
#ifdef _HPUX
				byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, overflowBuffer, overflowBufferSize, 0, (struct sockaddr*)&ca, (int *)&client_len);
#else
				byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, overflowBuffer, overflowBufferSize, 0, (struct sockaddr*)&ca, &client_len);
#endif
				correctBuffer=overflowBuffer;
			}
			else {
				ra_setLastError(99, error);
				return (void*)-1;
			}
		}
		else {
#ifdef _HPUX
			byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, request, MESSAGE_BUFFER_INCREMENT, 0, (struct sockaddr*)&ca, (int *)&client_len);
#else
			byte=recvfrom(((ra_serverInfo_t*)args)->sockfd, request, MESSAGE_BUFFER_INCREMENT, 0, (struct sockaddr*)&ca, &client_len);
#endif
			correctBuffer=request;
		}

		/* read the buffer into a message structure */
#ifdef _HPUX
		message=ra_readMessageFromBuffer((unsigned char *)correctBuffer, (unsigned long)byte);
#else
		message=ra_readMessageFromBuffer(correctBuffer, (unsigned long)byte);
#endif
		/* Formulate the reply to the client */

		if(message->type!=RA_ACKNOWLEDGEMENT_MESSAGE) {
			/* Create an acknowlegement message and send it */
			ra_message_t *replyMessage=ra_createMessage(RA_ACKNOWLEDGEMENT_MESSAGE, message->ticket);
#ifdef _HPUX
			ra_writeMessageToBuffer((unsigned char *)acknowlegementBuffer, 16, replyMessage);
#else
			ra_writeMessageToBuffer(acknowlegementBuffer, 16, replyMessage);
#endif
			sendto(((ra_serverInfo_t*)args)->sockfd, acknowlegementBuffer, 16, 0, (struct sockaddr*)&ca, client_len);
			ra_destroyMessage(replyMessage, TRUE);
		}
		messageProcessor(((ra_serverInfo_t*)args), message, (struct sockaddr*)&ca, sizeof(ca));

		/* If we allocated an overflow buffer then clean it up */
		if(correctBuffer != request) {
			free(correctBuffer);
			correctBuffer=NULL;
		}
		ra_destroyMessage(message, TRUE);
	}
}

/** CREATE_TCP_SERVER *****************************************
  * Creates a TCP socket server on a separate thread.  Once the server
  * is created the file descriptor and the thread ID of the server are
  * returned in a Server_info structure.
  * @param   portNum - the port you wish the server to be bound to.
  * @param resultSet - The ra_serverInfo structure with the
  *                    socket file descriptor, thread ID of the server,
  *                    and the function that will be called to process
  *                    incomming messages. The prototype of the fuction
  *                    must be of the type ra_messageProcessor.
  *					   THIS MEMORY MUST BE VALID FOR THE LIFE OF THE SERVER.
  * @returns  0 - create server was successful.
  *         <>0 - create failed, call ra_getlastErrorMajor/Minor
  *               to get the failure details.
  * IPV4-ONLY
  */
int ra_createTCPServer(unsigned short portNum,
					   ra_serverInfo_t *result) {

	HANDLE handle;
#ifdef _WIN32
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

	/* Run the server on a separate thread and return the tid */
#if defined(_WIN32)
	handle = CreateThread(NULL,					/* default security attributes */
						 0,						/* same stack size as current thread */
						 win32TCPServerProxy,	/* Thread entry point */
						 (LPVOID)result,		/* params */
						 0,						/* start executing immediately */
						 &(result->tid));		/* the thread ID */
	CloseHandle(handle);
#elif defined(_AIX)
	errno=0;
	pthread_attr_t thread_attr;

	pthread_attr_init(&thread_attr);
	pthread_attr_setstacksize( &thread_attr, 4194304 );
	int presult = pthread_create(&(result->tid), &thread_attr, TCPServer, result);
	if(presult!=0) {
		ra_setLastError(THREAD_CREATE_FAILED, errno);
		return -1;
	}
#else
	errno=0;
	int presult = pthread_create(&(result->tid), NULL, TCPServer, result);
	if(presult!=0) {
		ra_setLastError(THREAD_CREATE_FAILED, errno);
		return -1;
	}
#endif
	return 0;
}


/** TCP_SERVER *****************************************************
  * The actual server.  Processes requests on a separate thread
  * and delegates thier processing to PROCESS_MESSAGE
  */
void *TCPServer(void *args) {
/* 185579 begin */
#ifdef _WIN32
	size_t byte;
#else
	ssize_t byte;
#endif
/* 185579 end */
	size_t client_len;
	struct hostent *hpLocal;
	struct sockaddr_in serverAddress;
	char localhost[MAX_HOST_NAME_LENGTH+1];
	unsigned char request[MESSAGE_BUFFER_INCREMENT];
	int enable=1;

	/* Initialize buffers/structures */
	BZERO(&serverAddress, sizeof(serverAddress));
	client_len=sizeof(serverAddress);

	/* Get the local host information */
	gethostname(localhost,MAX_HOST_NAME_LENGTH);
	if((hpLocal=gethostbyname((char*)localhost))==NULL) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(GETLOCALHOST_FAILED, error);
		return (void*)-1;
	}

	/* Load the host info and address into the address struct */
	serverAddress.sin_family=hpLocal->h_addrtype;
	serverAddress.sin_port=htons(RA_CTL_PORT_NUM_SERVER);
	memcpy(&serverAddress.sin_addr, hpLocal->h_addr_list[0], hpLocal->h_length);

	while(TRUE) {
		unsigned char *correctBuffer;
		ra_message_t *message;
		ra_command_t *command;
		ra_command_t connectedCommand;
		int length;
		unsigned char *messageBuffer;
		BOOL firstConnectTial;
		ra_uint_t messageLength=0, offset=0;

			/* Set the socket up for connections using specified protocol */
		if((((ra_serverInfo_t*)args)->sockfd=socket(hpLocal->h_addrtype, SOCK_STREAM, 0))<0) {
			long error;
#ifdef _WIN32
			error=WSAGetLastError();
#else
			error=errno;
#endif
			ra_setLastError(SOCKET_CREATE_FAILED, error);
#ifdef _HPUX
			return (void *)FALSE;
#else
			return FALSE;
#endif
		}
		((ra_serverInfo_t*)args)->connected=FALSE;

		firstConnectTial=TRUE;
#ifdef __OS400__
		while(connect(((ra_serverInfo_t*)args)->sockfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress))<0) {
#else
		while(connect(((ra_serverInfo_t*)args)->sockfd, (const struct sockaddr*)&serverAddress, sizeof(serverAddress))<0) {
#endif
			/* Indicates the Agent Controller is unavailable.  Inform the agent */
			if(firstConnectTial) {
				connectedCommand.tag=RA_AGENT_CONTROLER_UNAVAILABLE;
				ra_copyRASTRING(&connectedCommand.info.agentName, &((ra_serverInfo_t*)args)->agentName);
				(*((ra_serverInfo_t*)args)->commandHandler)(&connectedCommand);
				ra_destroyRASTRING((ra_string_t*)&connectedCommand.info.agentName);
				firstConnectTial=FALSE;
			}
			/* Try once a second */
			SLEEP(1000);
		}
		((ra_serverInfo_t*)args)->connected=TRUE;

		/* Inform the agent we are connected */
		connectedCommand.tag=RA_AGENT_CONTROLER_AVAILABLE;
		ra_copyRASTRING(&(connectedCommand.info.agentName), &((ra_serverInfo_t*)args)->agentName);
		(*((ra_serverInfo_t*)args)->commandHandler)(&connectedCommand);
		ra_destroyRASTRING((ra_string_t*)&connectedCommand.info.agentName);

		/* Prepare the RA_AGENT_ACTIVE message*/
		message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
		command=ra_addCommandToMessage(message, NULL);
		command->tag=RA_AGENT_ACTIVE;
		command->info.agent_active.context=0;
		command->info.agent_active.processId=((ra_serverInfo_t*)args)->processID;
		ra_copyRASTRING(&command->info.agent_active.processUUID, &((ra_serverInfo_t*)args)->processUUID);
		ra_copyRASTRING(&command->info.agent_active.agent, &((ra_serverInfo_t*)args)->agentName);
		ra_copyRASTRING(&command->info.agent_active.agentUUID, &((ra_serverInfo_t*)args)->agentUUID);
		ra_copyRASTRING(&command->info.agent_active.agentType, &((ra_serverInfo_t*)args)->agentType);

		/* Pack the registration message and send it to the Agent Controller */
		length=ra_determineMessageLength(message);
		messageBuffer=(unsigned char*)malloc(length*sizeof(char));
		length=ra_writeMessageToBuffer(messageBuffer, length, message);
		byte=send(((ra_serverInfo_t*)args)->sockfd,		/* Use the server socket */
#ifdef __OS400__
				  (char*)messageBuffer,			/* the message */
#else
				  (const char*)messageBuffer,			/* the message */
#endif
				  length ,								/* message length */
				  0);									/* no special flags */

		if(byte<0) {
			long error;
#ifdef _WIN32
			error=WSAGetLastError();
#else
			error=errno;
#endif
			ra_setLastError(SOCKET_WRITE_FAILED, error);
		}
		ra_destroyMessage(message, TRUE);
		free(messageBuffer);

#ifdef _WIN32
		/* On windows we need to ensure that the socket is not inherited by
		   child processes
		*/
		if(!(errno=(SetHandleInformation((HANDLE)(((ra_serverInfo_t*)args)->sockfd), HANDLE_FLAG_INHERIT, 0)))) {
			ra_setLastError(HANDLE_NO_INHERIT_FAILED, errno);
			return (void*)-1;
		}
#endif

		/* Keep the connected socket alive even after long pauses */
		setsockopt(((ra_serverInfo_t*)args)->sockfd,
			   SOL_SOCKET,
			   SO_KEEPALIVE,
#ifdef __OS400__
			   (char*)&enable,
#else
			   (const char*)&enable,
#endif
			   sizeof(enable));

		while(TRUE) {
			/* Read as much data as we can */
			byte=READ(((ra_serverInfo_t*)args)->sockfd, request+offset, MESSAGE_BUFFER_INCREMENT-offset);
#ifdef _WIN32
			/* On Windows a return value of zero indicates the remote side closed
			   the connection
			*/
			if(byte==SOCKET_ERROR || byte==0) {
				long error=WSAGetLastError();
				/* Close the socket and allow this thread to exit */
				break;
			}
#else
/* 185579 begin */
			 /* On UNIX if read returns -1 there was a socket error so quit reading*/
			if (byte < 0) {
		   		break;
	   		}
/* 185579 end */
#endif
			 /* We assume the master buffer will not be fragmented due to an error later */
			offset=0;

			 /* Validate the header of this message.  If it is invalid go into
				error recovery mode.  If it is valid then find out how long this
				message is.
			*/
			correctBuffer=request;
validate:
			if(validateRecvBuffer(correctBuffer, byte)) {
				/* Determine how long this message is */
				messageLength=((correctBuffer[16]<<24)
						  |(ra_uint_t)(correctBuffer[17]<<16)
						  |(ra_uint_t)(correctBuffer[18]<<8)
						  | correctBuffer[19]);

				/* Do we have the entire message? */
				if(messageLength>byte) {
					unsigned char *temp=(unsigned char*)malloc(messageLength);
					size_t currentLength=byte;
					memcpy(temp, correctBuffer, currentLength);
					while(currentLength<messageLength) {
						byte=READ(((ra_serverInfo_t*)args)->sockfd, temp+currentLength, messageLength-currentLength);
						currentLength+=byte;
					}
					message=ra_readMessageFromBuffer(temp, (unsigned long)messageLength);
					/* Forward to the handler */
					messageProcessor(((ra_serverInfo_t*)args), message, (struct sockaddr*)&serverAddress, client_len);
					ra_destroyMessage(message, TRUE);
					free(temp);
				}
				else {
					/* We have the entire message, but do we have more then one message */
					message=ra_readMessageFromBuffer(correctBuffer, (unsigned long)(byte));
					messageProcessor(((ra_serverInfo_t*)args), message, (struct sockaddr*)&serverAddress, client_len);
					byte=byte-message->length;
					memcpy(correctBuffer, correctBuffer+message->length, byte);
					ra_destroyMessage(message, TRUE);
					goto validate;
				}
			}
			else {
				byte=errorRecoveryScan(correctBuffer, byte);
				/* If we have enough left that constitutes a header continue processing */
				if(byte>=12) {
					goto validate;
				}
				else {
					/* We need to read some more data.  Keep what we have so far */
					if(correctBuffer!=request) {
						memcpy(request, correctBuffer, byte);
					}
					offset=byte;
				}
			}

		}
		/* close the connection */
		ra_closeSocket(((ra_serverInfo_t*)args)->sockfd);
	}
	free(args);
	return NULL;
}

/** CONNECT_TO_TCP_SERVER **************************************
  * Opens a connection to a remote TCP server socket specified.
  * @param       ip  - the IP address of the remote server. THIS
  *                    IS ASSUMED TO BE IN NETWORK BYTE ORDER.
  * @param   portNum - The port the remote server is listening on.
  * @param    socket - The socket file descriptor that the resulting
  *                    connection is managed on. MUST BE PREALLOCATED.
  * @returns   0 - connect was successful
  *           <0 - connect failed, can ra_getLastErrorMajor/Minor
  *                to get the failure details.
  * IPV4-ONLY
  */
int ra_connectToTCPServer(ra_uint_t remoteIP,
						  unsigned short remotePortNum,
						  SOCKET  *sockfd) {

	struct sockaddr_in serverAddress;
	unsigned short localPortNum=DATA_PORT_NUM_CLIENT;
	int enable=1;
	BOOL nagle=FALSE;
	struct linger linger;
#ifndef _WIN32
	int retval;
	int loopcnt = 0;

#else
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

    /* Set the socket up for connections using specified protocol.
	   Note this is for IP networks only.
	*/
	*sockfd=socket(PF_INET, SOCK_STREAM, 0);
	if((*sockfd)<0) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(SOCKET_CREATE_FAILED, error);
		return -1;
	}

	/* Keep the socket alive even after long pauses */
	setsockopt(*sockfd,
			   SOL_SOCKET,
			   SO_KEEPALIVE,
#ifdef __OS400__
			   (char*)&enable,
#else
			   (const char*)&enable,
#endif
			   sizeof(enable));

	/* Wait for up to 100 seconds to empty the buffer before allowing socket to close */
	linger.l_onoff=1;
	linger.l_linger=100;
	setsockopt(*sockfd,
			   SOL_SOCKET,
			   SO_LINGER,
#ifdef __OS400__
			   (char*)&linger,
#else
			   (const char*)&linger,
#endif
			   sizeof(linger));

#ifndef MVS
	/* Disable the Nagle algorithm, this may be a bad idea!!!  */
	setsockopt(*sockfd,
			   IPPROTO_TCP,
			   TCP_NODELAY,
#ifdef __OS400__
			   (char*)&nagle,
#else
			   (const char*)&nagle,
#endif
			   sizeof(nagle));
#endif

	/* Load the remote host info and address into the address struct.
	  Note this IPV4 Specific
	*/
#ifdef __OS400__
	BZERO(&serverAddress, sizeof(serverAddress));
#endif
	memcpy(&(serverAddress.sin_addr), &remoteIP, sizeof(ra_uint_t));
	serverAddress.sin_family=PF_INET;
	serverAddress.sin_port=htons(remotePortNum);


	/* Attempt to make the connection */
#ifdef __OS400__
	if(connect(*sockfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress))<0) {
#else 
#ifndef _WIN32
	/* DNS - bugzilla 49199:  On some UNIX platforms the connect call can be interrupted by a signal.
	 * errno is set to EINTR in this case. The connect documentation says the operation
	 * continues asynchronously when this occurs.  This is particularly a problem on HPUX.
	 * We'll try to connect again a finite number of times to see if the connect operation
	 * completes.
	 */
	do {
		retval = connect(*sockfd, (const struct sockaddr*)&serverAddress, sizeof(serverAddress));
		loopcnt++;
	} while (retval == -1 && errno == EINTR && loopcnt < 50);

	if (retval == -1) {
#else
	if(connect(*sockfd, (const struct sockaddr*)&serverAddress, sizeof(serverAddress))<0) {
#endif
#endif
		int error=0;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(SOCKET_CONNECT_FAILED, error);
		return -1;
	}
	return 0;

}

/** WRITE_TO_SOCKET  ****************************************************
  * Place a buffer of data on the sockets buffer.  The current implementation
  * only loop stupidly trying to place the data on the buffer.  A future
  * implementation should leverage a queue for each socket and handle the IO
  * asynchronously to avoid blocking until information is placed on the wire.
  * @param        fd - the file descriptor of the socket to write to.
  * @param    buffer - address to start pulling data from.
  * @param byteCount - the nuber of bytes to write to the buffer.
  * @returns     >-1 - the number of bytes written to the socket.
  *               -1 - severe socket error, write failed.
  * TO_DO: add error code settings for severe socket errors.
  */

int ra_writeToSocket(SOCKET fd,
					 char *buffer,
					 ra_uint_t byteCount) {

	/* RKD:  On the various platforms, based upon the third parameter of send we may need
	         to break up the message.  This is just a skeleton that needs more investigation.
	if(sizeof(ra_uint_t)>=sizeof(int)) {

	}
	*/
	return send(fd, buffer, byteCount, 0);
}

/**
 * Resolves the formatted (e.g dot-delimited, null-terminated) local network 
 * order IPv4 address.
 * 
 * The local IP address is the network order IPv4 address of the first network 
 * interface bound to this physical host.
 *
 * The format of the local network order IPv4 address is:
 *
 *		nnn.nnn.nnn.nnn\0
 *
 * where n is a non-padded decimal (base 10) numeric.
 *
 * NOTE:  This function only supports formatting IPv4 addresses.
 *
 * @param ipAddressBuffer
 *           The string buffer to contain the formatted (e.g dot-delimited, 
 *           null-terminated) local network order IPv4 address.  This buffer
 *           must be sized sufficiently for an IPv4 (e.g. nnn.nnn.nnn.nnn\0) 
 *           address.
 * @returns  
 *           The number of characters written to the parameter buffer (excluding 
 *           null terminator character.  Otherwise, -1 if a severe error occurred. 
 *           Call ra_getLastErrorMajor/Minor() to determine the error conditions.
 * IPV4-ONLY
 */
short ra_getFormattedLocalIPAddress(char *ipAddressBuffer){
	
	char localhost[MAX_HOST_NAME_LENGTH];
	struct hostent *hpLocal;
	unsigned char *firstAddress;
	int index = 0;
	int counter = 0;

#ifdef _WIN32
	
	/* On WinDoze we have to initialize the sockets: */
	if(ra_win32InitializeSockets() < 0) {
		return -1;
	}
#endif

	/* Get the local host informantion: */
	gethostname(localhost,MAX_HOST_NAME_LENGTH);
	
	hpLocal = gethostbyname(((char*)(localhost)));

	if(hpLocal == NULL) {
		
#ifdef _WIN32
		ra_setLastError(GETLOCALHOST_FAILED, WSAGetLastError());
#else
		ra_setLastError(GETLOCALHOST_FAILED, errno);
#endif
		
		return -1;
	}

	/* Get the IP address of the first network interface bound to this physical host: */
	firstAddress = ((unsigned char*)(hpLocal->h_addr_list[0]));

	/* Iterate all the segments of the IP address and build the formatted (e.g. dot-delimited) IP address: */
	while(counter < (hpLocal->h_length)){
	
		index += sprintf((ipAddressBuffer + index), "%u", firstAddress[counter++]);

		ipAddressBuffer[index++] = '.';
	}

	/* Add the null terminator character: */
	ipAddressBuffer[--index] = '\0';

	return index;
}

/** GET_LOCAL_IP_ADDRESS *******************************************
  * Determine the IP address of the first network interface bound to
  * this physical host.
  * @returns >-1 - the 32bit, network order, IPv4 address of this
  *                interface.
  *           -1 - severe error occured getting the IP address.  Call
  *                ra_getLastErrorMajor/Minor to determine the
  *                error conditions.
  * IPV4-ONLY
  */
long ra_getLocalIPAddress() {
	char localhost[MAX_HOST_NAME_LENGTH];
	struct hostent *hpLocal;
	long result;

#ifdef _WIN32
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

	/* Get the local host informantion */
	gethostname(localhost,MAX_HOST_NAME_LENGTH);
	if((hpLocal=gethostbyname((char*)localhost))==NULL) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(GETLOCALHOST_FAILED, error);
		return -1;
	}

	memcpy(&result, hpLocal->h_addr_list[0], sizeof(long));

	return result;
}
 

/** GET_LOCAL_IP_ADDRESSES *******************************************
  * Get all the IP addresses associated with this physical host.
  * @param  addr - the address of a ra_IPAddrresses_t structure to load
  *				   the IP addresses into.  THE USER MUST FREE THE STORAGE
  *                SPECIFIED IN THE addresses MEMBER when they are finished
  *                with the data.  The addresses will be in NETWORK BYTE ORDER.
  * @returns   0 - success.
  *           -1 - severe error occured getting the IP address.  Call
  *                ra_getLastErrorMajor/Minor to determine the
  *                error conditions.
  *  IPV4-ONLY
  */
short ra_getLocalIPAddresses(ra_IPAddresses_t *addr) {
	char localhost[MAX_HOST_NAME_LENGTH];
	struct hostent *hpLocal;
	int count, i;

#ifdef _WIN32
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

	/* Get the local host informantion */
	gethostname(localhost,MAX_HOST_NAME_LENGTH);
	if((hpLocal=gethostbyname((char*)localhost))==NULL) {
		long error;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(GETLOCALHOST_FAILED, error);
		return -1;
	}

	/* how big is each address */
	addr->addressLength=hpLocal->h_length;

	/* Count the number of addresses */
	for(count=0; hpLocal->h_addr_list[count]!='\0'; count++);

	/* Allocate and load the "addr" with the addresses */
	addr->addresses=(unsigned char*)ra_malloc((count+1)*addr->addressLength+1);

	i=0;
	while(i<count) {
		memcpy((&addr->addresses[i*addr->addressLength]), hpLocal->h_addr_list[i], addr->addressLength);
		i++;
	}
	/* Add 127.0.0.1 */
	*((unsigned char*)(&addr->addresses[count*addr->addressLength]))=0x7F;
	*((unsigned char*)(&addr->addresses[count*addr->addressLength]+1))=0x00;
	*((unsigned char*)(&addr->addresses[count*addr->addressLength]+2))=0x00;
	*((unsigned char*)(&addr->addresses[count*addr->addressLength]+3))=0x01;

	/* Null the last address */
	addr->addresses[(count+1)*addr->addressLength]='\0';

	return 0;
}

/** CLOSE_SOCKET  **********************************************
  * Close and release the resources held by a socket.
  */
void ra_closeSocket(SOCKET sockfd) {
#ifdef _WIN32
	shutdown(sockfd, SD_BOTH); /* 9707 */
	closesocket(sockfd);
#else
	shutdown(sockfd, 2); /* 238855 */ /* 9512 */
	close(sockfd); /* Bug 67736 */
#endif
}

/** GET_LOCAL_HOST_NAME  ***************************************
  * Load the local host name into a buffer.
  * @param        buffer - the buffer to locad the host name into.
  * @param maxNameLength - the length of the buffer.
  */
int ra_getLocalHostName(char *buffer, unsigned int maxLength) {
	int result;

#ifdef _WIN32
	/* On WinDoze we have to initialize the sockets */
	if(ra_win32InitializeSockets()<0) {
		return -1;
	}
#endif

	result=gethostname(buffer,maxLength);
	if(result==-1) {
		int error=0;
#ifdef _WIN32
		error=WSAGetLastError();
#else
		error=errno;
#endif
		ra_setLastError(GETLOCALHOST_FAILED, error);
		return -1;
	}
	return result;
}


/** VALIDATE_RECV_BUFFER  *****************************************************
  * This is a simple test to ensure a message starts with the majic number
  * and that it at least contains enough information that we can determine
  * the message length.
  */
static BOOL validateRecvBuffer(unsigned char *buffer, int length) {
	/* Do we have a full header? */
	if(length<24) {
		return FALSE;
	}

	/* Compare against the majic number 0x82656780 */
	if(buffer[0]!=0x82 || buffer[1]!=0x65 || buffer[2]!=0x67 || buffer[3]!=0x80) {
		return FALSE;
	}
	return TRUE;

}


/** ERROR_RECOVERY_SCAN  *****************************************************
  * When we get a bad message flow we go into recovery mode, searching for the
  * next occurance of the majic number in the stream.  The buffer is then
  * compressed and number of valid remaining bytes is returned.
  */
static ra_uint_t errorRecoveryScan(unsigned char *buffer, int length) {
	int offset;
	/* If there isn't enough bytes to check the majic number return zero */
	if(length<4) {
		return length;
	}
	/* Search for the next occurance of the majic number */
	for(offset=0; offset<length-3; offset++) {
		if(buffer[offset]==0x82 && buffer[offset+1]==0x65 && buffer[offset+2]==0x67 && buffer[offset+3]==0x80) {
			memcpy(buffer, buffer+offset, length-offset);
			return length-offset;
		}
	}
	/* If we didn't find the magic number we need to save the last 3 bytes and return */
	memcpy(buffer, buffer+offset, length-offset);
	return length-offset;

}





/****************************  WIN32 Specific additions ***********************/

/**
  * Proxy to the server method for porting ease
  */
#ifdef _WIN32
DWORD WINAPI win32BoundUDPServerProxy(LPVOID args) {
	DWORD returnVal=0;
	UDPServer(args);
	return returnVal;
}

DWORD WINAPI win32TCPServerProxy(LPVOID args) {
	DWORD returnVal=0;
	TCPServer(args);
	return returnVal;
}

int ra_win32InitializeSockets() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);

	if (err != 0) {
		/* Tell the user that we couldn't find a useable */
		/* winsock.dll.     */
		ra_setLastError(SOCKET_INITIALIZE_FAILED, err);
		return -1;
	}

	/* Confirm that the Windows Sockets DLL supports 1.1.*/
	/* Note that if the DLL supports versions greater */
	/* than 1.1 in addition to 1.1, it will still return */
	/* 1.1 in wVersion since that is the version we */
	/* requested. */

	if ( LOBYTE( wsaData.wVersion ) != 2 ||
		HIBYTE( wsaData.wVersion ) != 2 ) {
		/* Tell the user that we couldn't find a useable */
		/* winsock.dll. */
		ra_setLastError(SOCKET_INITIALIZE_FAILED, err);
		WSACleanup();
		return -1;
	}
	return 0;
}

#endif


