/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation.
 * 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
 *
 * Contributors:
 *    Hoang M Nguyen, Intel - Initial API and Implementation
 *    IBM - Portions of old RAC code (brought over by Hoang M Nguyen)
 *
 * $Id: TransportSupportSocket.c,v 1.33 2009/08/26 14:59:54 jwest Exp $
 *
 *******************************************************************************/ 

#include "RAComm.h"

#define __TPTPCOMMON_H__

#include "tptp/TransportSupport.h"

#include "tptp/NoLog.h"

#ifdef MVS
	#include <sys/sys_time.h>
	#define NI_MAXHOST 256
#endif

// The following if-else was added for IPv6 support
#ifdef _WIN32
// TODO It's a workaround to get compiled on WIN IA32, it needs to be resolved.
	#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
	#include <ws2tcpip.h>
#else 
	#define INVALID_SOCKET -1
	#define SOCKET_ERROR -1
#endif

#ifdef _SOLARIS
    #define INADDR_NONE (-1)
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
#endif

static void convertAddrToIpString(struct sockaddr_storage * addr, char **hostname);
static SOCKET getSocketFromAddrList(struct addrinfo *AddrInfo, struct addrinfo **sockAddrOut);

/**
 *********************************************************
 *
 * @brief
 *    get the localhost (server) socket connection 
 *
 * @param portNum The port number of the local host to listen on. 
 * @param outResult A pointer to an array of SOCKETS to be listened on
 * @param outNumSockets The integer that is passed in as a pointer to this function is set to the number of sockets of the outResult array
 *********************************************************/
int getTheSocket(int portNum, SOCKET *outResult, int *outNumSockets) {
	int Family = PF_UNSPEC;
    int SocketType = SOCK_STREAM;
    char PortStr[8];
    char *Address = NULL;
    int i, RetVal;

	struct addrinfo Hints, *AddrInfo, *AI;
    SOCKET* sockArr;
	
	SOCKET serverSocket;

	int ServSockSize = 0;

	sprintf(PortStr, "%d", portNum);

	sockArr = outResult;

    // MS' description of the following call:
    // By setting the AI_PASSIVE flag in the hints to getaddrinfo, we're
    // indicating that we intend to use the resulting address(es) to bind
    // to a socket(s) for accepting incoming connections.  This means that
    // when the Address parameter is NULL, getaddrinfo will return one
    // entry per allowed protocol family containing the unspecified address
    // for that family.
    //
    memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
    Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
    RetVal = getaddrinfo(Address, PortStr, &Hints, &AddrInfo);
    if (RetVal != 0) {
        return -1;
    }
    
	// First check for IPv6 connections
    for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next) {

        // Highly unlikely, but check anyway.
        if (i == FD_SETSIZE) {
               		TPTP_LOG_ERROR_MSG("getaddrinfo exceeded the number of available addrs");
            return -1;
        }

        // PF_INET6 only
		if (!(AI->ai_family == PF_INET6)) {
            continue;
		}

		serverSocket = bindAndListen(AI);
		if(serverSocket >= 0) {
			sockArr[ServSockSize++] = serverSocket;
		}
		i++;
    }

	// Then check IPv4
	for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next) {

        // Highly unlikely, but check anyway.
        if (i == FD_SETSIZE) {
           		TPTP_LOG_ERROR_MSG("getaddrinfo returned more addresses than we could use.");
            return -1;
        }
        
		if (!(AI->ai_family == PF_INET)) {
            continue;
		}

		// jwest: On our Linux machines, listening on IPv6 also listened on IPv4. So if we get an error here, "Address already in Use", we can ignore it.
		serverSocket = bindAndListen(AI);
		if(serverSocket >= 0) {
			sockArr[ServSockSize++] = serverSocket;
		}
		
		i++;
    }

    freeaddrinfo(AddrInfo);

	*outNumSockets = ServSockSize;

    if (ServSockSize == 0) {
   		TPTP_LOG_ERROR_MSG("Fatal error: unable to serve on any address.") ;
        return -1;
    }

	return 0;
}

/**
 *********************************************************
 *
 * @brief
 *    bind and prepare the server socket
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
SOCKET bindAndListen(struct addrinfo *AI) {

	SOCKET ServSock;

	// Open a socket with the correct address family for this address.
    ServSock = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
    if (ServSock == INVALID_SOCKET) {
		return -1;
    }

	if (bind(ServSock, AI->ai_addr, (int)AI->ai_addrlen) == SOCKET_ERROR) {
		closeThisSocket(ServSock);
		return (-1);
       }

	if (listen(ServSock, 5) == SOCKET_ERROR) {
		closeThisSocket(ServSock);
		return (-1);
	}

	return (ServSock);

}

/**
 *********************************************************
 *
 * @brief
 *    get the port associated with the socket connection
 *
 * @return
 *    the port number
 * IPV4-ONLY
 *********************************************************/
int getSocketPort(SOCKET sock)
{
	int              len = sizeof(struct sockaddr);
	struct sockaddr  addr;

	getsockname( sock, &addr, &len );

	return ntohs( ((struct sockaddr_in*)(&addr))->sin_port );
}


/**
 *********************************************************
 *
 * @brief
 *		Gets the localhost info. 
 * 		
 * 		Note: When resolving the addresses of the local host, this function will always return the 
 *	 	first in the list of addresses given to it by the operating system. For the actual list of local addresses, 
 * 		use a different function.   
 * 		
 * 		Returns NULL if no address could be obtained.
 *  
 * @return
 *    host info data structure
 *********************************************************/
