/* NL-sock.c

    Copyright (C) 2007 Lutz Mueller

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "newlisp.h"
#include <string.h>

#ifdef WIN_32
#include <winsock2.h>
#include <ws2tcpip.h>
#define fdopen win32_fdopen
#define SHUT_RDWR 2
#else
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif

#ifdef SOLARIS
#include <stropts.h>
#include <sys/conf.h>
#include <netinet/in_systm.h>
#ifndef TRU64
#define FIONREAD I_NREAD
#endif
#endif

#ifdef OS2 
#define socklen_t int 
#define SHUT_RDWR 2 
#endif 

#ifndef INADDR_NONE
#define INADDR_NONE (unsigned) -1
#endif

#ifndef AF_UNIX
#define AF_UNIX PF_UNIX
#endif

#ifndef SUN_LEN
#define SUN_LEN(su) \
    (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif

#include "protos.h"

#define MAX_PENDING_CONNECTS 128
#define NO_FLAGS_SET 0

#ifdef WIN_32
#define socklen_t int
#define close closesocket
#else
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif


#define ERR_INET_OPEN_SOCKET 1
#define ERR_INET_HOST_UNKNOWN 2
#define ERR_INET_INVALID_SERVICE 3
#define ERR_INET_CONNECT_FAILED 4
#define ERR_INET_ACCEPT 5
#define ERR_INET_CONNECTION_DROPPED 6
#define ERR_INET_CONNECTION_BROKEN 7
#define ERR_INET_READ 8
#define ERR_INET_WRITE 9
#define ERR_INET_CANNOT_BIND 10
#define ERR_INET_TOO_MUCH_SOCKETS 11
#define ERR_INET_LISTEN_FAILED 12
#define ERR_INET_BAD_FORMED_IP 13
#define ERR_INET_SELECT_FAILED 14
#define ERR_INET_PEEK_FAILED 15
#define ERR_INET_NOT_VALID_SOCKET 16
#define ERR_INET_TIMEOUT 17
#define MAX_NET_ERROR 17

#define isnum(A) ((A)>= '0' && (A) <= '9')

typedef struct
    {
    int socket;
	int family;
    void * next;
    } INET_SESSION;

INET_SESSION * netSessions = NULL;

int deleteInetSession(int sock);
int getSocketFamily(int sock);

#define READY_READ 0
#define READY_WRITE 1

int errorIdx = 0;

extern int logTraffic;
extern int noPromptMode;

#ifdef WIN_32
struct timezone {
       int     tz_minuteswest;
       int     tz_dsttime;
};
int gettimeofday(struct timeval * tv, struct timezone * tz);
#endif


/********************** session functions *******************/

int createInetSession(int sock, int family)
{
INET_SESSION * iSession;

iSession = (INET_SESSION *)malloc(sizeof(INET_SESSION));

iSession->socket = sock;
iSession->family = family;

if(netSessions == NULL)
    {
    netSessions = iSession;
    iSession->next = NULL;
    }
else
    {
    iSession->next = netSessions;
    netSessions = iSession;
    }
return(TRUE);
}

int deleteInetSession(int sock)
{
INET_SESSION * session;
INET_SESSION * previous;

if(netSessions == NULL)
    return(0);
else
    session = previous = netSessions;

while(session)
    {
    if(session->socket == sock)
        {
        if(session == netSessions)
            netSessions = session->next;
        else
            previous->next = session->next;
        free((char *)session);
        return(TRUE);
        }
    previous = session;
    session = session->next;
    }

return(FALSE);
}


int getSocketFamily(int sock)
{
INET_SESSION * session;

session = netSessions;

while(session)
    {
	if(session->socket == sock)
		return(session->family);
    session = session->next;
    }

return(-1);
}

/********************* user functions **************************/


CELL * p_netClose(CELL * params) 
{
UINT sock; 
 
getInteger(params, &sock); 
deleteInetSession((int)sock);

if(!getFlag(params->next))
	shutdown(sock, SHUT_RDWR);

if(close((int)sock) == SOCKET_ERROR)
    return(netError(ERR_INET_NOT_VALID_SOCKET));

errorIdx = 0;
return(trueCell);
}


CELL * p_netSessions(CELL * params)
{
INET_SESSION * session;
INET_SESSION * sPtr;
CELL * sList;
CELL * last;

session = netSessions;
sList = getCell(CELL_EXPRESSION);
last = NULL;

while(session)
    {
    sPtr = session;
    session = session->next;
    if(last == NULL)
        {
        last = stuffInteger(sPtr->socket);
        sList->contents = (UINT)last;
        }
    else
        {
        last->next = stuffInteger(sPtr->socket);
        last = last->next;
        }
    }

return(sList);
}



/*********************************************************************/

CELL * p_netService(CELL * params) 
{
struct servent * pSe; 
char * service; 
char * protocol; 
int port; 
 
params = getString(params, &service); 
getString(params, &protocol); 
 
if((pSe = getservbyname(service, protocol)) == NULL) 
    return(netError(ERR_INET_INVALID_SERVICE)); 
 
port = (int)ntohs(pSe->s_port); 
 
errorIdx = 0; 
return(stuffInteger((UINT)port)); 
} 


CELL * p_netConnect(CELL * params)
{
UINT ttl = 3;
char * remoteHostName; 
int type; 
UINT portNo;
char * protocol = NULL;
int sock;

params = getString(params, &remoteHostName); 
#ifndef WIN_32
if(params == nilCell)
	{
	if((sock = netConnectLocal(remoteHostName)) == SOCKET_ERROR)
		return(netError(errorIdx));
	else	
		return(stuffInteger((UINT)sock));
	}
#endif

params = getInteger(params, &portNo);

type = SOCK_STREAM;
if(params != nilCell)
    {
    params = getString(params, &protocol);
    *protocol = toupper(*protocol);
    type = SOCK_DGRAM;
    if(*protocol == 'M')
        {
        if(params != nilCell)
            getInteger(params, &ttl);
        }
    }

if((sock = netConnect(remoteHostName, (int)portNo, type, protocol, (int)ttl)) == SOCKET_ERROR)
    return(netError(errorIdx));

createInetSession(sock, AF_INET);

errorIdx = 0;
return(stuffInteger((UINT)sock)); 
}


#ifndef WIN_32
/* create local domain UNIX socket */
int netConnectLocal(char * path)
{
int sock;
struct sockaddr_un remote_sun;

if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == SOCKET_ERROR)
	{
	errorIdx = ERR_INET_OPEN_SOCKET;
	return(SOCKET_ERROR);
	}
	
