/* nl-sock.c Copyright (C) 2016 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 WINDOWS #include #pragma push_macro("UINT") #undef UINT /* avoid clash with newLISP UINT */ #include #pragma pop_macro("UINT") #include #define fdopen win_fdopen #define SHUT_RDWR 2 #define gethostbyname2(A, B) gethostbyname(A) #else /* UNIX */ #include #include #include #include #include #include #define __FAVOR_BSD #include #include #include #include #include #include #ifndef OS2 #include #endif #include #include #ifndef IPPROTO_DIVERT #define IPPROTO_DIVERT 254 #endif /* Android needs it */ #ifndef ICMP6_FILTER #define ICMP6_FILTER 1 #endif #endif /* end UNIX */ #ifdef CYGWIN #include /* not on Cygwin, get from other OS */ #define ICMP_ECHO 8 struct icmp { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_cksum; unsigned short icmp_id; unsigned short icmp_seq; }; #endif /* end CYGWIN */ #if defined(SOLARIS) || defined(TRU64) || defined(AIX) #include #include #include #define gethostbyname2(A, B) gethostbyname(A) #endif #ifdef SOLARIS #define FIONREAD I_NREAD #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 WINDOWS #define close closesocket /* for file operations on Windows use _close */ #else #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 #endif #define isnum(A) ((A)>= '0' && (A) <= '9') IO_SESSION * ioSessions = NULL; int isSessionSocket(int sock); int getSocketFamily(int sock); #ifndef EMSCRIPTEN #define READY_READ 0 #define READY_WRITE 1 UINT netErrorIdx = 0; extern int logTraffic; extern int noPromptMode; int ADDR_FAMILY = AF_INET; /* the default is IPv4 */ int ICMP_TYPE; #define STRADDR_LEN 40 /* set the default interface and select IPv4 or IPv6 mode */ struct sockaddr * defaultIn = NULL; void * defaultInAddr = NULL; /* either (struct in6_addr *) or (struct in_adr *) */ char * defaultInterface = NULL; socklen_t defaultInLen; extern int opsys; void initDefaultInAddr() { struct sockaddr_in6 * address6; struct sockaddr_in * address; if(defaultIn != NULL) free(defaultIn); defaultInLen = (ADDR_FAMILY == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); defaultIn = callocMemory(defaultInLen); if(ADDR_FAMILY == AF_INET6) { address6 = (struct sockaddr_in6 *)defaultIn; address6->sin6_addr = in6addr_any; defaultInAddr = &address6->sin6_addr; address6->sin6_family = AF_INET6; ICMP_TYPE = IPPROTO_ICMPV6; opsys |= 0x200; } else { address = (struct sockaddr_in *)defaultIn; address->sin_addr.s_addr = INADDR_ANY; defaultInAddr = &address->sin_addr; address->sin_family = AF_INET; ICMP_TYPE = IPPROTO_ICMP; opsys &= ~0x200; } } #endif /* ifndef EMSCRIPTEN */ /********************** IO session functions *******************/ IO_SESSION * createIOsession(int handle, int family) { IO_SESSION * iSession; iSession = (IO_SESSION *)calloc(sizeof(IO_SESSION), 1); iSession->handle = handle; iSession->family = family; if(ioSessions == NULL) { ioSessions = iSession; iSession->next = NULL; } else { iSession->next = ioSessions; ioSessions = iSession; } return(iSession); } int deleteIOsession(int handle) { IO_SESSION * session; IO_SESSION * previous; if(ioSessions == NULL) return(0); else session = previous = ioSessions; while(session) { if(session->handle == handle) { if(session == ioSessions) ioSessions = session->next; else previous->next = session->next; if(session->stream != NULL) fclose(session->stream); else close(handle); free((char *)session); return(TRUE); } previous = session; session = session->next; } return(FALSE); } int isSessionSocket(int sock) { IO_SESSION * session; if(ioSessions == NULL) return(FALSE); session = ioSessions; while(session) { if(session->handle == sock) return(TRUE); session = session->next; } return(FALSE); } int getSocketFamily(int sock) { IO_SESSION * session; session = ioSessions; while(session) { if(session->handle == sock) return(session->family); session = session->next; } return(-1); } FILE * getIOstream(int handle) { IO_SESSION * session; session = ioSessions; while(session) { if(session->handle == handle) return(session->stream); session = session->next; } return(NULL); } /* ========================= IO session functions end ===================== */ #ifndef EMSCRIPTEN #ifdef WINDOWS int ipstrFromSockAddr(struct sockaddr * addr, char * host, int len) { WSAAddressToString(addr, defaultInLen, NULL, host, (LPDWORD)&len); return(TRUE); } #else /* UNIX */ int ipstrFromSockAddr(struct sockaddr * addr, char * host, int len) { struct sockaddr_in6 * saddr6 = (struct sockaddr_in6 *)addr; struct sockaddr_in * saddr =(struct sockaddr_in *)addr; if(addr->sa_family == AF_INET6) inet_ntop(AF_INET6, &saddr6->sin6_addr, host, len); else strncpy(host, inet_ntoa(saddr->sin_addr), len); return(TRUE); } #endif /* ANDROID and WINDOWS need this */ #ifndef in_port_t #define in_port_t short int #endif in_port_t portFromSockAddr(struct sockaddr * addr) { in_port_t * portPtr = (in_port_t *)&addr->sa_data[0]; return(ntohs(*portPtr)); } void setSockaddrPort(struct sockaddr * addr, in_port_t port) { in_port_t * portPtr = (in_port_t *)&addr->sa_data[0]; *portPtr = htons(port); } /********************* user functions **************************/ CELL * p_netClose(CELL * params) { UINT sock; getInteger(params, &sock); if(!deleteIOsession((int)sock)) return(netError(ERR_INET_NOT_VALID_SOCKET)); netErrorIdx = 0; return(trueCell); } CELL * p_netSessions(CELL * params) { IO_SESSION * session; IO_SESSION * sPtr; CELL * sList; session = ioSessions; sList = getCell(CELL_EXPRESSION); while(session) { sPtr = session; session = session->next; if(sPtr->family != AF_UNSPEC || getFlag(params)) addList(sList, stuffInteger(sPtr->handle)); } return(sList); } /*********************************************************************/ CELL * p_netService(CELL * params) { struct servent * pSe; char * service = NULL; char * protocol = NULL; CELL * cell; UINT port; params = getEvalDefault(params, &cell); getString(params, &protocol); if(cell->type == CELL_STRING) { service = (char *)cell->contents; if((pSe = getservbyname(service, protocol)) == NULL) return(netError(ERR_INET_INVALID_SERVICE)); port = (int)ntohs(pSe->s_port); netErrorIdx = 0; return(stuffInteger((UINT)port)); } if(isNumber(cell->type)) { getIntegerExt(cell, &port, FALSE); if((pSe = getservbyport(htons(port), protocol)) == NULL) return(netError(ERR_INET_INVALID_SERVICE)); netErrorIdx = 0; return(stuffString(pSe->s_name)); } return(errorProcExt(ERR_NUMBER_OR_STRING_EXPECTED, cell)); } CELL * p_netConnect(CELL * params) { CELL * cell; char * remoteHostName; UINT portNo; UINT topt = CONNECT_TIMEOUT; /* default ms timeout from newlisp.h */ int sock; int type; int protocol = 0; params = getString(params, &remoteHostName); #ifndef WINDOWS if(params == nilCell) { if((sock = netConnectLocal(remoteHostName)) == SOCKET_ERROR) return(netError(netErrorIdx)); else return(stuffInteger((UINT)sock)); } #endif params = getInteger(params, &portNo); type = SOCK_STREAM; if(params != nilCell) { cell = evaluateExpression(params); if(cell->type == CELL_STRING) { protocol = toupper(*(char *)cell->contents); if(!(protocol == 'M' || protocol == 'B' || protocol == 'U')) return(errorProc(ERR_INVALID_OPTION)); type = SOCK_DGRAM; if(protocol == 'M') /* get ttl */ { if(params->next != nilCell) /* topt is ttl in this case */ getInteger(params->next, &topt); else topt = 3; } } else if(isNumber(cell->type)) /* topt is ms timeout */ getIntegerExt(cell, &topt, FALSE); else return(errorProcExt(ERR_NUMBER_OR_STRING_EXPECTED, cell)); } if((sock = netConnect(remoteHostName, (int)portNo, type, protocol, (int)topt)) == SOCKET_ERROR) return(netError(netErrorIdx)); createIOsession(sock, ADDR_FAMILY); netErrorIdx = 0; return(stuffInteger((UINT)sock)); } #ifndef WINDOWS /* 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) { netErrorIdx = 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); netErrorIdx = ERR_INET_CONNECT_FAILED; return(SOCKET_ERROR); } createIOsession(sock, AF_UNIX); netErrorIdx = 0; return(sock); } #endif int blockSocket(int sock) { #ifdef WINDOWS u_long arg = 0; return(ioctlsocket(sock, FIONBIO, &arg)); #else /* UNIX */ int arg; arg = fcntl(sock, F_GETFL, NULL); arg &= (~O_NONBLOCK); return(fcntl(sock, F_SETFL, arg)); #endif } #ifdef UNBLOCK int unblockSocket(int sock) { #ifdef WINDOWS u_long arg = 1; return(ioctlsocket(sock, FIONBIO, &arg)); #else /* Unix */ int arg; arg = fcntl(sock, F_GETFL, NULL); arg &= (~O_NONBLOCK); return(fcntl(sock, F_SETFL, arg)); #endif } #endif /* create internet socket if prot = NULL then topt is timeout in ms, else topt is ttl (time to live) */ int netConnect(char * remoteHostName, int portNo, int type, int prot, int topt) { struct addrinfo hints, *res, *res0; char portStr[10]; int sock, opt; #if defined(WINDOWS) || defined(EMSCRIPTEN) u_long arg = 1; #else int arg, value; socklen_t socklen = sizeof(sock); #endif int result = -1; int sinlen; /* create socket */ if((sock = socket(ADDR_FAMILY, type, 0)) == INVALID_SOCKET) { netErrorIdx = ERR_INET_OPEN_SOCKET; return(SOCKET_ERROR); } if(prot == 0) /* topt is timeout in millisecs */ { #ifdef WINDOWS if(ioctlsocket(sock, FIONBIO, &arg) != 0) { netErrorIdx = ERR_INET_CANNOT_CHANGE_SOCK_BLOCK; return(SOCKET_ERROR); } #else /* UNIX */ arg = fcntl(sock, F_GETFL, NULL); if(fcntl(sock, F_SETFL, arg | O_NONBLOCK) < 0) { netErrorIdx = ERR_INET_CANNOT_CHANGE_SOCK_BLOCK; return(SOCKET_ERROR); } #endif } else if(prot == 'M' || prot == 'B') { sinlen = (ADDR_FAMILY == AF_INET6) ? sizeof(struct in6_addr) : sizeof(struct in_addr); if(prot == 'M') { setsockopt(sock, 0, IP_MULTICAST_IF, (const void *)defaultInAddr, sinlen); opt = topt; /* ttl time to live */ 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(type == SOCK_DGRAM && *remoteHostName == 0) return(sock); memset(&hints, 0, sizeof(hints)); hints.ai_family = (ADDR_FAMILY == AF_INET6) ? PF_INET6 : PF_INET; hints.ai_socktype = SOCK_STREAM; snprintf(portStr, 10, "%d", portNo); if(getaddrinfo(remoteHostName, portStr, &hints, &res0) != 0) { netErrorIdx = ERR_INET_HOST_UNKNOWN; return(SOCKET_ERROR); } for(res = res0; res; res = res->ai_next) { result = connect(sock, res->ai_addr, res->ai_addrlen); if(result < 0) { #ifdef WINDOWS if(WSAGetLastError() == WSAEWOULDBLOCK) #else if(errno == EINPROGRESS) #endif { if((result = wait_ready(sock, topt * 1000, READY_WRITE)) <= 0) { netErrorIdx = result < 0 ? ERR_INET_CONNECT_FAILED : ERR_INET_TIMEOUT; goto CONNECT_FAILED; } #ifndef WINDOWS getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&value, &socklen); if (value) { netErrorIdx = ERR_INET_CONNECT_FAILED; goto CONNECT_FAILED; } #endif result = 0; break; } else { netErrorIdx = ERR_INET_CONNECT_FAILED; goto CONNECT_FAILED; } break; } else break; /* result == 0 */ } if(result != 0) { netErrorIdx = ERR_INET_CONNECT_FAILED; goto CONNECT_FAILED; } if(blockSocket(sock) != 0) { netErrorIdx = ERR_INET_CANNOT_CHANGE_SOCK_BLOCK; goto CONNECT_FAILED; } freeaddrinfo(res0); netErrorIdx = 0; return(sock); CONNECT_FAILED: freeaddrinfo(res0); close(sock); return(SOCKET_ERROR); } /* take and empty sockaddr and fill in from portNo, socket type and interface if ifAddr is defined as NULL or and empty string assume sockaddr at defaultIn which is initialized to INADDR_ANY in initDefaultInAddr() */ int getHostAddr(struct sockaddr * address, int stype, char * ifAddr) { struct addrinfo hints, *res; int error; if(ifAddr != NULL && *ifAddr != '\0') { memset(&hints, 0, sizeof(hints)); hints.ai_family = (ADDR_FAMILY == AF_INET6) ? PF_INET6 : PF_INET; hints.ai_socktype = stype; if((error = getaddrinfo(ifAddr, NULL, &hints, &res)) != 0) return(error); memcpy(address, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return(0); } memcpy(address, defaultIn, defaultInLen); return(0); } CELL * p_netInterface(CELL * params) { char * ifAddr; char IPaddress[STRADDR_LEN]; netErrorIdx = 0; if(params != nilCell) { params = getString(params, &ifAddr); if(getHostAddr((struct sockaddr *)defaultIn, 0, ifAddr) != 0) return(netError(ERR_INET_HOST_UNKNOWN)); } getnameinfo((struct sockaddr *)defaultIn, defaultInLen, IPaddress, STRADDR_LEN, NULL, 0, NI_NUMERICHOST); return(stuffString(IPaddress)); } CELL * p_netIpv(CELL * params) { UINT protocol = (ADDR_FAMILY == AF_INET6) ? 6 : 4; if(params != nilCell) { getInteger(params, &protocol); if(protocol != 4 && protocol != 6) return(errorProc(ERR_INVALID_PARAMETER)); if(protocol == 6) { ADDR_FAMILY = AF_INET6; opsys |= 512; } else { ADDR_FAMILY = AF_INET; opsys &= ~512; } initDefaultInAddr(); } return(stuffInteger(protocol)); } /********* 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)); netErrorIdx = 0; return(stuffInteger(sock)); } int netAccept(int listenSock) { int sock, family; struct sockaddr * dest_sin; socklen_t dest_slen; #ifndef WINDOWS struct sockaddr_un dest_sun; #endif family = getSocketFamily(listenSock); #ifndef WINDOWS if(family == AF_UNIX) { dest_slen = sizeof(struct sockaddr_un); sock = accept(listenSock, (struct sockaddr *) &dest_sun, &dest_slen); } else #endif { dest_slen = (ADDR_FAMILY == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); dest_sin = alloca(dest_slen); sock = accept(listenSock, dest_sin, (void *)&dest_slen); } if(sock != INVALID_SOCKET) { createIOsession(sock, family); netErrorIdx = 0; } return(sock); } /******************* returns IP and port number from socket *************/ #define LOCAL_INFO 0 #define PEER_INFO 1 int getIpPortFromSocket(int sock, int peerLocalFlag, char * IPaddress) { int family; socklen_t address_sin_len; struct sockaddr * address_sin; family = getSocketFamily(sock); address_sin_len = (family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); address_sin = alloca(address_sin_len); *IPaddress = 0; if(family == AF_UNIX) { strncpy(IPaddress, "local", 6); return(0); } if(peerLocalFlag == LOCAL_INFO) { if(getsockname(sock, address_sin, (void *)&address_sin_len) == SOCKET_ERROR) return(SOCKET_ERROR); } else if(getpeername(sock, address_sin, (void *)&address_sin_len) == SOCKET_ERROR) return(SOCKET_ERROR); /* return address and port, IP number, address family taken fron address_sin */ ipstrFromSockAddr((struct sockaddr *)address_sin, IPaddress, STRADDR_LEN); return(portFromSockAddr((struct sockaddr *)address_sin)); } 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[STRADDR_LEN]; UINT addressPort, sock; getInteger(params, &sock); if((addressPort = getIpPortFromSocket((int)sock, peerLocalFlag, name)) == SOCKET_ERROR) return(netError(ERR_INET_NOT_VALID_SOCKET)); result = makeCell(CELL_EXPRESSION, (UINT)stuffString(name)); cell = (CELL *)result->contents; cell->next = stuffInteger((UINT)addressPort); netErrorIdx = 0; return(result); } CELL * p_netLookup(CELL * params) { char * hostString; int forceByName = 0; struct addrinfo hints, *res; char hbuf[NI_MAXHOST]; int flags = NI_NUMERICHOST; netErrorIdx = 0; params = getString(params, &hostString); forceByName = getFlag(params); if( ((ADDR_FAMILY == AF_INET6) && strstr(hostString, ":") && !forceByName) || ((ADDR_FAMILY == AF_INET) && isDigit((unsigned char)*hostString) && !forceByName) ) flags = NI_NAMEREQD; memset(&hints, 0, sizeof(hints)); hints.ai_family = (ADDR_FAMILY == AF_INET6) ? PF_INET6 : PF_INET; hints.ai_socktype = SOCK_STREAM; /* hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; */ if(getaddrinfo(hostString, NULL, &hints, &res) != 0) return(netError(ERR_INET_HOST_UNKNOWN)); if(getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, flags) != 0) { freeaddrinfo(res); return(netError(ERR_INET_HOST_UNKNOWN)); } freeaddrinfo(res); return(stuffString(hbuf)); } CELL * netReceive(int sock, SYMBOL * readSymbol, size_t readSize, CELL * params); CELL * p_netReceive(CELL * params) { UINT sock; SYMBOL * readSymbol; size_t readSize; CELL * cell; params = getInteger(params, &sock); params = getEvalDefault(params, &cell); if(!symbolCheck) return(errorProcExt(ERR_SYMBOL_EXPECTED, cell)); if(symbolCheck->contents != (UINT)cell) return(errorProc(ERR_IS_NOT_REFERENCED)); readSymbol = symbolCheck; 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 = {NULL, NULL, 0, 0, 0}; char chr; CELL * cell; if(isProtected(readSymbol->flags)) return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(readSymbol))); 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); deleteIOsession(sock); return(netError(ERR_INET_CONNECTION_DROPPED)); } if(bytesReceived == SOCKET_ERROR) { closeStrStream(&netStream); deleteIOsession(sock); return(netError(ERR_INET_READ)); } cell = stuffStringN(netStream.buffer, bytesReceived); closeStrStream(&netStream); deleteList((CELL *)readSymbol->contents); readSymbol->contents = (UINT)cell; netErrorIdx = 0; return(stuffInteger(bytesReceived)); } CELL * netReceiveFrom(int sock, size_t readSize, int closeFlag) { int portNo; int family; char * buffer; ssize_t bytesReceived; struct sockaddr * remote; char IPaddress[STRADDR_LEN]; CELL * cell; CELL * result; #ifdef TRU64 unsigned long remote_sin_len; #else socklen_t remote_sin_len; #endif buffer = (char *)allocMemory(readSize + 1); family = getSocketFamily(sock); remote_sin_len = (family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); remote = alloca(remote_sin_len); memset(remote, 0, remote_sin_len); bytesReceived = recvfrom(sock, buffer, readSize, 0, (struct sockaddr *)remote, &remote_sin_len); if(bytesReceived == SOCKET_ERROR) { freeMemory(buffer); deleteIOsession(sock); return(netError(ERR_INET_READ)); } ipstrFromSockAddr((struct sockaddr *)remote, IPaddress, STRADDR_LEN); portNo = portFromSockAddr((struct sockaddr *)remote); cell = result = makeCell(CELL_EXPRESSION, (UINT)stuffStringN(buffer, bytesReceived)); cell = (CELL *)cell->contents; cell->next = stuffString(IPaddress); ((CELL*)cell->next)->next = stuffInteger(portNo); freeMemory(buffer); if(closeFlag) { deleteIOsession(sock); } netErrorIdx = 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 = getInteger64Ext(params, &wait, TRUE); if(params != nilCell) getString(params, &ifaddr); } if((sock = netListenOrDatagram((int)portNo, SOCK_DGRAM, ifaddr, NULL, 0)) == 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, size2; char * buffer; ssize_t bytesSent; params = getInteger(params, &sock); params = getStringSize(params, &buffer, &size, TRUE); if(params->type != CELL_NIL) { getInteger(params, (UINT *)&size2); if(size2 < size) size = size2; } if((bytesSent = sendall((int)sock, buffer, size)) == SOCKET_ERROR) { deleteIOsession((int)sock); return(netError(ERR_INET_WRITE)); } netErrorIdx = 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 * destination; socklen_t destination_len; 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); destination_len = (ADDR_FAMILY == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); destination = alloca(destination_len); if(*remoteHost != 0) /* 10.0.1 */ { if(getHostAddr(destination, 0, remoteHost) != 0) return(netError(ERR_INET_HOST_UNKNOWN)); setSockaddrPort(destination, remotePort); } if(type == SEND_TO_UDP) /* for 'net-send-udp' */ { if((sock = socket(ADDR_FAMILY, 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); } /* for socket opened with ip-address in (net-connect host port "udp") in this case issue (net-send-to "" port message socket) 10.0.1 */ if(*remoteHost == 0) bytesSent = sendto((int)sock, buffer, size, NO_FLAGS_SET, NULL, 0); else bytesSent = sendto((int)sock, buffer, size, NO_FLAGS_SET, (struct sockaddr *)destination, destination_len); if(type == SEND_TO_UDP) close((int)sock); if(bytesSent == SOCKET_ERROR) return(netError(ERR_INET_WRITE)); netErrorIdx = 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; int sockopt = 0; int opt; CELL * cell; type = SOCK_STREAM; cell = evaluateExpression(params); params = params->next; #ifndef WINDOWS if(cell->type == CELL_STRING) { if((sock = netListenLocal((char *)cell->contents)) == SOCKET_ERROR) return(netError(netErrorIdx)); 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); opt = toupper(*option); if(opt == 'U') type = SOCK_DGRAM; #ifndef WINDOWS else if(opt == 'D') { type = SOCK_RAW; sockopt = IPPROTO_DIVERT; } #endif else if(opt == 'M') { type = SOCK_DGRAM; mcAddr = ifAddr; ifAddr = NULL; } else errorProc(ERR_INVALID_OPTION); } } if((sock = netListenOrDatagram((int)portNo, type, ifAddr, mcAddr, sockopt)) == SOCKET_ERROR) return(nilCell); return(stuffInteger(sock)); } #ifndef WINDOWS 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); netErrorIdx = ERR_INET_CANNOT_BIND; return(SOCKET_ERROR); } if(listen(sock, MAX_PENDING_CONNECTS) == SOCKET_ERROR) { close(sock); netErrorIdx = ERR_INET_LISTEN_FAILED; return(SOCKET_ERROR); } createIOsession(sock, AF_UNIX); netErrorIdx = 0; return(sock); } #endif int netListenOrDatagram(int portNo, int stype, char * ifAddr, char * mcAddr, int option) { int sock, one = 1; struct sockaddr * local; struct ip_mreq mcast; socklen_t local_len; if((sock = socket(ADDR_FAMILY, stype, option)) == INVALID_SOCKET) { netErrorIdx = ERR_INET_OPEN_SOCKET; return SOCKET_ERROR; } local_len = (ADDR_FAMILY == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); local = alloca(local_len); memset(local, 0, local_len); if(getHostAddr((struct sockaddr *)local, stype, ifAddr) != 0) return(SOCKET_ERROR); setSockaddrPort(local, portNo); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void*)&one, sizeof(one)); if(bind(sock, (struct sockaddr *)local, local_len) == SOCKET_ERROR) { close(sock); netErrorIdx = ERR_INET_CANNOT_BIND; return(SOCKET_ERROR); } if(mcAddr != NULL) { memset(&mcast, 0, sizeof(mcast)); mcast.imr_multiaddr.s_addr = inet_addr(mcAddr); if(ADDR_FAMILY == AF_INET) mcast.imr_interface.s_addr = INADDR_ANY; /* not handled/debugged else mcast.imr_interface.s_addr = IN6ADDR_ANY_INIT; */ setsockopt(sock, 0, IP_ADD_MEMBERSHIP, (const void *)&mcast, sizeof(mcast)); } if(stype == SOCK_STREAM) { if(listen(sock, MAX_PENDING_CONNECTS) == SOCKET_ERROR) { close(sock); netErrorIdx = ERR_INET_LISTEN_FAILED; return(SOCKET_ERROR); } } createIOsession(sock, ADDR_FAMILY); netErrorIdx = 0; return(sock); } /* returns number of bytes ready to read */ CELL * p_netPeek(CELL * params) { UINT sock; #ifdef WINDOWS u_long result; #else int result; #endif getInteger(params, &sock); if(ioctl((int)sock, FIONREAD, &result) == SOCKET_ERROR) return(netError(ERR_INET_PEEK_FAILED)); netErrorIdx = 0; return(stuffInteger((UINT)result)); } typedef struct { int sock; void * next; } SOCKLIST; /* checks a socket for readability/writeability */ CELL * p_netSelect(CELL * params) { INT64 wait; char * mode; struct timeval timeOut; fd_set socketSet; CELL * cell; CELL * list; CELL * sockListHead = NULL; CELL * sockListPtr; struct timeval* tmvPtr; UINT socket; int setSize = 0; netErrorIdx = 0; FD_ZERO(&socketSet); cell = evaluateExpression(params); if(isNumber(cell->type)) getIntegerExt(cell, (UINT*)&socket, FALSE); else if(isList(cell->type)) { cell = (CELL*)cell->contents; sockListHead = cell; while(cell != nilCell) { cell = getIntegerExt(cell, (UINT*)&socket, FALSE); if(setSize == FD_SETSIZE) return(netError(ERR_INET_TOO_MUCH_SOCKETS)); else setSize++; FD_SET((int)socket, &socketSet); } } else return(errorProcExt(ERR_LIST_OR_NUMBER_EXPECTED, params)); params = getString(params->next, &mode); getInteger64Ext(params, &wait, TRUE); tmvPtr = (wait == -1) ? NULL : &timeOut; timeOut.tv_sec = wait/1000000; timeOut.tv_usec = wait - timeOut.tv_sec * 1000000; if(sockListHead == NULL) FD_SET((int)socket, &socketSet); /* printf("%d %d %d\n", timeOut.tv_sec, timeOut.tv_usec, sizeof(timeOut.tv_sec)); */ if(*mode == 'r') setSize = select(FD_SETSIZE, &socketSet, NULL, NULL, tmvPtr); else if(*mode == 'w') setSize = select(FD_SETSIZE, NULL, &socketSet, NULL, tmvPtr); else if(*mode == 'e') setSize = select(FD_SETSIZE, NULL, NULL, &socketSet, tmvPtr); else return(errorProcExt2(ERR_INVALID_PARAMETER, stuffString(mode))); if(setSize >= 0) { if(sockListHead == NULL) { if(setSize == 0) return(nilCell); else return(trueCell); } list = getCell(CELL_EXPRESSION); if(setSize == 0) return(list); sockListPtr = sockListHead; while(sockListPtr != nilCell) { sockListPtr = getIntegerExt(sockListPtr, &socket, FALSE); /* printf("testing %lu\n", socket); */ if(FD_ISSET(socket, &socketSet)) { addList(list, stuffInteger(socket)); /* printf("adding %lu\n", socket); */ } } return(list); } netError(ERR_INET_SELECT_FAILED); return(nilCell); } extern char logFile[]; void writeLog(char * text, int newLine) { int handle; #ifdef WINDOWS 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 if(write(handle, text, strlen(text)) < 0) return; if(newLine) if(write(handle, &LINE_FEED, LINE_FEED_LEN) < 0) return; #ifdef WINDOWS _close(handle); #else close(handle); #endif } FILE * win_fdopen(int handle, const char * mode); FILE * serverFD(int port, char * domain, int reconnect) { static int sock, connection; char name[STRADDR_LEN]; char text[80]; time_t t; text[79] = 0; if(!reconnect) { #ifndef WINDOWS if(port != 0) sock = netListenOrDatagram(port, SOCK_STREAM, NULL, NULL, 0); else sock = netListenLocal(domain); #else sock = netListenOrDatagram(port, SOCK_STREAM, NULL, NULL, 0); #endif if(sock == SOCKET_ERROR) return(NULL); else { snprintf(text, 79, "newLISP v.%d listening on %s", version, domain); writeLog(text, TRUE); } } else { deleteIOsession(connection); } if((connection = netAccept(sock)) == SOCKET_ERROR) return NULL; /* avoid registering socket twice */ if(!isSessionSocket(connection)) createIOsession(connection, (port != 0) ? ADDR_FAMILY : AF_UNIX); /* print log */ getIpPortFromSocket(connection, PEER_INFO, name); t = time(NULL); snprintf(text, 79, "Connected to %s on %s", name, ctime(&t)); /* printf(text); */ writeLog(text, 0); setenv("REMOTE_ADDR", name, 1); 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; INT timeOut = 60000; /* milli secs */ int start, elapsed = 0; CELL * result; STREAM * netStream; CELL * netEvalIdle = NULL; char buffer[MAX_BUFF]; int rawMode = FALSE; int singleSession = FALSE; jmp_buf errorJumpSave; int errNo; UINT * 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); */ /* convert to string if required (since 10.1.1) */ prog = cellToString(evaluateExpression(params), &size, FALSE); params = params->next; 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); prog = cellToString(evaluateExpression(cell), &size, FALSE); rawMode = getFlag(cell->next); 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)); } /* timeout for making connection is always 15 secs, but variable timeOut is used during connection */ #ifndef WINDOWS if(port != 0) sock = netConnect(host, (int)port, SOCK_STREAM, 0, 15000); else sock = netConnectLocal(host); #else sock = netConnect(host, (int)port, SOCK_STREAM, 0, 15000); #endif if(sock == SOCKET_ERROR) { session->result = netEvalError(netErrorIdx); 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); createIOsession(sock, ADDR_FAMILY); count++; CONTINUE_CREATE_SESSION: list = list->next; if(list != nilCell) goto CREATE_SESSION; /* get timeout ms and optional handler symbol this is the timeout after connections are made waiting for the result to come back */ 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 = makeCell(CELL_EXPRESSION, (UINT)copyCell(netEvalIdle)); pushResult(cell); if(!evaluateExpressionSafe(cell, &errNo)) { freeSessions(base); longjmp(errorJump, errNo); } } bytes = -1; errNo = 0; 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 >= 0) printf("bytes:%ld=>%s<=\n", bytes, buffer); */ if(bytes) { if(bytes == -1) bytes = 0; errNo = (memcmp(buffer, "\nERR: ", 6) == 0); writeStreamStr(netStream, buffer, bytes); } } if(ready < 0 || bytes == 0 || errNo || elapsed >= timeOut) { /* printf("count=%ld ready=%d bytes=%ld elapsed=%d\n", count, ready, bytes, elapsed); */ if(elapsed >= timeOut) result = copyCell(nilCell); else if(rawMode || errNo) /* get raw buffer without the quote */ result = stuffStringN(netStream->buffer, netStream->position); else { memcpy(errorJumpSave, errorJump, sizeof(jmp_buf)); if((errNo = setjmp(errorJump)) != 0) { memcpy(errorJump, errorJumpSave, sizeof(jmp_buf)); freeSessions(base); longjmp(errorJump, errNo); } /* Only one expression should be sent out by net-eval. When contained in a string more than one can be sent out and will be evaluated, burt net-eval will only return the result of the last(10.6.3) one */ result = sysEvalString(netStream->buffer, currentContext, nilCell, READ_EXPR_NET); /* changed from READ_EXPR+SYNC to READ_EXPR in 10.6.3 */ memcpy(errorJump, errorJumpSave, sizeof(jmp_buf)); } if(netEvalIdle) { session->result = cell = makeCell(CELL_EXPRESSION, (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); deleteIOsession(session->sock); free(netStream); session->netStream = NULL; if(netEvalIdle) { list = makeCell(CELL_EXPRESSION, (UINT)copyCell(netEvalIdle)); cell = makeCell(CELL_QUOTE, (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 = makeCell(CELL_EXPRESSION, (UINT)base->result); cell = base->result; } } else { cell->next = base->result; cell = cell->next; } } session = base; base = base->next; free(session); } if(elapsed > timeOut) netErrorIdx = ERR_INET_TIMEOUT; else netErrorIdx = 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); deleteIOsession(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 + bytesSend, len - bytesSend, NO_FLAGS_SET)) == SOCKET_ERROR) return(SOCKET_ERROR); bytesSend += n; } return(bytesSend); } /*********************** error handling ***************************************/ char * netErrorMsg[] = { "No error", "Cannot open socket", "DNS resolution failed", "Not a valid service", "Connection failed", "Accept failed", "Connection closed", "Connection broken", "Socket recv failed", "Socket send failed", "Cannot bind socket", "Too many sockets", "Listen failed", "Badly formed IP", "Select failed", "Peek failed", "Not a valid socket", "Cannot block or unblock socket", "Operation timed out", /* for nl-web.c */ "HTTP bad formed URL", "HTTP file operation failed", "HTTP transfer failed", "HTTP invalid response from server", "HTTP no response from server", "HTTP not content", "HTTP error in header", "HTTP error in chunked format" }; CELL * netError(int errorNo) { netErrorIdx = errorNo; return(nilCell); } CELL * netEvalError(int errorNo) { netErrorIdx = errorNo; return(p_netLastError(nilCell)); } CELL * p_netLastError(CELL * params) { CELL * result; char str[40]; UINT numError = netErrorIdx; if(params != nilCell) getInteger(params, &numError); else if(numError == 0) return(nilCell); result = makeCell(CELL_EXPRESSION, (UINT)stuffInteger(numError)); snprintf(str, 40, "%s", (numError > MAX_NET_ERROR) ? UNKNOWN_ERROR : netErrorMsg[numError]); ((CELL *)result->contents)->next = stuffString(str); return(result); } #ifndef NO_NET_PING /* net-ping Undocumented boolean flag parameter (after count) puts error info into the result list when a packet could not be sent. */ CELL * p_netPing(CELL * params) { CELL * address; UINT maxwait = 1000, listmode = 0; UINT flag = 0; UINT count = 0; params = getEvalDefault(params, &address); 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)); if(params != nilCell) { params = getInteger(params, &maxwait); if(params != nilCell) params = getInteger(params, &count); flag = getFlag(params); } return(ping(address, (int)maxwait, (int)listmode, (int)count, (int)flag)); } #define PLEN 64 CELL * ping(CELL * address, int maxwait, int listmode, int maxCount, int flag) { char * host; char * ptr; char * hostaddr = NULL; unsigned char packet[PLEN]; struct ip *ip; char IPaddress[STRADDR_LEN]; struct icmp6_filter filter; struct icmp6_hdr *icp6 = (struct icmp6_hdr *) packet; struct icmp *icp = (struct icmp *) packet; struct sockaddr * whereto; struct sockaddr * from; int s; int sockopt = 1; #ifdef TRU64 unsigned long sockaddr_len; #else #ifdef OS2 int sockaddr_len; #else socklen_t sockaddr_len; #endif #endif int broadcast = 0; int size, ipNo, startIp = 0, endIp = 0; int timeout = 0, tdiff; int sendCount = 0, receiveCount = 0; ssize_t len; struct timeval tv, tp; CELL * result = NULL; CELL * link = NULL; char buff[64]; #ifdef MAC_OSX /* no superuser rights necessary */ if ((s = socket(ADDR_FAMILY, SOCK_DGRAM, ICMP_TYPE)) < 0) #else if ((s = socket(ADDR_FAMILY, SOCK_RAW, ICMP_TYPE)) < 0) #endif return(netError(ERR_INET_OPEN_SOCKET)); sockaddr_len = (ADDR_FAMILY == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); whereto = alloca(sockaddr_len); from = alloca(sockaddr_len); if(ADDR_FAMILY == AF_INET6) { ICMP6_FILTER_SETPASSALL (&filter); setsockopt (s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof (filter)); } 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(ADDR_FAMILY == AF_INET) /* IPv4 */ { 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; if(endIp > 254) endIp = 254; } } } /* 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; /* target host address info */ /* printf("->%s\n", hostaddr); */ memset((char *)whereto, 0, sockaddr_len); #ifdef MAC_OSX if(getHostAddr(whereto, SOCK_DGRAM, hostaddr) != 0) #else if(getHostAddr(whereto, SOCK_RAW, hostaddr) != 0) #endif { shutdown(s, SHUT_RDWR); return(netError(ERR_INET_HOST_UNKNOWN)); } if(ADDR_FAMILY == AF_INET6) setSockaddrPort(whereto, htons(IPPROTO_ICMPV6)); else if((broadcast = (strncmp(host + len - 4, ".255", 4) == 0))) setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) &sockopt, sizeof(sockopt)); /* ping setup ICMP packet */ if(ADDR_FAMILY == AF_INET6) { memset(icp6, 0, PLEN); icp6->icmp6_type = ICMP6_ECHO_REQUEST; icp6->icmp6_id = getpid() & 0xFFFF; gettimeofday((struct timeval *)&icp6->icmp6_data8[4], NULL); } else { memset(icp, 0, PLEN); icp->icmp_type = ICMP_ECHO; icp->icmp_id = getpid() & 0xFFFF; gettimeofday((struct timeval *)&icp[1], NULL); icp->icmp_cksum = in_cksum((unsigned short *) icp, PLEN ); } while(wait_ready(s, 10000, READY_WRITE) <= 0) { gettimeofday(&tp, NULL); if((timeout = (timediff_ms(tp, tv) > maxwait))) break; continue; } /* ping */ size = sendto(s, packet, PLEN, 0,(struct sockaddr *)whereto, sockaddr_len); if(size != PLEN) { if(flag) { snprintf(buff, 64, "%s", strerror(errno)); ipstrFromSockAddr((struct sockaddr *)whereto, IPaddress, STRADDR_LEN); link = addResult(&result, link, makePair(stuffString(IPaddress), stuffString(buff))); } if( !(listmode || startIp) ) break; continue; } sendCount++; } if(!listmode) break; address = address->next; } /* wait for response(s) */ if(maxCount == 0) maxCount = sendCount; while(sendCount) { if(wait_ready(s, 1000, READY_READ) <= 0) { gettimeofday(&tp, NULL); if((timeout = (timediff_ms(tp, tv) > maxwait))) break; continue; } memset(packet, 0, PLEN); memset(from, 0, sockaddr_len); if ( (len = recvfrom(s, packet, PLEN, 0, (struct sockaddr *)from, (socklen_t *)&sockaddr_len)) < 0) continue; ip = (struct ip *) packet; gettimeofday(&tp, NULL); if(ADDR_FAMILY == AF_INET6) { icp6 = (struct icmp6_hdr *)packet; if(icp6->icmp6_type != ICMP6_ECHO_REPLY) continue; if(icp6->icmp6_id != (getpid() & 0xFFFF)) continue; tdiff = timediff64_us(tp, *(struct timeval *)&icp6->icmp6_data8[4]); } else { icp = (struct icmp *)(packet + (ip->ip_hl << 2)); if(icp->icmp_id != (getpid() & 0xFFFF)) continue; tdiff = timediff64_us(tp, *(struct timeval *)&icp[1]); } ipstrFromSockAddr((struct sockaddr *)from, IPaddress, STRADDR_LEN); link = addResult(&result, link, makePair(stuffString(IPaddress), stuffInteger(tdiff))); if(++receiveCount == maxCount) break; if( !(broadcast || listmode || startIp) ) break; } shutdown(s, SHUT_RDWR); if(timeout) netErrorIdx = ERR_INET_TIMEOUT; else netErrorIdx = 0; return(result == NULL ? getCell(CELL_EXPRESSION) : result); } CELL * addResult(CELL * * result, CELL * cell, CELL * new) { if(*result == NULL) *result = makeCell(CELL_EXPRESSION, (UINT)new); else cell->next = new; return(new); } CELL * makePair(CELL * left, CELL * right) { left->next = right; return(makeCell(CELL_EXPRESSION, (UINT)left)); } unsigned short 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 /* NO_NET_PING */ /* check socket for readability or error return 0 if the time limit expires or -1 on error tie limit in wait given in micro-secs */ int wait_ready(int sock, INT64 wait_us, int mode) { struct timeval timeOut; fd_set socketSet; FD_ZERO(&socketSet); FD_SET(sock, &socketSet); timeOut.tv_sec = wait_us/1000000; timeOut.tv_usec = wait_us - timeOut.tv_sec * 1000000; if(mode == READY_READ) return(select(sock + 1, &socketSet, NULL, NULL, &timeOut)); else return(select(sock + 1, NULL, &socketSet, NULL, &timeOut)); } /* ------------------------- raw sockets --------------------------------------- (net-packet packet) Checksums are only gnerated if set to zero in the packet. Packets start with the ip header followed by either a TCP, UDP or ICMP header and data. */ /* for checksum calculation in TCP packets */ struct pseudohdr { struct in_addr source_addr; struct in_addr dest_addr; unsigned char dummy; unsigned char protocol; unsigned short len; }; #ifndef NO_NET_PACKET unsigned short pseudo_chks( struct ip * iph, char * packet, char * header, int data_offset); /* (net-packet str-packeA [int-config-flags]) */ CELL * p_netPacket(CELL * params) { char * packet; int sock; int one = 1; struct sockaddr_in dest_sin; size_t size; ssize_t bytesSent; struct ip * iph; struct tcphdr * tcph; struct udphdr * udph; struct icmp * icmph; int dport; /* ip_len and ip_off must be check-summed in network order (big-Endian), but one or both must be given to the sendto() in host byte-order on some OS. As of 10.2.6 the following configFLags work for Mac OS X on PPC and Intel, Linux on Intel and OpenBSD on Intel. Curently net-packet is IPv4 only. */ #if defined(MAC_OSX) UINT configFlags = 3; /* ntohs() for both ip_len and ip_off */ #elif defined(LINUX) UINT configFlags = 1; /* ntohs() for ip_len only */ #else /* works for OpenBSD */ UINT configFlags = 0; /* none */ #endif params = getStringSize(params, &packet, &size, TRUE); /* undocumented user-supplied extra configFlags parameter can have a value of 0,1,2,3 */ if(params != nilCell) getInteger(params, &configFlags); iph = (struct ip *)packet; if(iph->ip_sum == 0) iph->ip_sum = in_cksum((unsigned short *)iph, sizeof(struct ip)); switch(iph->ip_p) { case IPPROTO_TCP: tcph = (struct tcphdr *)(packet + sizeof(struct ip)); #ifdef ANDROID dport = tcph->dest; if(tcph->check == 0) tcph->check = pseudo_chks(iph, packet, (char *) tcph, tcph->doff * 4); #else dport = tcph->th_dport; if(tcph->th_sum == 0) tcph->th_sum = pseudo_chks(iph, packet, (char *) tcph, tcph->th_off * 4); #endif break; case IPPROTO_UDP: udph = (struct udphdr *)(packet + sizeof(struct ip)); dport = udph->uh_dport; if(udph->uh_sum == 0) udph->uh_sum = pseudo_chks(iph, packet, (char *) udph, sizeof(struct udphdr)); break; case IPPROTO_ICMP: icmph = (struct icmp *)(packet + sizeof(struct ip)); dport = 0; if(icmph->icmp_cksum == 0) icmph->icmp_cksum = in_cksum((unsigned short *)icmph, ntohs(iph->ip_len) - iph->ip_hl * 4); break; default: return(nilCell); } if(configFlags & 0x1) iph->ip_len = ntohs(iph->ip_len); if(configFlags & 0x2) iph->ip_off = ntohs(iph->ip_off); memset(&dest_sin, 0, sizeof(dest_sin)); dest_sin.sin_family = AF_INET; dest_sin.sin_port = dport; dest_sin.sin_addr = iph->ip_dst; if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) return(netError(ERR_INET_OPEN_SOCKET)); if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) return(netError(ERR_INET_OPEN_SOCKET)); if((bytesSent = sendto(sock, packet, size, NO_FLAGS_SET, (struct sockaddr *)&dest_sin, sizeof(dest_sin))) != size) return(nilCell); return(stuffInteger(bytesSent)); } unsigned short pseudo_chks( struct ip * iph, char * packet, char * header, int data_offset) { /* packet was give with ip_len in net-work byte order */ unsigned short checksum; int segment_len = ntohs(iph->ip_len) - iph->ip_hl * 4; int data_len = segment_len - data_offset; struct pseudohdr * pseudoh = alloca(sizeof(struct pseudohdr) + segment_len); pseudoh->source_addr = iph->ip_src; pseudoh->dest_addr = iph->ip_dst; pseudoh->dummy = 0; pseudoh->protocol = iph->ip_p; pseudoh->len = htons(segment_len); data_len = segment_len - data_offset; memcpy((char *)pseudoh + sizeof(struct pseudohdr), header, data_offset); memcpy((char *)pseudoh + sizeof(struct pseudohdr) + data_offset, packet + iph->ip_hl * 4 + data_offset, data_len); checksum = in_cksum((unsigned short *)pseudoh, sizeof(struct pseudohdr) + segment_len); #ifdef DEBUG printf("segment_len:%d\n", segment_len); printf("data_offset:%d\n", data_offset); printf("data_len:%d\n", data_len); printf("checksum:%x\n", ntohs(checksum)); #endif return(checksum); } #endif /* NO_NET_PACKET */ /* ------------------ socket->filestream stuff for windows ------------------------*/ #ifdef WINDOWS extern int IOchannelIsSocketStream; /* 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. Sinc 10.0.1 the IOchannelIsSocketStream flag is used to identify IOchannel as a fake file struct and extract the socket. Following win_fxxx routines are used to define fopen(), fclose(), fprintf(), fgetc() and fgets() in some *.c */ FILE * win_fdopen(int handle, const char * mode) { FILE * fPtr; if((fPtr = (FILE *)malloc(sizeof(FILE))) == NULL) return(NULL); memset(fPtr, 0, sizeof(FILE)); fPtr->_file = handle; fPtr->_flag = 0xFFFF; return(fPtr); } int win_fclose(FILE * fPtr) { if(IOchannelIsSocketStream) return(close(getSocket(fPtr))); return(fclose(fPtr)); } int win_fprintf(FILE * fPtr, char * notused, char * buffer) { int pSize; if(!IOchannelIsSocketStream) return(fprintf(fPtr, buffer)); pSize = strlen(buffer); if((pSize = sendall(getSocket(fPtr), buffer, pSize)) == SOCKET_ERROR) { close(getSocket(fPtr)); return(-1); } return(pSize); } int win_fgetc(FILE * fPtr) { char chr; if(!IOchannelIsSocketStream) return(fgetc(fPtr)); if(recv(getSocket(fPtr), &chr, 1, NO_FLAGS_SET) <= 0) { close(getSocket(fPtr)); return(-1); } return(chr); } char * win_fgets(char * buffer, int size, FILE * fPtr) { int bytesReceived = 0; char chr; if(!IOchannelIsSocketStream) 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 /* WINDOWS */ #else /* for EMSCRIPTEN define dummy writeLog() */ void writeLog(char * text, int newLine) { return; } #endif /* ifndef EMSCRIPTEN */ /* eof */