struct sockaddr_storage* getHostInfo() 
{
	int RetVal;
	char *Address = NULL;
	char *Port = "10002"; // Hardcoded because we need a port

	struct addrinfo Hints, *AddrInfo;
	struct sockaddr_storage *result = NULL;


    int Family = PF_UNSPEC; // Give us both the IPv4 and IPv6 addresses
    int SocketType = SOCK_STREAM;
	
	memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
    Hints.ai_flags = AI_NUMERICHOST;

    RetVal = getaddrinfo(Address, Port, &Hints, &AddrInfo);

    if(RetVal == 0) {
    
		result = cloneSockAddr((struct sockaddr_storage *)AddrInfo->ai_addr);
		
		freeaddrinfo(AddrInfo);
    }
	
	return (result);
}

struct hostent * getHostInfoIPv4M() 
{
	char   localhost[MAX_HOST_NAME_LENGTH+1];

	// Get the local host informantion 
	if (gethostname(localhost,MAX_HOST_NAME_LENGTH) < 0) return NULL;
	 
	return gethostbyname((char*)localhost) ;
}

/**
 *********************************************************
 *
 * @brief
 *    convert the given IP address string from "II.JJ.KK.LL"
 *    format to an unsigned long value
 *
 * @return
 *    0          success
 *    non-zero   failure
 *********************************************************/
int convertIPAddressStringToUlongIPv4M( char *addrStr, unsigned long* addr )
{
	*addr = inet_addr( addrStr );
	if ( *addr == INADDR_NONE )
	{
		/* The string wasn't recognized as a valid IP address */
		return -1;
	}

	return 0;
}

/**
 *********************************************************
 *
 * @brief
 *    get the IP address associated with the given socket
 *
 * @param sock       The socket whose IP address is returned
 * @param ipAddrStr  Pointer to receive the address of the
 *                   IP addr string.  The caller must free
 *                   the memory allocated for this string
 *                   using tptp_free.
 *
 * @return
 *    0          success
 *    non-zero   failure code
 *
 *********************************************************/
int getSocketIPString( SOCKET sock, char** ipAddrStr )
{
	struct sockaddr_storage *sockAddrPtr = NULL;

	if ( sock == (SOCKET)NULL )
	{
		sockAddrPtr = getHostInfo();
	}
	else
	{
		sockAddrPtr = getSelfAddrOfSocket(sock);
	}

	if (sockAddrPtr == NULL) return -1;

	convertAddrToIpString(sockAddrPtr, ipAddrStr);
	if ( *ipAddrStr == NULL )
	{
		return TPTP_SYS_NO_MEM;
	}
	return 0;
}

int getSocketIPStringIPv4M( SOCKET sock, char** ipAddrStr )
{
	char*  temp;

	if ( sock == (SOCKET)NULL )
	{
		struct hostent* hinfo;

		hinfo = getHostInfoIPv4M();
		if (hinfo == NULL) return -1;

		/* The socket code owns 'temp' as returned below, so we need to copy it */
		temp = inet_ntoa(*(struct in_addr*)(hinfo->h_addr_list[0]));
	}
	else
	{
		int                saSize;
		struct sockaddr_in saddr;

		saddr.sin_family      = AF_INET;
		saddr.sin_port        = htons(0);
		saddr.sin_addr.s_addr = INADDR_ANY;
		BZERO(&saddr.sin_zero,8);

		saSize = sizeof(struct sockaddr);
		getsockname(sock,(struct sockaddr *)&saddr,&saSize);

		/* The socket code owns 'temp' as returned below, so we need to copy it */
		temp = inet_ntoa(saddr.sin_addr);
	}

	//Need to add 1 to strlen so free doesn't crash!
	*ipAddrStr = (char *)tptp_malloc( strlen(temp) +1);
	if ( *ipAddrStr == NULL )
	{
		return TPTP_SYS_NO_MEM;
	}

	strcpy( *ipAddrStr, temp );

	return 0;
}


/**
 *********************************************************
 *
 * @brief
 *    get the IP address associated with the given socket
 *
 * @param sock       The socket whose IP peer address is returned
 * @param ipAddrStr  Pointer to receive the address of the
 *                   IP addr string.  The caller must free
 *                   the memory allocated for this string
 *                   using tptp_free.
 *
 * @return
 *    0          success
 *    non-zero   failure code
 *
 *********************************************************/
int getPeerIPString( SOCKET sock, char** ipAddrStr )
{
	struct sockaddr_storage *sockAddrPtr = NULL;

	if ( sock == (SOCKET)NULL )
	{
		return -1;
	}

	sockAddrPtr = getPeerAddrOfSocket(sock);	

	if(sockAddrPtr == NULL) {
		TPTP_LOG_ERROR_MSG("Error: Unable to get the address information for the remote peer.") ;
	}

	convertAddrToIpString(sockAddrPtr, ipAddrStr);
	tptp_free(sockAddrPtr);
	if ( *ipAddrStr == NULL )
	{
		return TPTP_SYS_NO_MEM;
	}

	return 0;
}


struct hostent * getTargetHostInfoIPv4M(char* hostname) 
{
	struct hostent *pInfo = NULL ;

	/* Get the local host informantion */
	pInfo = gethostbyname(hostname) ;

	return ( pInfo );
}