remote_sun.sun_family = AF_UNIX;
strncpy(remote_sun.sun_path, path, sizeof(remote_sun.sun_path) - 1);
remote_sun.sun_path[sizeof (remote_sun.sun_path) - 1] = '\0';
if (connect(sock, (struct sockaddr *)&remote_sun, SUN_LEN(&remote_sun)) == -1) 
	{
	close(sock);
	errorIdx = ERR_INET_CONNECT_FAILED;
	return(SOCKET_ERROR);
    }

createInetSession(sock, AF_UNIX);

errorIdx = 0;
return(sock); 
}
#endif




/* create internet socket */
int netConnect(char * remoteHostName, int portNo, int type, char * prot, int ttl)
{
struct sockaddr_in dest_sin;
struct in_addr iaddr;
struct hostent * pHe;
int sock, idx;
/* char opt; */
int opt;

/* create socket */
if((sock = socket(AF_INET, type, 0)) == INVALID_SOCKET)
    {
    errorIdx = ERR_INET_OPEN_SOCKET;
    return(SOCKET_ERROR);
    }

if(prot != NULL) if(*prot == 'M' || *prot == 'B')
    {
    memset(&iaddr, 0, sizeof(iaddr));
    iaddr.s_addr = INADDR_ANY;

    if(*prot == 'M')
        {
        setsockopt(sock, 0, IP_MULTICAST_IF, (const void *)&iaddr, sizeof(iaddr));
        opt = ttl;
        setsockopt(sock, 0, IP_MULTICAST_TTL, (const void *)&opt, sizeof(opt));
        }

    if(*prot == 'B')
        {
        opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const void *)&opt, sizeof(opt));
        }
    }

if((pHe = gethostbyname(remoteHostName)) == NULL)
    {
    errorIdx = ERR_INET_HOST_UNKNOWN;
    return(SOCKET_ERROR);
    }

for(idx = 0; ; idx++)
    {
    memcpy((char *)&(dest_sin.sin_addr),
        pHe->h_addr_list[idx], pHe->h_length);

    dest_sin.sin_port = htons((u_short)portNo);
    dest_sin.sin_family = AF_INET;
	memset(&(dest_sin.sin_zero), '\0', sizeof(dest_sin.sin_zero));

    if(connect(sock,(struct sockaddr *)&dest_sin, sizeof(dest_sin)) == 0)
        break;

    if(pHe->h_addr_list[idx+1] != NULL)
        continue;

    close(sock);
    errorIdx = ERR_INET_CONNECT_FAILED;
    return(SOCKET_ERROR);
    }

errorIdx = 0;
return(sock);
}

/********* should be called after listen/accept notification **********/

CELL * p_netAccept(CELL * params) 
{
int sock;
UINT listenSock;

getInteger(params, &listenSock); 

if((sock = netAccept((int)listenSock)) == INVALID_SOCKET)
    return(netError(ERR_INET_ACCEPT));

return(stuffInteger(sock)); 
}


int netAccept(int listenSock)
{
int sock, family;
struct sockaddr_in dest_sin;
#ifndef WIN_32
struct sockaddr_un dest_sun;
#endif
socklen_t dest_slen;

family = getSocketFamily(listenSock);

#ifndef WIN_32
if(family == AF_UNIX)
	{
	dest_slen = sizeof(struct sockaddr_un);
	sock = accept(listenSock, (struct sockaddr *) &dest_sun,  &dest_slen);
	}
else
#endif
	{
	dest_slen = sizeof(struct sockaddr_in);
	sock = accept(listenSock, (struct sockaddr *) &dest_sin, (void *)&dest_slen);
	}

if(sock != INVALID_SOCKET) 
    {
    createInetSession(sock, family);
    errorIdx = 0;
    }

return(sock);
}


/******************* returns remote IP and port number *************/

#define LOCAL_INFO 0
#define PEER_INFO 1


int getPeerName(int sock, int peerLocalFlag, char * IPaddress)
{
socklen_t address_sin_len; 
struct sockaddr_in address_sin; 

if(getSocketFamily(sock) == AF_UNIX)
	{
	snprintf(IPaddress, 6, "local");
	return(0);
	}

*IPaddress = 0;
address_sin_len = sizeof(address_sin); 
if(peerLocalFlag == LOCAL_INFO) 
    { 
    if(getsockname(sock,  
        (struct sockaddr *)&address_sin, (void *)&address_sin_len) 
            == SOCKET_ERROR) 
        return(0); 
    } 
else 
    { 
    if(getpeername(sock,  
        (struct sockaddr *)&address_sin, (void *)&address_sin_len) 
            == SOCKET_ERROR) 
        return(0); 
    } 
 
/* return address IP number  */
snprintf(IPaddress, 16, inet_ntoa(address_sin.sin_addr)); 

return(ntohs(address_sin.sin_port));
}


CELL * p_netLocal(CELL * params) 
{
return(netPeerLocal(params, LOCAL_INFO));
}

CELL * p_netPeer(CELL * params)
{
return(netPeerLocal(params, PEER_INFO));
}


CELL * netPeerLocal(CELL * params, int peerLocalFlag) 
{ 
CELL * result; 
CELL * cell; 
char name[16];
UINT addressPort, sock;

getInteger(params, &sock); 
if((addressPort = getPeerName((int)sock, peerLocalFlag, name)) == SOCKET_ERROR)
    return(nilCell);
 
result = getCell(CELL_EXPRESSION); 
result->contents = (UINT)stuffString(name); 
cell = (CELL *)result->contents; 
cell->next = stuffInteger((UINT)addressPort); 
 
errorIdx = 0; 
return(result); 
} 
 

CELL * p_netLookup(CELL * params)
{
union ipSpec 
    {
    unsigned int no;
    unsigned char chr[4];
    } ip;

struct sockaddr_in address;
struct hostent * pHe;
char * hostString;
char IPaddress[16];
int forceByName = 0;

params = getString(params, &hostString);
forceByName = getFlag(params);

/* get hostname from ip-number */
if(isDigit((unsigned char)*hostString) && !forceByName)
    {
    if((ip.no = inet_addr(hostString)) == INADDR_NONE)
        return(netError(ERR_INET_BAD_FORMED_IP));

    if((pHe = gethostbyaddr((char *) &ip.no,4,PF_INET)) == NULL)
        return(nilCell);

    return(stuffString((char *)pHe->h_name));
    }

/* get ip-number from hostname */
if((pHe = gethostbyname(hostString)) == NULL)
    return(nilCell);

memcpy((char *)&(address.sin_addr), pHe->h_addr_list[0], pHe->h_length);
snprintf(IPaddress, 16, inet_ntoa(address.sin_addr));

errorIdx = 0;
return(stuffString(IPaddress));
}

