/****************************************************************************** * $Id: altsocklib.c,v 1.15 2006/02/23 19:54:44 gareuselesinge Exp $ * This file is part of liberopops (http://liberopops.sf.net) * * This file is distributed under the terms of GNU GPL license. * ******************************************************************************/ /****************************************************************************** * File description: * Win32-POSIX socket compatibility layer * Notes: * The user should use the upper layer socketcommon.h * Authors: * Gerald Dueck * Andrew Lynch * Enrico Tassi * Change History: * 07sep97: created * 24Sep97: added sockinfo, senddata, recvdata * Jan-Feb00: modified for NT/UNIX transparency * Sep02: Andrew Lynch added timeout option to recvstring * Oct03: Enrico Tassi removed sock_listen (implemented in the upper layer * socketcommon.[ch]) * Jan04: Enrico Tassi More fix (EINTR prone), restylings, .... * ******************************************************************************/ #if defined(WIN32) && !defined(CYGWIN) #include #include #include #include #include #include #include #include #include #define CHECK_RC(rc) (__extension__(\ {\ long int __result=0;\ if(rc == -1)\ {\ SAY("%s : %s : %d : (%d)\n",\ __FILE__,__FUNCTION__,__LINE__,WSAGetLastError());\ __result = 1;\ }\ __result;\ })) #else /* #ifndef WIN32 */ #include #ifndef FREEBSD #include #endif #include #include #include #include #include #include #include #include #include /* on some libc there is no _r support */ #if !defined(strerror_r) || defined(OSXSTC) # include pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER; # define strerror_r(a,b,c) {\ pthread_mutex_lock(&strerror_lock);\ snprintf(b,c,"%s",strerror(a));\ pthread_mutex_unlock(&strerror_lock);\ } #endif /* ndef strerror_r */ /* macro to check result of send() */ #ifndef OSXSTC #define CHECK_RC(rc) (__extension__(\ {\ long int __result=0;\ if(rc == -1)\ {\ char buff[100];\ strerror_r(errno,buff,100);\ SAY("%s : %s : %d : (%d)%s\n",\ __FILE__,__FUNCTION__,__LINE__,errno,buff);\ __result = 1;\ }\ __result;\ })) #else #define CHECK_RC(rc) (__extension__(\ {\ long int __result=0;\ if(rc == -1)\ {\ char buff[100];\ pthread_mutex_lock(&strerror_lock);\ snprintf(buff,100,"%s",strerror(errno));\ pthread_mutex_unlock(&strerror_lock);\ SAY("%s : %s : %d : (%d)%s\n",\ __FILE__,__FUNCTION__,__LINE__,errno,buff);\ __result = 1;\ }\ __result;\ })) #endif /* ndef OSXSTC */ #endif /* ndef WIN32 */ /* unistd GNU macro to avoid EINTR */ #if defined(WIN32) && !defined(CYGWIN) #define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && WSAGetLastError() == WSAEINTR); \ __result; })) #else #define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && errno == EINTR); \ __result; })) #endif #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif #include "altsocklib.h" #include "beos_compatibility.h" #include "log.h" #define LOG_ZONE "ALTSOCKLIB" #if defined(WIN32) && !defined(CYGWIN) static struct { int errorNumber; char *errorString; } sockerrlist [] = { {10013, "Permission denied"}, {10048, "Address already in use"}, {10049, "Cannot assign requested address"}, {10047, "Address family not supported by protocol family"}, {10037, "Operation already in progress"}, {10053, "Software caused connection abort"}, {10061, "Connection refused"}, {10054, "Connection reset by peer"}, {10039, "Destination address required"}, {10014, "Bad address"}, {10064, "Host is down"}, {10065, "No route to host"}, {10036, "Operation now in progress"}, {10004, "Interrupted function call"}, {10022, "Invalid argument"}, {10056, "Socket is already connected"}, {10024, "Too many open files"}, {10040, "Message too long"}, {10050, "Network is down"}, {10052, "Network dropped connection on reset"}, {10051, "Network is unreachable"}, {10055, "No buffer space available"}, {10042, "Bad protocol option"}, {10057, "Socket is not connected"}, {10038, "Socket operation on non-socket"}, {10045, "Operation not supported"}, {10046, "Protocol family not supported"}, {10067, "Too many processes"}, {10043, "Protocol not supported"}, {10041, "Protocol wrong type for socket"}, {10058, "Cannot send after socket shutdown"}, {10044, "Socket type not supported"}, {10060, "Connection timed out"}, {10109, "Class type not found"}, {10035, "Resource temporarily unavailable"}, {11001, "Host not found"}, {10093, "Successful WSAStartup not yet performed"}, {11004, "Valid name; no data record of requested type"}, {11003, "Non-recoverable error"}, {10091, "Network subsystem is unavailable"}, {11002, "Non-authoritative host not found"}, {10092, "WINSOCK.DLL version out of range"}, {10094, "Graceful shutdown in progress"}, {0, NULL} }; #endif void sockerror(char *msg) { #if defined(WIN32) && !defined(CYGWIN) int err = WSAGetLastError(); int i; for (i = 0; sockerrlist[i].errorNumber; i++) if (sockerrlist[i].errorNumber == err) { fprintf(stderr, "%s: (%d) %s\n", msg, err, sockerrlist[i].errorString); SAY("%s: (%d) %s\n", msg, err, sockerrlist[i].errorString); return; } #else SAY("'%s' : ",msg); CHECK_RC(-1); #endif } /* === HERE WE HAVE THE REAL CODE =========================================== */ /********************************************************* * * gethostbyname thread safe implementation ipv4 only * probably endian dependent (win runs on big endians?) * */ #if !(defined(WIN32) || defined(CYGWIN)) static unsigned int gethostbyname_thsafe (char *host) { int res; unsigned int dst; struct addrinfo hint, *addr; memset(&hint, 0, sizeof(hint)); hint.ai_family = PF_INET; res = getaddrinfo(host, NULL, &hint, &addr); /* Check for errors. */ if (res) return 0xffffffff; dst = ((struct sockaddr_in *)(addr->ai_addr))->sin_addr.s_addr; freeaddrinfo(addr); return dst; } #else static pthread_mutex_t mutex_for_gethostbyname = PTHREAD_MUTEX_INITIALIZER; static unsigned int gethostbyname_thsafe (char *host) { struct hostent *hp; unsigned int h; pthread_mutex_lock(&mutex_for_gethostbyname); hp = gethostbyname(host); if(hp != NULL) h = ((struct in_addr *) (hp->h_addr))->s_addr; else h = 0xffffffff; pthread_mutex_unlock(&mutex_for_gethostbyname); return h; } #endif /******************************************** * * win32 needs to be initialized even before * calling gethostbyname(), so we call this * */ #if defined(WIN32) && !defined(CYGWIN) void sockinit() { static int started = 0; if (!started) { short wVersionRequested = 0x101; WSADATA wsaData; if (WSAStartup( wVersionRequested, &wsaData ) == -1) { sockerror("sockopen"); exit(0); } if (wsaData.wVersion != 0x101) { fprintf(stderr, "Incorrect winsock version\n"); exit(0); } started = 1; } } #endif /********************************************************* * This is a dual purpose routine. * - if host is not NULL, connect as a client to the given * host and port. * - if host is NULL, bind a socket to the given port. * In either case, return a valid socket or -1. */ int sockopen(char *host, struct in_addr bind_add, unsigned short port) { int sd; struct sockaddr_in sin; unsigned int add; #if defined( WIN32) && !defined(CYGWIN) sockinit(); #endif /* get an internet domain socket */ if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1; /* complete the socket structure */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); if (host != NULL) { /* get the IP address of the requested host */ if ((add = gethostbyname_thsafe(host)) == 0xffffffff) return -1; sin.sin_addr.s_addr = add; //FIX memory leak for hp if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) == -1) return -1; } else { /* server */ int one = 1; /* avoid "bind: socket already in use" msg */ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) < 0) return -1; /* let you bind different from 0.0.0.0 */ sin.sin_addr.s_addr = bind_add.s_addr; /* bind the socket to the port number */ if (bind(sd, (struct sockaddr *) &sin, sizeof(sin)) == -1) return -1; } return sd; } /******************************************** * * get some info aboute the socket * * */ int sockinfo(int sd, unsigned char *info) { struct sockaddr_in sin; #if !defined(WIN32) && !defined(BEOS) && !defined(OSXSTC) socklen_t len = sizeof(sin); #else int len = sizeof(sin); #endif memset(&sin, 0, sizeof(sin)); if (getsockname(sd, (struct sockaddr *) &sin, &len) == -1) { sockerror("sockinfo"); return -1; } memcpy(info, &sin.sin_addr, 4); memcpy(info + 4, &sin.sin_port, 2); return 0; } /*************************************************** * * pretty close the socket truncating connections * */ int sockclose(int sock) { #if defined(WIN32) && !defined(CYGWIN) shutdown(sock, SD_BOTH); return closesocket(sock); #else shutdown(sock,SHUT_RDWR); return close(sock); #endif } /****************************************************************** * senddata * - appends before transmitting data through socket. * - is O(n) where n is sizeo of stream * - is not SIG_PIPE prone * bugs: * - WIN32 implementation should be rewritten using WSASend */ int senddata(int socket, char *buffer, int length) { #if !(defined(WIN32) && !defined(CYGWIN)) static char crlf[] = "\r\n"; int rc; for(rc=0;rc < length;) { rc = TEMP_FAILURE_RETRY( send(socket,&buffer[rc],length-rc,MSG_NOSIGNAL)); if(CHECK_RC(rc)) return -1; } for(rc=0;rc < 2;) { rc = TEMP_FAILURE_RETRY(send(socket,&crlf[rc],2-rc,MSG_NOSIGNAL)); if(CHECK_RC(rc)) return -3; } return length; #endif #if defined(WIN32) && !defined(CYGWIN) char *buffer2 = (char *)malloc(length+2); int rc; memcpy(buffer2, buffer, length); memcpy(buffer2+length, "\r\n", 2); for(rc=0;rc < length;) { rc = TEMP_FAILURE_RETRY(send(socket, buffer2, length+2-rc, 0)); if ( rc < 0) length -= 2; } free(buffer2); return length; #endif } /*********************************************************************** * senddata_raw * - NOT appends before transmitting data through socket. * - is O(n) where n is sizeo of stream * - is not SIG_PIPE prone * bugs: * - WIN32 implementation should be rewritten using WSASend */ int senddata_raw(int socket,const char *buffer, int length) { #if !(defined(WIN32) && !defined(CYGWIN)) int rc; for(rc=0;rc < length;) { rc = TEMP_FAILURE_RETRY( send(socket,&buffer[rc],length-rc,MSG_NOSIGNAL)); if(CHECK_RC(rc)) return -1; } return length; #endif #if defined(WIN32) && !defined(CYGWIN) int rc; for(rc=0;rc < length;) { rc = TEMP_FAILURE_RETRY(send(socket, buffer, length-rc, 0)); if(CHECK_RC(rc)) return -1; } return length; #endif } /*************************************************************************** * recvdata * - extracts -delimited record from socket stream * - works with fragmented socket stream * - fragments returned records correctly if sent size > maxsize * - works correctly with any size recvbuffer * - is O(n) where n = sizeof stream * bugs: * - uses static state information and hence must always be * called with the same socket. If called with two different sockets, * call with second socket may return data left over from call with * first socket. */ int recvdata(int socket, char *buffer, int maxsize) { static char recvbuffer[1024]; static char *bufp = recvbuffer, *bufe = recvbuffer; char *dst = buffer; static enum { stData, stCr, stCopy } state = stData; while (dst - buffer < maxsize) { if (bufp == bufe) { int length = recv(socket, recvbuffer, sizeof(recvbuffer), 0); if (length < 0) { if (dst - buffer > 0) return dst - buffer; else return length; } bufp = recvbuffer; bufe = recvbuffer + length; if (length == 0) { *dst= 0; break; } } switch (state) { case stData: if (*bufp == '\r') { state = stCr; bufp++; } else *dst++ = *bufp++; break; case stCr: if (*bufp == '\n') { state = stData; bufp++; return dst - buffer; } else { state = stCopy; *dst++ = '\r'; } break; case stCopy: *dst = *bufp++; state = stData; break; } } buffer[maxsize-1] = '\0'; return maxsize; } /************************************************************************* * recvstring * - like recvdata but without the bugs. * - requires an initialized recvbuffer_t. * - extracts -delimited record from socket stream * - works with fragmented socket stream * - fragments returned records correctly if sent size > maxsize * - works correctly with any size recvbuffer * - is O(n) where n = sizeof stream */ recvbuffer_t *recvBufferCreate(int size) { recvbuffer_t *rb = (recvbuffer_t *) malloc (sizeof(recvbuffer_t)); rb->recvbuffer = (char *)malloc(size); rb->bufp = rb->recvbuffer; rb->bufe = rb->recvbuffer; rb->size = size; return rb; } void recvBufferDestroy(recvbuffer_t *rb) { free(rb->recvbuffer); free(rb); } int recvstring(int socket, char *buffer, int maxsize, recvbuffer_t *rb) { char *dst = buffer; static enum { stData, stCr, stCopy } state = stData; while (dst - buffer < maxsize - 1) { if (rb->bufp == rb->bufe) { int length; length = recv(socket, rb->recvbuffer, rb->size, MSG_NOSIGNAL); if (length < 0) { if (dst - buffer > 0) { *dst = '\0'; return dst - buffer; } else { buffer[0] = '\0'; return length; } } rb->bufp = rb->recvbuffer; rb->bufe = rb->recvbuffer + length; if (length == 0) { *dst = '\0'; break; } } switch (state) { case stData: if (*rb->bufp == '\r') { state = stCr; rb->bufp++; } else *dst++ = *rb->bufp++; break; case stCr: if (*rb->bufp == '\n') { state = stData; rb->bufp++; *dst = '\0'; return dst - buffer; } else { state = stCopy; *dst++ = '\r'; } break; case stCopy: *dst = *rb->bufp++; state = stData; break; } } buffer[maxsize-1] = '\0'; return maxsize; } int recvstring_with_timeout(int socket, char *buffer, int maxsize, recvbuffer_t *rb, int timeout) { char *dst = buffer; static enum { stData, stCr, stCopy } state = stData; while (dst - buffer < maxsize - 1) { if (rb->bufp == rb->bufe) { int length; fd_set fds; int n; struct timeval tv; // set up the file descriptor set FD_ZERO(&fds); FD_SET(socket, &fds); // set up the struct timeval for the timeout tv.tv_sec = timeout; tv.tv_usec = 0; // wait until timeout or data received n = select(socket+1, &fds, NULL, NULL, &tv); if (n == 0) return -2; // timeout! if (n == -1) return -1; // error // data must be here, so do a normal recv() length = recv(socket, rb->recvbuffer, rb->size, MSG_NOSIGNAL); if (length < 0) { if (dst - buffer > 0) { *dst = '\0'; return dst - buffer; } else { buffer[0] = '\0'; return length; } } rb->bufp = rb->recvbuffer; rb->bufe = rb->recvbuffer + length; if (length == 0) { *dst = '\0'; break; } } switch (state) { case stData: if (*rb->bufp == '\r') { state = stCr; rb->bufp++; } else *dst++ = *rb->bufp++; break; case stCr: if (*rb->bufp == '\n') { state = stData; rb->bufp++; *dst = '\0'; return dst - buffer; } else { state = stCopy; *dst++ = '\r'; } break; case stCopy: *dst = *rb->bufp++; state = stData; break; } } buffer[maxsize-1] = '\0'; return maxsize; } int sendstring(int socket, char *string) { return senddata(socket, string, strlen(string)); } int sendstring_raw(int socket, const char *string) { return senddata_raw(socket, string, strlen(string)); }