/**
 *********************************************************
 *
 * @brief
 *    get the localhost info
 *
 * @return
 *    host info data structure
 *********************************************************/
int convertHostnameToAddrs(char *hostname, struct sockaddr_storage *** outResult, int *outNumResults) {
	int RetVal;
	int i;

	struct addrinfo Hints, *AddrInfo, *AI;

    int Family = PF_UNSPEC; // Give us both the IPv4 and IPv6 addresses
    int SocketType = SOCK_STREAM; // TCP only

	struct sockaddr_storage ** result;

	// malloc an array of pointers to struct sockaddr_storages
	result = (struct sockaddr_storage **)tptp_malloc(sizeof(struct sockaddr_storage *) * 128);
	
	memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
	
	// This was originally in the code, but it prevents lookups of host names, e.g. www.google.com. It only works with numeric hosts.
    // Hints.ai_flags = AI_NUMERICHOST;  

    RetVal = getaddrinfo(hostname, NULL, &Hints, &AddrInfo);
    
    // If no error was returned
    if(RetVal == 0) {
    
		for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next, i++) {
			result[i] = cloneSockAddr((struct sockaddr_storage *)AI->ai_addr);
		}
		freeaddrinfo(AddrInfo);
    } else {
    	// If an error was returned, free the results
    	tptp_free(result);
    	result = NULL;
    	i = 0;
    }
    
	

	*outResult = result;
	*outNumResults = i;

	return RetVal;
}


/**
 *********************************************************
 *
 * @brief
 *    frees acquired addresses list
 *
 * @return
 *    host info data structure
 *********************************************************/
void freeAddressList(struct sockaddr_storage ** arrOfAddr, int numAddr) {
	int x = 0;

	for(x = 0; x < numAddr; x++) {
		tptp_free(arrOfAddr[x]);
	}

}

/**
 *********************************************************
 *
 * @brief
 *    get peer address
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *  IPV4-ONLY 
 *********************************************************/
int getPeerName(SOCKET sock, unsigned char* pAddr)
{
	int clientLen = sizeof(struct sockaddr_in);
	struct sockaddr_in sockAddr;

	    /* Retrieve the IP address of the remote client */
	getpeername(sock, (struct sockaddr *)&sockAddr, (int *)&clientLen);

	memcpy(pAddr, &sockAddr.sin_addr, sizeof(long));

	return 0;

}


/**
 *********************************************************
 *
 * @brief
 *    Get server socket ready and accept incoming connection requests
 *
 * @return
 *    the socket for the connection request
 *********************************************************/

SOCKET  acceptSocketConnection(SOCKET serverSock)
{
	SOCKET clientSock ;
	struct sockaddr_storage clientAddress;
	size_t clientLen=sizeof(clientAddress);
	int    rc = 0 ;
	int    enable=1;
	struct linger linger;

	TPTP_LOG_DEBUG_MSG("Ready to accept next socket connection request...") ;

    clientSock = accept(serverSock, (struct sockaddr * ) & clientAddress, (int *)&clientLen);  

	rc = isSocketValid(clientSock) ;

	if ( rc == 0 )
	{
		TPTP_LOG_ERROR_MSG("Error: unable to accept connection.") ;
	}
	else
	{
		TPTP_LOG_DEBUG_MSG("Accept new socket connection request.");
	}


	/* Keep the incomming socket alive even after long pauses */
	if (rc != 0)
	{
		setsockopt(clientSock,
					SOL_SOCKET,
					SO_KEEPALIVE,
					(const char*)&enable,
					sizeof(enable));

		/* Wait for up to 3 seconds to finish reading/writing information to the socket */
		linger.l_onoff=1;
		linger.l_linger=3;

		setsockopt(clientSock,
				   SOL_SOCKET,
				   SO_LINGER,
				   (const char*)&linger,
				   sizeof(linger));
	}

	return (clientSock) ;
}


/**
 *********************************************************
 *
 * @brief
 *    connect to the given system
 *
 * @return
 *    0 - Success and the socket representing this connection
 *    nonzero - Error.
 *********************************************************/
int connectToTCPServer(char * hostname, int port, SOCKET *pSock)
{
	int    rc = 0 ;
	SOCKET sock ;
	struct addrinfo *AI = NULL;

	TPTP_LOG_DEBUG_MSG("connectToTCPServer...") ;

	sock = connectToHostWithHostname(hostname, port, &AI);

	if(sock != INVALID_SOCKET) {
		freeaddrinfo(AI);
	}

	*pSock = sock ;

	return ( rc ) ;
}