CELL * netReceive(int sock, SYMBOL * readSymbol, size_t readSize, CELL * params);

CELL * p_netReceive(CELL * params) 
{ 
UINT sock;
SYMBOL * readSymbol;
size_t readSize;

params = getInteger(params, &sock);
params = getSymbol(params, &readSymbol);
params = getInteger(params, (UINT *)&readSize);

return(netReceive((int)sock, readSymbol, readSize, params));
}

CELL * netReceive(int sock, SYMBOL * readSymbol, size_t readSize, CELL * params)
{
char * waitFor;
ssize_t bytesReceived;
size_t length;
int found;
STREAM netStream;
char chr;
CELL * cell;

if(isProtected(readSymbol->flags))
    return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(readSymbol)));

memset(&netStream, 0, sizeof(netStream));

if(params == nilCell)
    {
    openStrStream(&netStream, readSize, 0);
    found = 1;
    bytesReceived  = recv(sock, netStream.buffer, readSize, NO_FLAGS_SET);
    }
else
    {
    getString(params, &waitFor);
    openStrStream(&netStream, MAX_LINE, 0);
    found = bytesReceived = 0;
    length = strlen(waitFor);
    while(bytesReceived < (int)readSize)
        {
        if(recv(sock, &chr, 1, NO_FLAGS_SET) <= 0)
            {
            bytesReceived = 0;
            break;
            }
        writeStreamChar(&netStream, chr); 
         if(++bytesReceived < length) continue;
        if(strncmp(waitFor,  netStream.ptr - length, length) == 0)
             {
            found = 1;
            break;
            }        
        }
            
    }

if(bytesReceived == 0 || found == 0) 
    { 
    closeStrStream(&netStream); 
    deleteInetSession(sock); 
    close(sock); 
    return(netError(ERR_INET_CONNECTION_DROPPED)); 
    } 

if(bytesReceived == SOCKET_ERROR) 
    { 
    closeStrStream(&netStream);         
    deleteInetSession(sock); 
    close(sock); 
    return(netError(ERR_INET_READ)); 
    } 
  
cell = stuffStringN(netStream.buffer, bytesReceived);
closeStrStream(&netStream);

deleteList((CELL *)readSymbol->contents); 
readSymbol->contents = (UINT)cell; 
 
errorIdx = 0; 
return(stuffInteger(bytesReceived)); 
} 



CELL * netReceiveFrom(int sock, size_t readSize, int closeFlag)
{
int portNo;
char * buffer;
ssize_t bytesReceived;
struct sockaddr_in remote_sin;
CELL * cell;
CELL * result;
#ifdef WIN_32
int remote_sin_len;
#else
#ifdef TRU64
unsigned long remote_sin_len;
#else
socklen_t remote_sin_len;
#endif
#endif
char IPaddress[16];

buffer = (char *)allocMemory(readSize + 1);
remote_sin_len = sizeof(remote_sin);
memset(&remote_sin, 0, sizeof(remote_sin));

bytesReceived = recvfrom(sock, buffer, readSize, 0, 
    (struct sockaddr *)&remote_sin, &remote_sin_len);

if(bytesReceived == SOCKET_ERROR) 
    {
    freeMemory(buffer);
    close(sock); 
    return(netError(ERR_INET_READ)); 
    }

snprintf(IPaddress, 16, inet_ntoa(remote_sin.sin_addr)); 
portNo = ntohs(remote_sin.sin_port);


cell = result = getCell(CELL_EXPRESSION);
cell->contents = (UINT)stuffStringN(buffer, bytesReceived);
cell = (CELL *)cell->contents;
cell->next = stuffString(IPaddress);
((CELL*)cell->next)->next = stuffInteger(portNo);

freeMemory(buffer);

if(closeFlag) close(sock);

errorIdx = 0;
return(result);
}


CELL * p_netReceiveUDP(CELL * params) 
{ 
UINT portNo;
int sock;
size_t readSize;
INT64 wait = 0;
INT64 elapsed;
char * ifaddr = NULL;

params = getInteger(params, &portNo);
params = getInteger(params, (UINT *)&readSize);
if(params != nilCell)
    {
    params = getInteger64(params, &wait);
    if(params != nilCell)
        getString(params, &ifaddr);
    }

if((sock = netListenOrDatagram((int)portNo, SOCK_DGRAM, ifaddr, NULL)) == SOCKET_ERROR)
    return(nilCell);

/* if timeout parameter given wait for socket to be readable */
if(wait > 0)
        {
        if((elapsed = wait_ready(sock, wait, READY_READ)) <= 0)
                {
                close(sock);
				if(elapsed == 0) return(netError(ERR_INET_TIMEOUT));
				else netError(ERR_INET_SELECT_FAILED);
                }
        }

return(netReceiveFrom(sock, readSize, TRUE));
}


CELL * p_netReceiveFrom(CELL * params)
{
UINT sock;
size_t readSize;

params = getInteger(params, &sock);
getInteger(params, (UINT*)&readSize);

return(netReceiveFrom((int)sock, readSize, FALSE));
}


/**********************************************************************/

CELL * p_netSend(CELL * params) 
{
UINT sock; 
size_t size; 
CELL * cell; 
SYMBOL * writeSptr; 
char * buffer; 
ssize_t bytesSent; 

params = getInteger(params, &sock); 
cell = evaluateExpression(params);
if(cell->type == CELL_SYMBOL)
    {
    writeSptr = (SYMBOL *)cell->contents;
    cell = (CELL *)writeSptr->contents;
    }
else if(cell->type == CELL_DYN_SYMBOL)
    {
    writeSptr = getDynamicSymbol(cell);
    cell = (CELL *)writeSptr->contents;
    }

if(cell->type != CELL_STRING) 
    return(errorProcExt(ERR_STRING_EXPECTED, params)); 

params = params->next; 
if(params->type == CELL_NIL) 
    size = cell->aux - 1; 
else        
    getInteger(params, (UINT *)&size); 
 
buffer = (char *)cell->contents; 
if(size > (cell->aux - 1)) size = cell->aux - 1;  

if((bytesSent = send((int)sock, buffer, size, NO_FLAGS_SET))  == SOCKET_ERROR) 
    { 
    deleteInetSession((int)sock); 
    close((int)sock); 
    return(netError(ERR_INET_WRITE)); 
    }

errorIdx = 0; 
return(stuffInteger(bytesSent)); 
}

