/* 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 . */ #include "newlisp.h" #include #ifdef WIN_32 #include #include #define fdopen win32_fdopen #define SHUT_RDWR 2 #else #include #include #include #include #include #include #include #include #include #include #include #include #endif #ifdef SOLARIS #include #include #include #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 */