int connectToTCPServerIPv4M(unsigned long ipAddr,
					   unsigned short portNum,
					   SOCKET *pSock)
{
	int    rc = 0 ;
	SOCKET sock ;
	struct sockaddr_in serverAddress;
	int    enable = 1 ;
	BOOL   nagle  = FALSE;
	struct linger linger;
	int    loopcnt = 0 ;

	TPTP_LOG_DEBUG_MSG("connectToTCPServer...") ;

	sock = socket(PF_INET, SOCK_STREAM, 0);

	setsockopt(sock,
			   SOL_SOCKET,
			   SO_KEEPALIVE,
			   (const char*)&enable,
			   sizeof(enable));

	linger.l_onoff=1;
	linger.l_linger=3;
	setsockopt(sock,
			   SOL_SOCKET,
			   SO_LINGER,
			   (const char*)&linger,
			   sizeof(linger));

	setsockopt(sock,
			   IPPROTO_TCP,
			   TCP_NODELAY,
			   (const char*)&nagle,
			   sizeof(nagle));

	memcpy(&(serverAddress.sin_addr), &ipAddr, sizeof(long));
	serverAddress.sin_family=PF_INET;
	serverAddress.sin_port=htons(portNum);

	/* go make the connection */
#ifndef _WIN32
	do {
		rc = connect(sock, (const struct sockaddr*)&serverAddress, sizeof(serverAddress));
		loopcnt++;
	} while (rc == -1 && errno == EINTR && loopcnt < 50);
#else
	rc = connect(sock, (const struct sockaddr*)&serverAddress, sizeof(serverAddress)) ;
#endif

	if (rc == 0)
		*pSock = sock ;

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    connect to the given system
 *
 * @return
 *    0 - Success and the socket representing this connection
 *    nonzero - Error.
 *********************************************************/
int connectToTCPServerAddr(struct sockaddr_storage * addr, int port, SOCKET *pSock)
{
	int    rc = 0 ;
	SOCKET sock ;

	TPTP_LOG_DEBUG_MSG("connectToTCPServerAddr...") ;

	setSockaddrStoragePort(addr, port);
	
	sock = connectToHostWithAddr(addr);

	*pSock = sock ;

	return ( rc ) ;
}




/**
 *********************************************************
 *
 * @brief
 *    common interface to terminate the given connection
 *    (socket).
 *
 * @return
 *    the actual number of bytes sent
 *********************************************************/

int  writeToSocket(SOCKET sock,
				   char *buffer,
				   int byteCount)
{

	int result = -1;

	result = (int) send(sock, buffer, byteCount, 0);

	return result;
}

#ifdef __linux__
int closeSocket (int socket) {
	struct linger linger;
	int rc;

	linger.l_onoff = 1;
	linger.l_linger = 0;

	rc = setsockopt(socket, SOL_SOCKET, SO_LINGER, (const char*) &linger, 
				sizeof(linger));

	close (socket);
	
	return rc;
}
#else
int closeSocket (int socket) {
	return closeThisSocket(socket);
}
#endif

struct sockaddr_storage * cloneSockAddr(struct sockaddr_storage * addr) {
	struct sockaddr_storage* result;

	if(addr->ss_family == PF_INET){
		result = (struct sockaddr_storage *)tptp_malloc(sizeof(struct sockaddr_in));
		memcpy(result, (struct sockaddr_in *)addr,sizeof(struct sockaddr_in));
		
	} else if(addr->ss_family == PF_INET6) {
		result = (struct sockaddr_storage *) tptp_malloc(sizeof(struct sockaddr_in6));
		memcpy(result, (struct sockaddr_in6 *)addr, sizeof(struct sockaddr_in6));
	} else {
		result = (struct sockaddr_storage *) NULL;
	}

	return result;
}

static void convertAddrToIpString(struct sockaddr_storage * addr, char **hostname) {
	int r;

	*hostname = (char *)tptp_malloc(sizeof(char) * NI_MAXHOST);
	if(*hostname == NULL) return;
	
	memset(*hostname, 0, sizeof(char) * NI_MAXHOST);
	// hostname[0] = 0;

	r = getnameinfo((struct sockaddr *)addr, sizeof(struct sockaddr_storage), *hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
}

/** SOCKET ->  struct sockaddr_storage * for the peer. */
struct sockaddr_storage * getPeerAddrOfSocket(SOCKET ConnSocket) {
	int result = 0;
	int addrLen = 0;
	struct sockaddr_storage * addr;

	addrLen = sizeof(struct sockaddr_storage);
    addr = (struct sockaddr_storage *)tptp_malloc(sizeof(struct sockaddr_storage));

	result = getpeername(ConnSocket, (struct sockaddr *) addr, &addrLen);

	if(result == SOCKET_ERROR) {
   		TPTP_LOG_ERROR_MSG("getpeername call failed to get peer address") ;
		return NULL;
	}

	return addr;	
}

/** SOCKET => struct sockaddr_storage *, of the host's side of the socket. For the peer's address, use getPeerAddrOfSocket. */
struct sockaddr_storage * getSelfAddrOfSocket(SOCKET ConnSocket) {
	struct sockaddr_storage Addr;
	struct sockaddr_storage *result;
	int r = 0;
	
	socklen_t AddrLen = 0;

	AddrLen = sizeof (Addr);

	r = getsockname(ConnSocket, (struct sockaddr *) & Addr, &AddrLen);

	if(r != 0) return NULL;

	result = (struct sockaddr_storage *)tptp_malloc(sizeof(struct sockaddr_storage));

	memcpy(result, &Addr, sizeof(struct sockaddr_storage));

	return result;
}

/** Creates a client socket connection to the given hostname/ip, on the given port. The sockAddrOut variable is set to the particular
	struct addrinfo that was used to create the connection. This allows you to determine the family, either IPV4 of IPV6 (if needed.)
	
	If a hostname is specified in 'hostnameOrIp', this method will try to connect on both the IPv4 and IPv6 addresses of the hostname, where available. It
	returns the first successful SOCKET connection as a result.

	If an IP address is specified in 'hostnameOrIp', this method will only connect on either the IPv4 (x.x.x.x) or IPv6 (xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx)
	address of that host (based on the format of the ip address that is based in).
	*/
SOCKET connectToHostWithHostname(char * hostnameOrIp, int port, struct addrinfo ** sockAddrOut ) {
    int Family = PF_UNSPEC;
    int SocketType = SOCK_STREAM; // TCP
    int RetVal;
	char portStr[8];
    struct addrinfo Hints, *AddrInfo;
    SOCKET ConnSocket;

	// Convert the integer port to a string for getaddrinfo
	sprintf(portStr, "%d", port);

    //
    // By not setting the AI_PASSIVE flag in the hints to getaddrinfo, we're
    // indicating that we intend to use the resulting address(es) to connect
    // to a service.  This means that when the Server parameter is NULL,
    // getaddrinfo will return one entry per allowed protocol family
    // containing the loopback address for that family.
    //

    memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
    RetVal = getaddrinfo(hostnameOrIp, portStr, &Hints, &AddrInfo);

    if (RetVal != 0) {
        return -1;
    }

	if (AddrInfo == NULL) {
        return -1;
    }
	
	ConnSocket = getSocketFromAddrList(AddrInfo, sockAddrOut);
	
	return ConnSocket;
}

/** Connects to the given address, and returns a SOCKET for that conncetion. If a socket could not be created,
	then INVALID_SOCKET is returned. Note: The struct sockaddr_storage must have a port specified. */
SOCKET connectToHostWithAddr(struct sockaddr_storage *addr) {
	SOCKET sock;
	int    enable = 1 ;
	BOOL   nagle  = FALSE;
	struct linger linger;
	int    rc = 0 ;
	int loopcnt = 0;

	// Open a socket with the correct address family for this address.
	sock = socket(addr->ss_family, SOCK_STREAM, 0); 
	
	setsockopt(sock,
			   SOL_SOCKET,
			   SO_KEEPALIVE,
			   (const char*)&enable,
			   sizeof(enable));

	linger.l_onoff=1;
	linger.l_linger=3;
	setsockopt(sock,
			   SOL_SOCKET,
			   SO_LINGER,
			   (const char*)&linger,
			   sizeof(linger));

	setsockopt(sock,
			   IPPROTO_TCP,
			   TCP_NODELAY,
			   (const char*)&nagle,
			   sizeof(nagle));

		/* go make the connection */
#ifndef _WIN32
	do {
		rc = connect(sock, (struct sockaddr*)addr, sizeof(struct sockaddr_storage));
		loopcnt++;
	} while (rc == -1 && errno == EINTR && loopcnt < 50);
#else
	rc = connect(sock, (struct sockaddr*)addr, sizeof(struct sockaddr_storage)) ;
#endif

	/*
	if (connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)) == SOCKET_ERROR) {
		closeThisSocket(sock);
		return INVALID_SOCKET;
	}*/
	
	return sock;
}

/** Sets the port of a struct sockaddr_storage. */
void setSockaddrStoragePort(struct sockaddr_storage *addr, int port) {
	if(addr->ss_family == PF_INET) {
		struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
		saddr->sin_port = htons(port);

	} else if(addr->ss_family == PF_INET6) {
		struct sockaddr_in6 *saddr = (struct sockaddr_in6 *) addr;

		saddr->sin6_port = htons(port);
	}
}

/** Given a list of addresses (AddrInfo), it returns a SOCKET for the first one it is able to successfully connect to. The socket is returned
	as the return value from this function, and the the address of that socket is returned in sockAddrOut. 
	Note: If a connection cannot be made, INVALID_SOCKET is returned. */
static SOCKET getSocketFromAddrList(struct addrinfo *AddrInfo, struct addrinfo **sockAddrOut) {
	struct addrinfo *AI;
	SOCKET ConnSocket = INVALID_SOCKET;
	char AddrName[NI_MAXHOST];

	if(sockAddrOut != NULL) {
		*sockAddrOut = 0;
	}

	//
    // Try each address getaddrinfo returned, until we find one to which
    // we can successfully connect.
    //
    for (AI = AddrInfo; AI != NULL; AI = AI->ai_next) {

        // Open a socket with the correct address family for this address. (on error -1 is returned)
        ConnSocket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);

        if (ConnSocket == INVALID_SOCKET) {
            continue;
        }

        // printf("Attempting to connect to: %s\n", Server ? Server : "localhost");
		if (connect(ConnSocket, AI->ai_addr, (int)AI->ai_addrlen) != SOCKET_ERROR) {
			if(sockAddrOut != NULL) {
				*sockAddrOut = AI;
			}
            break;
		}
		
		if (getnameinfo(AI->ai_addr, (socklen_t)AI->ai_addrlen, AddrName, sizeof (AddrName), NULL, 0, NI_NUMERICHOST) != 0) {
		}

		closeThisSocket(ConnSocket);
		ConnSocket = INVALID_SOCKET;
    }

	return ConnSocket;
}

/** struct sockaddr_storage * => IP address in byte format (e.g. in_addr / in6_addr) + family 
    The bytes are stored in outAddr, and the family is stored in family.*/
void getByteFormatFromSockAddr(struct sockaddr_storage *addr, void ** outAddr, int *family) {
	
	if(addr->ss_family == PF_INET) {

		struct sockaddr_in * sin = (struct sockaddr_in *) addr;
		struct in_addr ia = sin->sin_addr;
		
		struct in_addr *result = (struct in_addr *)tptp_malloc(sizeof(struct in_addr));
		memcpy(result, &ia, sizeof(ia));
		
		*family = PF_INET;

		*outAddr = result;

	} else { // PF_INET6

		struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) addr;
		struct in6_addr ia = sin6->sin6_addr;
		struct in6_addr *result = (struct in6_addr *)tptp_malloc(sizeof(struct in6_addr));
		memset(result, 0, sizeof(struct in6_addr));

		memcpy(result, &ia, sizeof(struct in6_addr));
		*family = PF_INET6;

		*outAddr = result;
		
	}
}