#define SEND_TO_UDP 0
#define SEND_TO_SOCK 1

CELL * netSendTo(CELL * params, int type)
{
char * remoteHost;
UINT remotePort;
struct sockaddr_in dest_sin;
struct hostent * pHe;
size_t size;
char * buffer;
ssize_t bytesSent;
UINT sock;
/* char one = 1; */
int one = 1;

params = getString(params, &remoteHost);
params = getInteger(params, &remotePort);
params = getStringSize(params, &buffer, &size, TRUE);

if((pHe = gethostbyname(remoteHost)) == NULL)
        return(netError(ERR_INET_HOST_UNKNOWN));

if(type == SEND_TO_UDP) /* for 'net-send-udp' */
	{
    if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
        return(netError(ERR_INET_OPEN_SOCKET));

    if(getFlag(params))
		setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const void *)&one, sizeof(one));
	}
else /* SEND_TO_SOCK , socket may or may not be UDP, for 'net-send-to' */
    {
    params = getInteger(params, &sock);
    }

memcpy((char *)&(dest_sin.sin_addr), pHe->h_addr_list[0], pHe->h_length);
dest_sin.sin_port = htons((u_short)remotePort);
dest_sin.sin_family = AF_INET;
memset(&(dest_sin.sin_zero), '\0', 8);

bytesSent = sendto((int)sock, buffer, size, NO_FLAGS_SET,
        (struct sockaddr *)&dest_sin, sizeof(dest_sin));

if(type == SEND_TO_UDP) close((int)sock);

if(bytesSent == SOCKET_ERROR)
        return(netError(ERR_INET_WRITE));

errorIdx = 0;
return(stuffInteger(bytesSent));
}


CELL * p_netSendUDP(CELL * params)
{
return(netSendTo(params, SEND_TO_UDP));
}


CELL * p_netSendTo(CELL * params)
{
return(netSendTo(params, SEND_TO_SOCK));
}


/************************* listen **************************************/

CELL * p_netListen(CELL * params) 
{ 
UINT portNo;
char * ifAddr = NULL;
char * option = NULL;
char * mcAddr = NULL;
int sock, type; 
CELL * cell;
 
type = SOCK_STREAM;
cell = evaluateExpression(params);
params = params->next;

#ifndef WIN_32
if(cell->type == CELL_STRING)
	{
	if((sock = netListenLocal((char *)cell->contents)) == SOCKET_ERROR)
		return(netError(errorIdx));
	else
		return(stuffInteger((UINT)sock));
	}
#endif

getIntegerExt(cell, &portNo, FALSE); 

if(params != nilCell)
    {
    params = getString(params, &ifAddr);
    if(*ifAddr == 0) ifAddr = NULL;
    if(params != nilCell)
        {
        params = getString(params, &option);
        if(*option == 'u' || *option == 'U')
            type = SOCK_DGRAM;
        else if(*option == 'm' || *option == 'M')
            {
            type = SOCK_DGRAM;
            mcAddr = ifAddr;
            ifAddr = NULL;
            }
        }
    }
    

if((sock = netListenOrDatagram((int)portNo, type, ifAddr, mcAddr)) == SOCKET_ERROR)
    return(nilCell);

return(stuffInteger(sock));
} 

#ifndef WIN_32
int netListenLocal(char * name)
{
int sock;
struct sockaddr_un local_sun;

sock  = socket(AF_UNIX, SOCK_STREAM, 0);
local_sun.sun_family = AF_UNIX;
strncpy(local_sun.sun_path, name, sizeof(local_sun.sun_path) - 1);
local_sun.sun_path[sizeof (local_sun.sun_path) - 1] = '\0';
unlink(local_sun.sun_path);

#ifdef OS2
if(bind(sock, (struct sockaddr *)&local_sun, sizeof(struct sockaddr_un)) == -1)
#else
if(bind(sock, (struct sockaddr *)&local_sun, SUN_LEN(&local_sun)) != 0)
#endif
	{
	close(sock);
	errorIdx = ERR_INET_CANNOT_BIND;
	return(SOCKET_ERROR);
	}

if(listen(sock, MAX_PENDING_CONNECTS) == SOCKET_ERROR)
	{
	close(sock);
	errorIdx = ERR_INET_LISTEN_FAILED;
	return(SOCKET_ERROR);
	}

createInetSession(sock, AF_UNIX);

errorIdx = 0;
return(sock);
}
#endif


int netListenOrDatagram(int portNo, int type, char * ifAddr, char * mcAddr)
{
int sock; 
int one = 1;
/* char one = 1; */
struct sockaddr_in local_sin; 
struct hostent * pHe;
struct ip_mreq mcast;

if((sock = socket(AF_INET, type, 0)) == INVALID_SOCKET)
    {
    errorIdx = ERR_INET_OPEN_SOCKET; 
    return SOCKET_ERROR;
    }

memset(&local_sin, 0, sizeof(local_sin));

if(ifAddr != NULL && *ifAddr != 0)
	{
	if((pHe = gethostbyname(ifAddr)) == NULL)
		{
		errorIdx = ERR_INET_HOST_UNKNOWN;
		return(SOCKET_ERROR);
		}
	memcpy((char *)&(local_sin.sin_addr), pHe->h_addr_list[0], pHe->h_length);
	}
else 
	local_sin.sin_addr.s_addr = INADDR_ANY; 

local_sin.sin_port = htons((u_short)portNo); 
local_sin.sin_family = AF_INET; 

setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void*)&one, sizeof(one));
 