/** Given the IP Address in byte format (e.g. in_addr/inaddr_6) and the family 
		=>		struct sockaddr_storage * (either sockaddr_in or sockaddr_in6)  */
void getSockAddrFromByteFormat(void *inAddr, int family, struct sockaddr_storage **outResult) {
	
	if(family == PF_INET) {
		struct in_addr * ia = (struct in_addr *)inAddr;
		struct sockaddr_in * result = (struct sockaddr_in *)tptp_malloc(sizeof(struct sockaddr_in));
		
		memcpy(&result->sin_addr, ia, sizeof(struct in_addr));
		result->sin_family = family;
		result->sin_port = htons(0);
		memset(result->sin_zero, 0, 8);

		*outResult = (struct sockaddr_storage*)result;
		
	} else { // PF_INET6

		struct in6_addr * ia = (struct in6_addr *)inAddr;
		struct sockaddr_in6 * result = (struct sockaddr_in6 *) tptp_malloc(sizeof(struct sockaddr_in6));

		memcpy(&result->sin6_addr, ia, sizeof(struct in6_addr));
		result->sin6_family = family;
		result->sin6_port = htons(0);
		result->sin6_flowinfo = 0; 
		result->sin6_scope_id = 0; 

		*outResult = (struct sockaddr_storage*)result;
	}
}