if(bind(sock, (struct sockaddr *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) 
    { 
    close(sock);
    errorIdx = ERR_INET_CANNOT_BIND; 
    return(SOCKET_ERROR);
    } 

if(mcAddr != NULL)
    {
    memset(&mcast, 0, sizeof(mcast));
    mcast.imr_multiaddr.s_addr = inet_addr(mcAddr);
    mcast.imr_interface.s_addr = INADDR_ANY;    
    setsockopt(sock, 0, IP_ADD_MEMBERSHIP, (const void *)&mcast, sizeof(mcast));
    }

if(type == SOCK_STREAM)
	{
	if(listen(sock, MAX_PENDING_CONNECTS) == SOCKET_ERROR)  
		{ 
		close(sock); 
		errorIdx = ERR_INET_LISTEN_FAILED;
		return(SOCKET_ERROR);
		} 
	}

createInetSession(sock, AF_INET); 

errorIdx = 0;
return(sock);
}


/* returns number of bytes ready to read */
CELL * p_netPeek(CELL * params)
{
UINT sock;
#ifdef WIN_32
unsigned long result;
#else
int result;
#endif

getInteger(params, &sock);

if(ioctl((int)sock, FIONREAD, &result) == SOCKET_ERROR)
    return(netError(ERR_INET_PEEK_FAILED));

errorIdx = 0;
return(stuffInteger((UINT)result));
} 


typedef struct
    {
    int sock;
    void * next;
    } SOCKLIST;


/* checks a socket for readability/writeability  */
CELL * p_netSelect(CELL * params)
{
long value;
INT64 wait;
char * mode;
struct timeval timeOut;
fd_set socketSet;
SOCKLIST * sockPtr;
SOCKLIST * sockList = NULL;
CELL * cell;
CELL * list = NULL;
struct timeval* tmvPtr;

errorIdx = 0;

FD_ZERO(&socketSet);

cell = evaluateExpression(params);
if(isNumber(cell->type))
    getIntegerExt(cell, (UINT*)&value, FALSE);
else if(isList(cell->type))
    {
    cell = (CELL*)cell->contents;
    if(cell == nilCell) return(getCell(CELL_EXPRESSION));
    sockList = sockPtr = allocMemory(sizeof(SOCKLIST));
    sockPtr->sock = cell->contents;
    sockPtr->next = NULL;
    FD_SET(sockPtr->sock, &socketSet);
    value = 1;
    while((cell = cell->next) != nilCell)
        {
        sockPtr->next = allocMemory(sizeof(SOCKLIST));
        sockPtr = sockPtr->next;
        sockPtr->sock = cell->contents;        
        sockPtr->next = NULL;
        if(value == FD_SETSIZE)
            return(netError(ERR_INET_TOO_MUCH_SOCKETS));
        else value++;
        FD_SET(sockPtr->sock, &socketSet);
        }
    }
else return(errorProcExt(ERR_LIST_OR_NUMBER_EXPECTED, params));

params = getString(params->next, &mode);
getInteger64(params, &wait);

tmvPtr = (wait == -1) ? NULL : &timeOut;
timeOut.tv_sec = wait/1000000;
timeOut.tv_usec = wait - timeOut.tv_sec * 1000000;

if(sockList == NULL)
    {
    FD_SET((int)value, &socketSet);
    value = 1;
    }

/* printf("%d %d %d\n", timeOut.tv_sec, timeOut.tv_usec, sizeof(timeOut.tv_sec));  */

if(*mode == 'r')
    value = select(FD_SETSIZE, &socketSet, NULL, NULL, tmvPtr);
else if(*mode == 'w')
    value = select(FD_SETSIZE, NULL, &socketSet, NULL, tmvPtr);
else if(*mode == 'e')
    value = select(FD_SETSIZE, NULL, NULL, &socketSet, tmvPtr);

if(value >= 0)
    {
    if((sockPtr = sockList) == NULL)
        {
        if(value == 0) return(nilCell);
        else return(trueCell);
        }    

    cell = getCell(CELL_EXPRESSION);
    while(sockPtr != NULL)
        {
        if(FD_ISSET(sockPtr->sock, &socketSet))
            {
            if(list == NULL)
                {
                list = cell;
                cell->contents = (UINT)stuffInteger(sockPtr->sock);
                cell = (CELL *)cell->contents;
                }
            else
                {
                cell->next = stuffInteger(sockPtr->sock);
                cell = cell->next;
                }
            }
        sockPtr = sockPtr->next;
        free(sockList);
        sockList = sockPtr;
        }

    if(list == NULL) return(cell);
    else return(list);
    }

netError(ERR_INET_SELECT_FAILED);

if(sockList == NULL) return(nilCell);
return(getCell(CELL_EXPRESSION));
}

extern char logFile[];

void writeLog(char * text, int newLine)
{
int handle;

#ifdef WIN_32
handle = open(logFile, O_RDWR | O_APPEND | O_BINARY | O_CREAT, S_IREAD | S_IWRITE);
#else
handle = open(logFile, O_RDWR | O_APPEND | O_BINARY | O_CREAT,
          S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); /* rw-rw-rw */
#endif

write(handle, text, strlen(text));
if(newLine) write(handle, &LINE_FEED, strlen(LINE_FEED));
close(handle);
}


FILE * serverFD(int port, char * domain, int reconnect)
{
static int sock, connection;
char name[18];
char text[80];
time_t t;

if(!reconnect)
	{
#ifndef WIN_32
	if(port != 0)
		sock = netListenOrDatagram(port, SOCK_STREAM, NULL, NULL);
	else
		sock = netListenLocal(domain);
#else
	sock = netListenOrDatagram(port, SOCK_STREAM, NULL, NULL);
#endif

	if(sock == SOCKET_ERROR)
		{
		snprintf(text, 78, "newLISP v.%d listening on %s", version, domain);
		writeLog(text, TRUE);
		return(NULL);
		}
	}
else
	{
	deleteInetSession(connection);
	close(connection); 
	}

if((connection = netAccept(sock)) == SOCKET_ERROR)
	return NULL;

createInetSession(connection, (port != 0) ? AF_INET : AF_UNIX);

/* print log */
getPeerName(connection, PEER_INFO, name);
t = time(NULL);
snprintf(text, 78, "Connected to %s on %s", name, ctime(&t));
/* printf(text); */
writeLog(text, 0);

return(fdopen(connection, "r+")); 
}

/******************************* distributed computing ***********************/

#define MAX_BUFF 1024
CELL * netEvalError(int errNo);

typedef struct
	{
	char * host;
	int port;
	int sock;
	int timeOut;
	STREAM * netStream;
	CELL * result;
	void * next;
	} NETEVAL;

void freeSessions(NETEVAL * base);

CELL * p_netEval(CELL * params)
{
CELL * cell = NULL;
CELL * list = NULL;
NETEVAL * session = NULL;
NETEVAL * base = NULL;
char * host;
char * prog;
UINT port;
int ready;
int sock;
size_t size, count = 0;
ssize_t bytes;
long timeOut = MAX_LONG;
int start, elapsed = 0;
CELL * result;
STREAM * netStream;
CELL * netEvalIdle = NULL;
char buffer[MAX_BUFF];
STREAM evalStream;
int rawMode = FALSE;
int singleSession = FALSE;
jmp_buf errorJumpSave;
int errNo;
int resultStackIdxSave;

list  = evaluateExpression(params);
if(list->type == CELL_STRING)
	{
	host = (char *)list->contents;
	params = getIntegerExt(params->next, &port, TRUE);
	params = getStringSize(params, &prog, &size, TRUE);
	list = nilCell;
	singleSession = TRUE;
	goto SINGLE_SESSION;
	}
else if(list->type == CELL_EXPRESSION)
	{
	list = (CELL*)list->contents;
	params = params->next;
	}
else return(errorProcExt(ERR_LIST_OR_STRING_EXPECTED, params));

CREATE_SESSION:
if(!isList(list->type))
    return(errorProcExt(ERR_LIST_EXPECTED, list));
cell = (CELL *)list->contents;

/* get node parameters, since 8.9.8 evaluated */
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
if((errNo = setjmp(errorJump)) != 0)
	{
	memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
	freeSessions(base);
	longjmp(errorJump, errNo);
	}
cell = getStringSize(cell, &host, &size, TRUE);
cell = getInteger(cell, &port);
cell = getStringSize(cell, &prog, &size, TRUE);
rawMode = getFlag(cell);

memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));