void initSocketAccept(socket_accept_t * sat) {
	tptp_list_init(&(sat->serverSocketList));
	tptp_list_init(&(sat->socketsToProcess));
	
	tptp_initializeLock(&sat->sockProcLock);

}

typedef struct {
	socket_accept_t * sat;
	SOCKET * serverSocket;

} listenOnSocket_parameters_t;

/** Used by acceptSocketConnections. Parameter is a listenOnSocket_parameters_t pointer. */
THREAD_USER_FUNC_RET_TYPE listenAcceptSocketConnections(LPVOID args) 
{
	SOCKET *serverSock;
	SOCKET clientSock;
	SOCKET *outClientSock;
	socket_accept_t * sat;
	struct sockaddr_storage clientAddress;
	size_t clientLen=sizeof(clientAddress);
	
	int numAcceptAttempts = 0;
	const int TOTAL_ACCEPT_ATTEMPTS = 120;
	
	listenOnSocket_parameters_t *lpt;

	lpt = (listenOnSocket_parameters_t *) args;

	serverSock = lpt->serverSocket;
	sat = lpt->sat;

	if(*serverSock == INVALID_SOCKET) {
		return (THREAD_USER_FUNC_RET_TYPE)(-1);
	}

	while(1) {
		// Listen on the socket that we have been passed
		clientSock = accept(*serverSock, (struct sockaddr * ) & clientAddress, (int *)&clientLen);  
		
		if(clientSock == INVALID_SOCKET) {

			// If the accept attempt returns an invalid socket, note it
			numAcceptAttempts++;
			TPTP_LOG_ERROR_MSG("An invalid socket was returned by an accept() call.") ;
			
			// If we receive an extraordinarily large number of failures, terminate the listening thread 
			if(numAcceptAttempts >= TOTAL_ACCEPT_ATTEMPTS) {
				TPTP_LOG_ERROR_MSG("listenAcceptSocketConnections accept() calls have reached failure threshold, ending server socket listen thread.") ;	
				return (THREAD_USER_FUNC_RET_TYPE) (-1);
			}
			
			SLEEP(500);
			
	   		continue;
		}
		
		numAcceptAttempts = 0;

		// Add the socket to the list of sockets

		tptp_getWriteLock(&(sat->sockProcLock));

		outClientSock = (SOCKET *) tptp_malloc(sizeof(SOCKET));
		*outClientSock = clientSock;
		
		tptp_list_add(&(sat->socketsToProcess), outClientSock);
		
		tptp_releaseWriteLock(&(sat->sockProcLock));
		
	}
	
	return 0;

}


/** 
Spawns threads to accept() on a list of given sockets, and returns the SOCKET from those threads.
*/
SOCKET acceptSocketConnections(SOCKET * sockets, int numSockets, socket_accept_t * sockAcceptState) {
	int i = 0;
	tptp_node_t * node;
	tptp_list_t * socketsMonitored = &(sockAcceptState->serverSocketList);
	SOCKET * s;
	SOCKET result;
	int matchFound = 0;
	TID threadID;
	HANDLE threadHandle;
	int socketsWaiting = 0;

	listenOnSocket_parameters_t *params;

    // Given the list of sockets, scan through and see if we are being asked to listen on any new ones
	for(i = 0; i < numSockets; i++) {
		s = &(sockets[i]);
		matchFound = 0;
		
		// Scan through the list of sockets that we're already monitoring: check if there are any new additions
		node = socketsMonitored->head;
		while(node != NULL) {
			if(node->data == s) {
				matchFound = 1;
				break;
			}
			node = node->next;
		}

		if(!matchFound) {
			tptp_list_add(socketsMonitored, s);
			params = (listenOnSocket_parameters_t*) tptp_malloc(sizeof(listenOnSocket_parameters_t));
			params->sat = sockAcceptState;
			params->serverSocket = s;

			tptpStartThread(listenAcceptSocketConnections, params, &threadID, &threadHandle) ;
		}
	}

	do {
		// Check if there are any client sockets waiting...
		tptp_getReadLock(&(sockAcceptState->sockProcLock));
		
		socketsWaiting = (sockAcceptState->socketsToProcess).count;
		
		tptp_releaseReadLock(&(sockAcceptState->sockProcLock));
		
		if(!socketsWaiting) {
			Sleep(10);
		}
		
	} while(!socketsWaiting); // While (there are no new sockets)

	tptp_getWriteLock(&(sockAcceptState->sockProcLock));

	// Get the value at the top of the list.
	node = sockAcceptState->socketsToProcess.head;

	result = *((SOCKET *)node->data);
	
	s = (SOCKET*)(node->data);
	tptp_list_remove(&(sockAcceptState->socketsToProcess), s); // This calls frees the malloced socket.

	tptp_releaseWriteLock(&(sockAcceptState->sockProcLock));

	return result;

}