SINGLE_SESSION:
if(base == NULL)
    {
    base = session = allocMemory(sizeof(NETEVAL));
    memset(base, 0, sizeof(NETEVAL));
    }
else
    {
    session->next = allocMemory(sizeof(NETEVAL));
    session = session->next;
    memset(session, 0, sizeof(NETEVAL));
    }

#ifndef WIN_32
if(port != 0)
	sock = netConnect(host, (int)port, SOCK_STREAM, NULL, 3);
else
	sock = netConnectLocal(host);
#else
sock = netConnect(host, (int)port, SOCK_STREAM, NULL, 3);
#endif

if(sock == SOCKET_ERROR)
		{
		session->result = netEvalError(errorIdx);
		goto CONTINUE_CREATE_SESSION;
		}
	
session->host = host;
session->port = port;
session->sock = sock;

if( sendall(sock, "[cmd]\n", 6)  == SOCKET_ERROR ||
    sendall(sock, prog, size) == SOCKET_ERROR ||
    sendall(sock, "(exit)\n[/cmd]\n", 14) == SOCKET_ERROR )
    { 
    close(sock); 
    session->result = netEvalError(ERR_INET_WRITE); 
    goto CONTINUE_CREATE_SESSION;
    }

session->netStream = (void *)allocMemory(sizeof(STREAM));
memset(session->netStream, 0, sizeof(STREAM));
openStrStream(session->netStream, MAX_BUFF, 0);
/* prepend quote for evaluation */
writeStreamChar(session->netStream, '\'');
createInetSession(sock, AF_INET);
count++;
CONTINUE_CREATE_SESSION:
list = list->next;
if(list != nilCell) goto CREATE_SESSION;

/* get timeout and optional handler symbol */
session = base;
if(params != nilCell)
	params = getInteger(params, (UINT *)&timeOut);
if(params != nilCell)
	netEvalIdle = params;
   
/* printf("timeout %d idle-loop %X\n", timeOut, netEvalIdle); */
 
/* collect data from host in each active session */
while(count)
    {
    resultStackIdxSave = resultStackIdx;
    if( (netStream = session->netStream) == NULL) 
        {
        session = session->next;
        if(session == NULL) session = base;
        continue;
        }
        
    start = milliSecTime();

    if(netEvalIdle) 
        {
        cell = getCell(CELL_EXPRESSION);
        cell->contents = (UINT)copyCell(netEvalIdle);
        pushResult(cell);
        if(!evaluateExpressionSafe(cell, &errNo))
			{
			freeSessions(base);
			longjmp(errorJump, errNo);
			}
        }

    bytes = -1;
    ready = wait_ready(session->sock, 100, READY_READ);
    if(ready > 0)
        {
        memset(buffer, 0, MAX_BUFF);
        bytes = recv(session->sock, buffer, MAX_BUFF, NO_FLAGS_SET);
        if(bytes) writeStreamStr(netStream, buffer, bytes);
        }
    if(ready < 0 || bytes == 0 || elapsed >= timeOut)
        {
/*        printf("count=%d ready=%d bytes=%d elapsed=%d\n", count, ready, bytes, elapsed); */
        if(elapsed >= timeOut) result = copyCell(nilCell); 
        else if(rawMode) /* get raw buffer without the quote */
            result = stuffStringN(netStream->buffer + 1, netStream->position - 1);
        else 
            {
            makeStreamFromString(&evalStream, netStream->buffer);
            memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
            if((errNo = setjmp(errorJump)) != 0)
                {
                memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
                freeSessions(base);
                longjmp(errorJump, errNo);
                }
            result = evaluateStream(&evalStream, 0, TRUE);
            memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
            }

        if(netEvalIdle)
            {
            session->result = cell = getCell(CELL_EXPRESSION);
            cell->contents = (UINT)stuffString(session->host);
            cell = (CELL *)cell->contents;
            cell->next = stuffInteger(session->port);
            cell = cell->next;
            cell->next = result;
            }
        else
            session->result = result;
            
        closeStrStream(netStream);
        deleteInetSession(session->sock);
        close(session->sock);
        free(netStream);
        session->netStream = NULL;
            
        if(netEvalIdle)
            {
            list = getCell(CELL_EXPRESSION);
            list->contents = (UINT)copyCell(netEvalIdle);
            cell = getCell(CELL_QUOTE);
            cell->contents = (UINT)session->result;
            ((CELL*)list->contents)->next = cell;
            pushResult(list);
        	if(!evaluateExpressionSafe(list, &errNo))
				{
				freeSessions(base);
				longjmp(errorJump, errNo);
				}
            }

        count--;
        }
        
    /* check for rollover at midnight */
    if(milliSecTime() >= start) 
        elapsed += milliSecTime() - start;
    else 
        elapsed += milliSecTime();

    session = session->next;
    if(session == NULL) session = base;

    cleanupResults(resultStackIdxSave);
  }

/* free all sessions and configure result */
result = NULL;
while(base != NULL)
    {
    if(netEvalIdle == NULL)
        {
        if(result == NULL)
            {
			if(singleSession)
				result = base->result;
			else
				{
            	result = getCell(CELL_EXPRESSION);
            	result->contents = (UINT)base->result;
            	cell = base->result;
            	}
			}
        else
            {
            cell->next = base->result;
            cell = cell->next;
            }
        }
    session = base;
    base = base->next;
    free(session);
    }

if(elapsed > timeOut) 
	errorIdx = ERR_INET_TIMEOUT;
else errorIdx = 0;
if(netEvalIdle == NULL) return(result);
return(trueCell);
}    


void freeSessions(NETEVAL * base)
{
NETEVAL * session;

while(base != NULL)
    {
    if(base->netStream != NULL)
        {
        if(base->result != NULL)
          deleteList(base->result);
        closeStrStream(base->netStream);
        deleteInetSession(base->sock);
        close(base->sock);
        free(base->netStream);
        base->netStream = NULL;
        }
    session = base;
    base = base->next;
    free(session);
    }
}

int sendall(int sock, char * buffer, int len)
{
int bytesSend = 0;
int n;

while(bytesSend < len)
	{
 	if((n = send(sock, buffer, len - bytesSend, NO_FLAGS_SET)) == SOCKET_ERROR)
		return(SOCKET_ERROR);
	bytesSend += n;
	}

return(bytesSend);
}

/*********************** error handling ***************************************/

char * netErrorMsg[] =
    {
    "No error",
    "Cannot open socket",
    "Host name not known",
    "Not a valid service",
    "Connection failed",
    "Accept failed",
    "Connection closed",
    "Connection broken",
    "Socket recv failed",
    "Socket send failed",
    "Cannot bind socket",
    "Too much sockets in net-select",
    "Listen failed",
    "Badly formed IP",
    "Select failed",
    "Peek failed",
    "Not a valid socket",
	"Operation timed out"
    };


CELL * netError(int errorNo) 
{ 
errorIdx = errorNo; 
return(nilCell); 
} 

CELL * netEvalError(int errorNo)
{ 
errorIdx = errorNo; 
return(p_netLastError(NULL));
}

CELL * p_netLastError(CELL * params)
{
CELL * cell;
char str[64];

if(errorIdx == 0 || errorIdx > MAX_NET_ERROR) return(nilCell);

cell = getCell(CELL_EXPRESSION);
cell->contents = (UINT)stuffInteger(errorIdx);
snprintf(str, 63, "ERR: %s", netErrorMsg[errorIdx]);
((CELL*)cell->contents)->next = stuffString(str);

return(cell);
}


#ifdef NET_PING
/* net-ping */


#define PLEN 64

CELL * p_netPing(CELL * params)
{
CELL * address;
long maxwait = 1000, listmode = 0;
long option = 0;
long count = 0;

address = evaluateExpression(params);
if(address->type == CELL_EXPRESSION)
  {
  address = (CELL *)address->contents;
  listmode = 1;
  }
else if(address->type != CELL_STRING)
  return(errorProcExt(ERR_LIST_OR_STRING_EXPECTED, address));

params = params->next;
if(params != nilCell)  
  {
  params = getInteger(params, (UINT *)&maxwait);
  if(params != nilCell)
    params = getInteger(params, (UINT*)&count);
  if(params != nilCell)
    getInteger(params, (UINT*)&option);
  }

return(ping(address, (int)maxwait, (int)listmode, (int)count, (int)option));
}

static int ping_sequence = 0;

CELL * ping(CELL * address, int maxwait, int listmode, int maxCount, int option)
{
char * host;
char * ptr;
char * hostaddr = NULL;
struct hostent *hp;
struct sockaddr_in whereto;
struct sockaddr_in from;
struct protoent *proto;
int s, sockopt;
#ifdef WIN_32
int fromlen;
#else
#ifdef TRU64
unsigned long fromlen;
#else
#ifdef OS2
int fromlen;
#else
unsigned int fromlen;
#endif
#endif
#endif
unsigned char packet[64];
struct ip *ip;
struct icmp *icp = (struct icmp *) packet;
int broadcast = 0;
int size, ipNo, startIp = 0, endIp = 0;
int timeout = 0;
int sendCount = 0, receiveCount = 0;
size_t len;
struct timeval tv, tp;
CELL * result = NULL;
CELL * cell = nilCell;

proto = getprotobyname("icmp");
if ((s = socket(AF_INET, SOCK_RAW, (proto ? proto->p_proto : 1))) < 0) 
    return(netError(ERR_INET_OPEN_SOCKET));
    
/*
sockopt = 48 * 1024;
setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *) &sockopt, sizeof(sockopt));
*/

if(option)
  {
  sockopt = 1;
  setsockopt(s, SOL_SOCKET, option, (void *)&sockopt, sizeof(sockopt));
  }

gettimeofday(&tv, NULL );

/* for each list-IP */
while(address != nilCell)
	{
	mySleep(1);
	if(address->type != CELL_STRING)
		{
		shutdown(s, SHUT_RDWR);
		return(errorProcExt(ERR_STRING_EXPECTED, address));
		}

	host = (char *)address->contents;
	len = address->aux - 1;

	if(strncmp(host + len - 2, ".*", 2) == 0)
		{
		startIp = 1;
		endIp = 254;
		len--;
		}
	else
		{
		startIp = endIp = 0;

		ptr = host + len - 1;
		while(isdigit((int)*ptr)) --ptr;
		if(*ptr == '-')
			{
			endIp = atoi(ptr + 1);
			--ptr;
			while(isdigit((int)*ptr)) --ptr;
			if(*ptr == '.')
				{
				startIp = atoi(ptr + 1);
				len = ptr - host + 1;
				}
			else endIp = startIp = 0;
			if(endIp < startIp) endIp = startIp;	
			}
		}

	/* ping ip range */
	for(ipNo = startIp; ipNo <= endIp; ipNo++)
        {
		if(startIp)
			{
        	if(hostaddr == NULL) hostaddr = alloca(len + 4);
        	memcpy(hostaddr, host, len);
        	snprintf(hostaddr + len, 4, "%d", ipNo);
			}
    	else
       		hostaddr = host;
        
		/* printf("->%s\n", hostaddr); */

		memset((char *)&whereto, 0, sizeof(struct sockaddr_in));
		whereto.sin_family = AF_INET;

		if((whereto.sin_addr.s_addr = inet_addr(hostaddr)) == INADDR_NONE)
			{
			if(!(hp = gethostbyname(hostaddr)))
				{
				shutdown(s, SHUT_RDWR);
				return(netError(ERR_INET_HOST_UNKNOWN));
				}
			whereto.sin_family = hp->h_addrtype;
			memcpy((void *)&whereto.sin_addr, hp->h_addr, hp->h_length);
			}
      
		broadcast = ((whereto.sin_addr.s_addr & 0x000000ff) == 255);
		if(broadcast)
			{
			sockopt = 1;
			setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) &sockopt, sizeof(sockopt));
			}

		/* ping */
		memset(icp, 0, PLEN);
		icp->icmp_type = ICMP_ECHO;
		icp->icmp_code = 0;
		icp->icmp_cksum = 0;
		icp->icmp_seq = ping_sequence;
		icp->icmp_id = getpid() & 0xFFFF; 
		icp->icmp_cksum = in_cksum((unsigned short *) icp, PLEN );


		while(wait_ready(s, 10000, READY_WRITE) <= 0)
			{
        	gettimeofday(&tp, NULL);
        	if((timeout = (timediff(tp, tv) > maxwait))) break;
        	continue;
			}
			
		size = sendto(s, packet, PLEN, 0,(struct sockaddr *)&whereto, sizeof(struct sockaddr));
		sendCount++;
		if(size != PLEN)
			{
			/*
			printf("errno: %d host: %s\n", errno, hostaddr);
			*/
			if(errno == EHOSTUNREACH || errno == EHOSTDOWN) continue;
			shutdown(s, SHUT_RDWR);    
			return(netError(ERR_INET_WRITE));
			}
		}
	if(!listmode) break;
	address = address->next;
	}