/** A (binary) comparison of the contents of two addresses. 
Note, it does not compare the port value, or any of the other sockaddr_in/sockaddr_in6 fields, merely the address field.
Also note, if the family of the two addresses differs, then this function will return FALSE. If you're looking for a way to compare IPv4 and
IPv6 addresses together, try comparing their resolved hostnames. */
int compareAddresses(struct sockaddr_storage * one, struct sockaddr_storage * two) {
	struct sockaddr_in * a4;
	struct sockaddr_in * b4;

	struct sockaddr_in6 *a6;
	struct sockaddr_in6 *b6;
	int x = 0;

	unsigned char a;
	unsigned char b;

	if(one->ss_family != two->ss_family) return 0;

	if(one->ss_family == PF_INET) {
		a4 = (struct sockaddr_in *)one;
		b4 = (struct sockaddr_in *)two;

		if(sizeof(a4->sin_addr) != sizeof(b4->sin_addr)) return 0;

		for(x = 0; x < sizeof(a4->sin_addr); x++) {
			a = ((unsigned char*)&a4->sin_addr)[x];
			b = ((unsigned char*)&b4->sin_addr)[x];

			if(a != b) return 0;
		}

	} else if(one->ss_family == PF_INET6) {
		a6 = (struct sockaddr_in6 *)one;
		b6 = (struct sockaddr_in6 *)two;
		
		if(sizeof(a6->sin6_addr) != sizeof(b6->sin6_addr)) return 0; 
		
		for(x = 0; x < sizeof(a6->sin6_addr); x++) {
			a = ((unsigned char*)&a6->sin6_addr)[x];
			b = ((unsigned char*)&b6->sin6_addr)[x];

			if(a != b) return 0;
			
		}

	} else {
		return 0;
	}
    
	return 1;
}

/** sockaddr_storage -> "www.google.com" */
char * convertAddrToHostname(struct sockaddr_storage *addr) {
	char * hostname;
	int rc;
	hostname = (char *)tptp_malloc(sizeof(char) * 260);
	hostname[0] = 0;

	rc = getnameinfo((struct sockaddr *)addr, sizeof(struct sockaddr_storage), hostname, 260, NULL, 0, 0);
	
	if(rc != 0) {
		tptp_free(hostname);
		return NULL;
	}

	return hostname;

}

int isAddressIPv6RemappedToIPv4(struct sockaddr_storage * addr) {
   	unsigned char *value;
	int family = 0;
	
	int c = 0;

	// Only IPv6 addresses support remapping	
	if(addr->ss_family != PF_INET6) {
		return 0;
	}
	
	getByteFormatFromSockAddr(addr, (void **)&value, &family);
	
	// Only IPv6 addresses support remapping
	if(family != PF_INET6) {
		tptp_free(value);
		return 0;
	}
	
	// The first ten digits should be 0
	for(c = 0; c < 10; c++) {
		if( ((unsigned int)value[c]) != 0) {
			tptp_free(value);
			return 0;			
		}
	}
	
	// The eleventh digit should be 255 
	if( ((unsigned int) value[10])  != 255 ) {
		tptp_free(value);
		return 0;
	}
	
	// The twelfth digit should be 255
	if( ((unsigned int) value[11])  != 255 ) {
		tptp_free(value);
		return 0;
	}
	
	tptp_free(value);

	// The 13th (value[12]), 14th (value[13]), 15th (value[14]) and 16th (value[15]) are then the IPv4 address itself
	
	return 1;
	
}

/** Given an IPv6 address, which is actually an IPv4 remap address (e.g. contains an IPv4 address but is in IPv6 format) 
 * then convert it to its IPv4 equivalent*/
struct sockaddr_storage * convertIPv6RemappedAddresstoIPv4(struct sockaddr_storage * in) {
	unsigned char v4byte[4];
   	unsigned char *value;
	int family = 0;
	struct sockaddr_storage * result;

	if(in->ss_family != PF_INET6) return NULL;

	getByteFormatFromSockAddr(in, (void **)&value, &family);
	
	if(family != PF_INET6) {
		return NULL;
	}
	
	// The 13th, 14th, 15th, and 16th bytes are the IPv4 address itself
	v4byte[0] = (unsigned int) value[12];
	v4byte[1] = (unsigned int) value[13];
	v4byte[2] = (unsigned int) value[14];
	v4byte[3] = (unsigned int) value[15];
	
	getSockAddrFromByteFormat(v4byte, PF_INET, &result);
	