/* wait for response(s) */
if(maxCount == 0) maxCount = sendCount;
for (;;) {
    memset(packet, 0, PLEN);
    fromlen = sizeof(from);

    if(wait_ready(s, 1000, READY_READ) <= 0)
        {
        gettimeofday(&tp, NULL);
       	if((timeout = (timediff(tp, tv) > maxwait))) break;
        continue;
        }    

    if ( (len = recvfrom(s, packet, PLEN, 0, (struct sockaddr *)&from, &fromlen)) < 0)
        continue;
    
    ip = (struct ip *) packet;
    icp = (struct icmp *)(packet + (ip->ip_hl << 2));

    if(icp->icmp_id != (getpid() & 0xFFFF)) continue; 

    if(result == NULL)
      {
      result = getCell(CELL_EXPRESSION);
      cell = stuffString(inet_ntoa(from.sin_addr));
      result->contents = (UINT)cell;
      }
    else
      {
      cell->next = stuffString(inet_ntoa(from.sin_addr));
      cell = cell->next;
      }

    if(++receiveCount == maxCount) break;
    if( !(broadcast || listmode || startIp) ) break;
    }
    
shutdown(s, SHUT_RDWR); 
ping_sequence++;
if(timeout) errorIdx = ERR_INET_TIMEOUT;
else errorIdx = 0;
/*
printf("sendCount %d receiveCount %d maxCount %d timeout %d\n", 
	sendCount, receiveCount, maxCount, timeout);
*/
return(result == NULL ? getCell(CELL_EXPRESSION) : result);
}


int in_cksum(unsigned short * addr, int len)
{
int nleft = len;
unsigned short *w = addr;
unsigned short  answer;
int sum = 0;

while( nleft > 1 )  {
    sum += *w++;
    nleft -= 2;
    }

if( nleft == 1 ) {
    u_short    u = 0;
    *(unsigned char *)(&u) = *(unsigned char *)w ;
    sum += u;
    }

sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}

#endif /* NET_PING */


/* check socket for readability or error 
   return 0 if the time limit expires or -1
   on error 
*/
int wait_ready(int sock, INT64 wait, int mode)
{
struct timeval timeOut;
fd_set socketSet;

FD_ZERO(&socketSet);
FD_SET(sock, &socketSet);

timeOut.tv_sec = wait/1000000;
timeOut.tv_usec = wait - timeOut.tv_sec * 1000000;

if(mode == READY_READ)
    return(select(FD_SETSIZE, &socketSet, NULL, &socketSet, &timeOut));
else
    return(select(FD_SETSIZE, NULL, &socketSet, &socketSet, &timeOut));
}


/* ----------------------------- socket->filestream stuff for win32 ------------------------*/

#ifdef WIN_32

/*
These functions use the FILE structure to store the raw file handle in '->_file' and
set ->_flag to 0xFFFF, to identify this as a faked FILE structure.
*/

FILE * win32_fdopen(int handle, const char * mode)
{
FILE * fPtr;

if((fPtr = (FILE *)malloc(sizeof(FILE))) == NULL)
    return(NULL);

memset(fPtr, 0, sizeof(FILE));


#ifdef WIN_32
fPtr->_file = handle;
fPtr->_flag = 0xFFFF;
#endif

return(fPtr);
}

int win32_fclose(FILE * fPtr)
{
if(isSocketStream(fPtr))
   return(close(getSocket(fPtr)));


return(fclose(fPtr));
}


/* for a full fprintf with format string and parameters
   see version previous to 9.0.2
*/
int win32_fprintf(FILE * fPtr, char * buffer)
{
int pSize;

if(!isSocketStream(fPtr))
    return(fprintf(fPtr, buffer));

pSize = strlen(buffer);

if((pSize = sendall(getSocket(fPtr), buffer, pSize)) == SOCKET_ERROR)
     {
     close(getSocket(fPtr));
     return(-1);
     }

return(pSize);
}

int win32_fgetc(FILE * fPtr)
{
char chr;

if(!isSocketStream(fPtr))
    return(fgetc(fPtr));

if(recv(getSocket(fPtr), &chr, 1, NO_FLAGS_SET) <= 0)
    {
    close(getSocket(fPtr));
    return(-1);
    }

return(chr);
}


char * win32_fgets(char * buffer, int  size, FILE * fPtr)
{
int bytesReceived = 0;
char chr;

if(!isSocketStream(fPtr))
    return(fgets(buffer, size - 1, fPtr));

while(bytesReceived < size)
    {
    if(recv(getSocket(fPtr), &chr, 1, NO_FLAGS_SET) <= 0)
        {
        close(getSocket(fPtr));
        return(NULL);
        }

    *(buffer + bytesReceived++) = chr;

    if(chr == '\n') break;
    }

*(buffer + bytesReceived) = 0;

return(buffer);
}

#endif

/* eof */


syntax highlighted by Code2HTML, v. 0.9.1