	tptp_free(value);
	
	return result;

}



/** This method uses a number of tricks to determine whether or not the given address is local. */
int isAddrLocal(struct sockaddr_storage * addr) {
	int foundMatch = 0;
	int x = 0;
	struct sockaddr_storage ** addrList;
	int addrListSize = 0;
	struct sockaddr_storage * addr2;
	
	char *hostName;
	char *localHostName = NULL;

	// If the address is an IPv6 address that remaps to an IPv4 address...
	if(isAddressIPv6RemappedToIPv4(addr)) {
	
		// then convert the address to IPv4, and run isAddrLocal with the resulting value
		addr2 = convertIPv6RemappedAddresstoIPv4(addr);
		
		if(addr2 != NULL && addr2->ss_family == PF_INET) {	
			// (only run if the value is valid, we don't want a recursive stack overflow)	
			foundMatch = isAddrLocal(addr2);
		
			tptp_free(addr2);
			return foundMatch;
		}
	} 

	getLocalHostAddresses(&addrList, &addrListSize);
	for(x = 0; x < addrListSize; x++) {

		if(!foundMatch) {
			foundMatch = compareAddresses(addrList[x], addr);

			if(localHostName == NULL) {
				localHostName = convertAddrToHostname(addrList[x]);
			}
		}
	}

	// If we couldn't match using binary comparison, resolve both hostnames and compare those, also try standard localhost addrs
	if(!foundMatch) {

		hostName = convertAddrToHostname(addr);

		if(hostName != NULL && (strcmp(hostName, "::1") == 0 || strcmp(hostName, "127.0.0.1") == 0 || strcmp(hostName, "localhost") == 0)) {
			foundMatch = 1;
		}

		if(localHostName != NULL && hostName != NULL && strcmp(localHostName, hostName) == 0 ) {
			foundMatch = 1;
		}
		tptp_free(hostName);
	}
	
	freeAddressList(addrList, addrListSize);
	tptp_free(localHostName);
	
	return foundMatch;
}



/** Get localhost addresses in sockaddr_storage form. Note that sockaddr_storages received from this method use port
10002, and this will need to change if they are being listened on. */
void getLocalHostAddresses(struct sockaddr_storage *** arrOfAddr, int * numAddr) {
	int RetVal;
	char *Address = NULL;
	char *Port = "10002"; // One needs to specify a port, so I'm giving it this one.
	int i = 0, size = 0, x = 0, y = 0;
	struct sockaddr_storage ** addrs;
	int matchFound = 0;

	char hostname[260];

	struct sockaddr_storage ** resultArr = (struct sockaddr_storage **)tptp_malloc(sizeof(struct sockaddr_storage) * 64);

	struct addrinfo Hints, *AddrInfo = NULL, *AI;

    int Family = PF_UNSPEC; // Give us both the IPv4 and IPv6 addresses
    int SocketType = SOCK_STREAM;

	*numAddr = 0;

	memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
    Hints.ai_flags = AI_NUMERICHOST;

    RetVal = getaddrinfo(Address, Port, &Hints, &AddrInfo);
	
	if(RetVal != 0) return;

	for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next) {
		resultArr[i] = cloneSockAddr((struct sockaddr_storage *)AI->ai_addr);
		
		i++;
	}

	freeaddrinfo(AddrInfo);

	// Grab the user's hostname
	if(gethostname(hostname, 260) == 0) {

		// Resolve it to a list of IP addresses
		convertHostnameToAddrs(hostname, &addrs, &size);

		// For each address...
		for(x = 0; x< size; x++) {
			matchFound = 0;

			// Verify that that address is not already in the list
			for(y = 0; y < i; y++) {
				if(compareAddresses(addrs[x], resultArr[y])) {
					matchFound = 1;
					break;
				}
			}

			// If it's not found, add it to the list
			if(!matchFound) {
				resultArr[i] = addrs[x];
				i++;
			} else {
				// Otherwise, that address is already in the list, so free it
				tptp_free(addrs[x]);
			}
		}
	}

	

	*numAddr = i;
	*arrOfAddr = resultArr;
}


/** Given a range of bytes, print their int value. */
void printChars(unsigned char *b, int l) {
	int x = 0;

	for(x = 0; x < l; x++) {
		printf("[%d]", (*b));
		b++;
	}
}

/** For debug purposes, prints the values of the fields of sockaddr_storage.*/
void printSockAddrStorage(struct sockaddr_storage *inAddr) {

	if(inAddr->ss_family == PF_INET) {
		struct sockaddr_in * ia = (struct sockaddr_in *)inAddr;
		
		printf("addr: "); 
		printChars((unsigned char*)&(ia->sin_addr), 4);
		printf("\r\n");

		printf("family: %d\r\n", ia->sin_family);
		printf("port: %d\r\n", ia->sin_port);
		printf("zero: %s\r\n", ia->sin_zero);
		

	} else { // PF_INET6

		struct sockaddr_in6 * ia = (struct sockaddr_in6 *)inAddr;

		printf("addr: "); 
		printChars((unsigned char*)&(ia->sin6_addr), 16);
		printf("\r\n");

		printf("family: %d\r\n", (int)ia->sin6_family);
		printf("flowinfo: %d\r\n", (int)ia->sin6_flowinfo);
		printf("port: %d\r\n", (int)ia->sin6_port);
		printf("scope_id: %d\r\n", (int)ia->sin6_scope_id);
		
	}

}
