00001 /* 00002 * Copyright (c) 2002 - 2003 00003 * NetGroup, Politecnico di Torino (Italy) 00004 * All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 00010 * 1. Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * 2. Redistributions in binary form must reproduce the above copyright 00013 * notice, this list of conditions and the following disclaimer in the 00014 * documentation and/or other materials provided with the distribution. 00015 * 3. Neither the name of the Politecnico di Torino nor the names of its 00016 * contributors may be used to endorse or promote products derived from 00017 * this software without specific prior written permission. 00018 * 00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00020 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00021 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00022 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00023 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00024 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00025 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00026 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00027 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00029 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00030 * 00031 */ 00032 00033 00034 00048 #include "sockutils.h" 00049 #include <string.h> // for strerror 00050 #include <errno.h> // for the errno variable 00051 #include <stdio.h> // for the stderr file 00052 00053 00054 00055 00056 00057 00058 // WinSock Initialization 00059 #ifdef WIN32 00060 #define WINSOCK_MAJOR_VERSION 2 00061 #define WINSOCK_MINOR_VERSION 2 00062 int sockcount= 0; 00063 #endif 00064 00065 // Some minor differences between UNIX and Win32 00066 #ifdef WIN32 00067 #define SHUT_WR SD_SEND 00068 #define snprintf _snprintf 00069 #endif 00070 00071 00073 #define SOCK_ERRBUF_SIZE 1024 00074 00075 00076 // Constants; used in order to keep strings here 00077 #define SOCKET_NO_NAME_AVAILABLE "No name available" 00078 #define SOCKET_NO_PORT_AVAILABLE "No port available" 00079 #define SOCKET_NAME_NULL_DAD "Null address (DAD Phase)" 00080 00081 00082 00083 00084 /**************************************************** 00085 * * 00086 * Locally defined functions * 00087 * * 00088 ****************************************************/ 00089 00090 int sock_ismcastaddr(const struct sockaddr *saddr); 00091 00092 00093 00094 00095 00096 /**************************************************** 00097 * * 00098 * Function bodies * 00099 * * 00100 ****************************************************/ 00101 00102 00123 void sock_geterror(const char *caller, char *errbuf, int errbuflen) 00124 { 00125 #ifdef WIN32 00126 int retval; 00127 int code; 00128 TCHAR message[SOCK_ERRBUF_SIZE]; /* It will be char (if we're using ascii) or wchar_t (if we're using unicode) */ 00129 00130 code= GetLastError(); 00131 00132 retval= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | 00133 FORMAT_MESSAGE_MAX_WIDTH_MASK, 00134 NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 00135 message, sizeof(message) / sizeof(TCHAR), NULL); 00136 00137 if (retval == 0) 00138 { 00139 if (errbuf) 00140 { 00141 snprintf(errbuf, errbuflen, "%sUnable to get the exact error message", caller); 00142 errbuf[errbuflen - 1]= 0; 00143 } 00144 00145 return; 00146 } 00147 00148 if (errbuf) 00149 { 00150 snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, code); 00151 errbuf[errbuflen - 1]= 0; 00152 } 00153 00154 00155 #else 00156 char *message; 00157 00158 message= strerror(errno); 00159 00160 if (errbuf) 00161 { 00162 snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errno); 00163 errbuf[errbuflen - 1]= 0; 00164 } 00165 00166 #endif 00167 } 00168 00169 00170 00187 int sock_init(char *errbuf, int errbuflen) 00188 { 00189 #ifdef WIN32 00190 if (sockcount == 0) 00191 { 00192 WSADATA wsaData; // helper variable needed to initialize Winsock 00193 00194 // Ask for Winsock version 2.2. 00195 if ( WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 00196 { 00197 if (errbuf) 00198 { 00199 snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n"); 00200 errbuf[errbuflen - 1]= 0; 00201 } 00202 00203 WSACleanup(); 00204 00205 return -1; 00206 } 00207 } 00208 00209 sockcount++; 00210 #endif 00211 00212 return 0; 00213 } 00214 00215 00216 00225 void sock_cleanup() 00226 { 00227 #ifdef WIN32 00228 sockcount--; 00229 00230 if (sockcount == 0) 00231 WSACleanup(); 00232 #endif 00233 } 00234 00235 00236 00242 int sock_ismcastaddr(const struct sockaddr *saddr) 00243 { 00244 if (saddr->sa_family == PF_INET) 00245 { 00246 struct sockaddr_in *saddr4 = (struct sockaddr_in *) saddr; 00247 if (IN_MULTICAST(ntohl(saddr4->sin_addr.s_addr))) return 0; 00248 else return -1; 00249 } 00250 else 00251 { 00252 struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) saddr; 00253 if (IN6_IS_ADDR_MULTICAST(&saddr6->sin6_addr)) return 0; 00254 else return -1; 00255 } 00256 } 00257 00258 00259 00291 SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen) 00292 { 00293 SOCKET sock; 00294 00295 sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); 00296 if (sock == -1) 00297 { 00298 sock_geterror("socket(): ", errbuf, errbuflen); 00299 return -1; 00300 } 00301 00302 00303 // This is a server socket 00304 if (server) 00305 { 00306 #ifdef BSD 00307 // Force the use of IPv6-only addresses; in BSD you can accept both v4 and v6 00308 // connections if you have a "NULL" pointer as the nodename in the getaddrinfo() 00309 // This behaviour is not clear in the RFC 2553, so each system implements the 00310 // bind() differently from this point of view 00311 00312 if (addrinfo->ai_family == PF_INET6) 00313 { 00314 int on; 00315 00316 if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (int)) == -1) 00317 { 00318 if (errbuf) 00319 { 00320 snprintf(errbuf, errbuflen, "setsockopt(IPV6_BINDV6ONLY)"); 00321 errbuf[errbuflen - 1]= 0; 00322 } 00323 return -1; 00324 } 00325 } 00326 #endif 00327 00328 // WARNING: if the address is a mcast one, I should place the proper Win32 code here 00329 if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) 00330 { 00331 sock_geterror("bind(): ", errbuf, errbuflen); 00332 return -1; 00333 } 00334 00335 if (addrinfo->ai_socktype == SOCK_STREAM) 00336 if (listen(sock, nconn) == -1) 00337 { 00338 sock_geterror("listen(): ", errbuf, errbuflen); 00339 return -1; 00340 } 00341 00342 // server side ended 00343 return sock; 00344 } 00345 else // we're the client 00346 { 00347 struct addrinfo *tempaddrinfo; 00348 00349 tempaddrinfo= addrinfo; 00350 00351 // We have to loop though all the addinfo returned. 00352 // For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying 00353 // to connect to is unavailable in IPv6, so we have to try in IPv4 as well 00354 while (tempaddrinfo) 00355 { 00356 if (connect(sock, tempaddrinfo->ai_addr, tempaddrinfo->ai_addrlen) == -1) 00357 tempaddrinfo= tempaddrinfo->ai_next; 00358 else 00359 break; 00360 } 00361 00362 // Check how we exit from the previous loop 00363 // If tempaddrinfo is equal to NULL, it means that all the connect() failed. 00364 if (tempaddrinfo == NULL) 00365 { 00366 sock_geterror("Is the server properly installed on the other host? connect() failed: ", errbuf, errbuflen); 00367 closesocket(sock); 00368 return -1; 00369 } 00370 else 00371 return sock; 00372 } 00373 } 00374 00375 00376 00377 00396 int sock_close(SOCKET sock, char *errbuf, int errbuflen) 00397 { 00398 // SHUT_WR: subsequent calls to the send function are disallowed. 00399 // For TCP sockets, a FIN will be sent after all data is sent and 00400 // acknowledged by the Server. 00401 if (shutdown(sock, SHUT_WR) ) 00402 { 00403 sock_geterror("shutdown(): ", errbuf, errbuflen); 00404 // close the socket anyway 00405 closesocket(sock); 00406 return -1; 00407 } 00408 00409 closesocket(sock); 00410 return 0; 00411 } 00412 00413 00414 00415 00416 00417 00456 int sock_initaddress(const char *address, const char *port, 00457 struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen) 00458 { 00459 int retval; 00460 00461 retval = getaddrinfo(address, port, hints, addrinfo); 00462 if (retval != 0) 00463 { 00464 // if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard 00465 // error routines (errno) in UNIX; WIN32 suggests using the GetLastError() instead. 00466 if (errbuf) 00467 #ifdef WIN32 00468 sock_geterror("getaddrinfo(): ", errbuf, errbuflen); 00469 #else 00470 if (errbuf) 00471 { 00472 snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval)); 00473 errbuf[errbuflen - 1]= 0; 00474 } 00475 #endif 00476 return -1; 00477 } 00483 // This software only supports PF_INET and PF_INET6. 00484 if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6)) 00485 { 00486 if (errbuf) 00487 { 00488 snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported"); 00489 errbuf[errbuflen - 1]= 0; 00490 } 00491 return -1; 00492 } 00493 00494 if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) ) 00495 { 00496 if (errbuf) 00497 { 00498 snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams"); 00499 errbuf[errbuflen - 1]= 0; 00500 } 00501 00502 return -1; 00503 } 00504 00505 return 0; 00506 } 00507 00508 00509 00535 int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen) 00536 { 00537 int nsent; 00538 00539 send: 00540 #ifdef linux 00541 /* 00542 Another pain... in Linux there's this flag 00543 MSG_NOSIGNAL 00544 Requests not to send SIGPIPE on errors on stream-oriented 00545 sockets when the other end breaks the connection. 00546 The EPIPE error is still returned. 00547 */ 00548 nsent = send(socket, buffer, size, MSG_NOSIGNAL); 00549 #else 00550 nsent = send(socket, buffer, size, 0); 00551 #endif 00552 00553 if (nsent == -1) 00554 { 00555 sock_geterror("send(): ", errbuf, errbuflen); 00556 return -1; 00557 } 00558 00559 if (nsent != size) 00560 { 00561 size-= nsent; 00562 buffer+= nsent; 00563 goto send; 00564 } 00565 00566 return 0; 00567 } 00568 00569 00625 int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen) 00626 { 00627 00628 if ((*offset + size) > totsize) 00629 { 00630 if (errbuf) 00631 { 00632 snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer."); 00633 errbuf[errbuflen - 1]= 0; 00634 } 00635 00636 return -1; 00637 }; 00638 00639 if (!checkonly) 00640 memcpy(tempbuf + (*offset), buffer, size); 00641 00642 (*offset)+= size; 00643 00644 return 0; 00645 } 00646 00647 00648 00684 int sock_recv(SOCKET sock, char *buffer, int size, int receiveall, char *errbuf, int errbuflen) 00685 { 00686 int nread; 00687 int totread= 0; 00688 // We can obtain the same result using the MSG_WAITALL flag 00689 // However, this is not supported by recv() in Win32 00690 00691 if (size == 0) 00692 { 00693 SOCK_ASSERT("I have been requested to read zero bytes", 1); 00694 return 0; 00695 } 00696 00697 again: 00698 nread= recv(sock, &(buffer[totread]), size - totread, 0); 00699 00700 if (nread == -1) 00701 { 00702 sock_geterror("recv(): ", errbuf, errbuflen); 00703 return -1; 00704 } 00705 00706 if (nread == 0) 00707 { 00708 if (errbuf) 00709 { 00710 snprintf(errbuf, errbuflen, "The other host terminated the connection."); 00711 errbuf[errbuflen - 1]= 0; 00712 } 00713 00714 return -1; 00715 } 00716 00717 // If we want to return as soon as some data has been received, 00718 // let's do the job 00719 if (!receiveall) 00720 return nread; 00721 00722 totread+= nread; 00723 00724 if (totread != size) 00725 goto again; 00726 00727 return totread; 00728 } 00729 00730 00731 00767 /* 00768 int sock_recv(SOCKET sock, char *buffer, int size, char *errbuf, int errbuflen) 00769 { 00770 int nread; 00771 00772 nread= recv(sock, buffer, size, 0); 00773 00774 if (nread == -1) 00775 { 00776 sock_geterror("recv(): ", errbuf, errbuflen); 00777 return -1; 00778 } 00779 00780 if (nread == 0) 00781 { 00782 if (errbuf) 00783 { 00784 snprintf(errbuf, errbuflen, "The other host terminated the connection."); 00785 errbuf[errbuflen - 1]= 0; 00786 } 00787 00788 return -1; 00789 } 00790 00791 return nread; 00792 } 00793 */ 00794 00795 00821 int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) 00822 { 00823 #define TEMP_BUF_SIZE 65536 00824 00825 char buffer[TEMP_BUF_SIZE]; // network buffer, to be used when the message is discarded 00826 00827 // A static allocation avoids the need of a 'malloc()' each time we want to discard a message 00828 // Our feeling is that a buffer if 65536 is enough for mot of the application; 00829 // in case this is not enough, the "while" loop discards the message by calling the 00830 // sockrecv() several times. 00831 00832 while (size > TEMP_BUF_SIZE) 00833 { 00834 if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) 00835 return -1; 00836 00837 size-= TEMP_BUF_SIZE; 00838 } 00839 00840 // If there is still data to be discarded 00841 // In this case, the data can fit into the temporaty buffer 00842 if (size) 00843 { 00844 if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) 00845 return -1; 00846 } 00847 00848 SOCK_ASSERT("I'm currently discarding data\n", 1); 00849 00850 return 0; 00851 } 00852 00853 00854 00883 int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen) 00884 { 00885 // checks if the connecting host is among the ones allowed 00886 if ( (hostlist) && (hostlist[0]) ) 00887 { 00888 char *token; // temp, needed to separate items into the hostlist 00889 struct addrinfo *addrinfo, *ai_next; 00890 char *temphostlist; 00891 00892 temphostlist= (char *) malloc (strlen(hostlist) + 1); 00893 if (temphostlist == NULL) 00894 { 00895 sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen); 00896 return -2; 00897 } 00898 00899 // The problem is that strtok modifies the original variable by putting '0' at the end of each token 00900 // So, we have to create a new temporary string in which the original content is kept 00901 strcpy(temphostlist, hostlist); 00902 00903 token= strtok(temphostlist, sep); 00904 00905 while( token != NULL ) 00906 { 00907 struct addrinfo hints; 00908 int retval; 00909 00910 addrinfo = NULL; 00911 memset(&hints, 0, sizeof (struct addrinfo) ); 00912 hints.ai_family = PF_UNSPEC; 00913 hints.ai_socktype= SOCK_STREAM; 00914 00915 retval = getaddrinfo(token, "0", &hints, &addrinfo); 00916 if (retval != 0) 00917 { 00918 if (errbuf) 00919 { 00920 snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval)); 00921 errbuf[errbuflen - 1]= 0; 00922 } 00923 00924 SOCK_ASSERT(errbuf, 1); 00925 00926 // Get next token 00927 token = strtok( NULL, sep); 00928 continue; 00929 } 00930 00931 // ai_next is required to preserve the content of addrinfo, in order to deallocate it properly 00932 ai_next= addrinfo; 00933 while(ai_next) 00934 { 00935 if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0) 00936 { 00937 free(temphostlist); 00938 return 0; 00939 } 00940 00941 // If we are here, it means that the current address does not matches 00942 // Let's try with the next one in the header chain 00943 ai_next= ai_next->ai_next; 00944 } 00945 00946 freeaddrinfo(addrinfo); 00947 addrinfo= NULL; 00948 00949 // Get next token 00950 token = strtok( NULL, sep); 00951 } 00952 00953 if (addrinfo) 00954 { 00955 freeaddrinfo(addrinfo); 00956 addrinfo= NULL; 00957 } 00958 00959 if (errbuf) 00960 { 00961 snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused."); 00962 errbuf[errbuflen - 1]= 0; 00963 } 00964 00965 free(temphostlist); 00966 return -1; 00967 } 00968 00969 // No hostlist, so we have to return 'empty list' 00970 return 1; 00971 } 00972 00973 00992 int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second) 00993 { 00994 if (first->ss_family == second->ss_family) 00995 { 00996 if (first->ss_family == AF_INET) 00997 { 00998 if (memcmp( &(((struct sockaddr_in *) first)->sin_addr), 00999 &(((struct sockaddr_in *) second)->sin_addr), 01000 sizeof(struct in_addr) ) == 0) 01001 return 0; 01002 } 01003 else // address family is AF_INET6 01004 { 01005 if (memcmp( &(((struct sockaddr_in6 *) first)->sin6_addr), 01006 &(((struct sockaddr_in6 *) second)->sin6_addr), 01007 sizeof(struct in6_addr) ) == 0) 01008 return 0; 01009 } 01010 } 01011 01012 return -1; 01013 } 01014 01015 01016 01065 int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen) 01066 { 01067 socklen_t sockaddrlen; 01068 int retval; // Variable that keeps the return value; 01069 01070 retval= -1; 01071 01072 #ifdef WIN32 01073 if (sockaddr->ss_family == AF_INET) 01074 sockaddrlen = sizeof(struct sockaddr_in); 01075 else 01076 sockaddrlen = sizeof(struct sockaddr_in6); 01077 #else 01078 sockaddrlen = sizeof(struct sockaddr_storage); 01079 #endif 01080 01081 /* TEMP 01082 if ( (SockAddr->ss_family == AF_INET6) && 01083 (memcmp( &((sockaddr_in6 *) SockAddr)->sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(struct in6_addr) ) == 0) ) 01084 { 01085 strncpy(Name, SOCKET_NAME_NULL_DAD, NameLen); 01086 return -1; 01087 } 01088 */ 01089 01090 if ( getnameinfo((struct sockaddr *) sockaddr, sockaddrlen, address, addrlen, port, portlen, flags) != 0) 01091 { 01092 // If the user wants to receive an error message 01093 if (errbuf) 01094 { 01095 sock_geterror("getnameinfo(): ", errbuf, errbuflen); 01096 errbuf[errbuflen-1]= 0; 01097 } 01098 01099 if (address) 01100 { 01101 strncpy(address, SOCKET_NO_NAME_AVAILABLE, addrlen); 01102 address[addrlen-1]= 0; 01103 } 01104 01105 if (port) 01106 { 01107 strncpy(port, SOCKET_NO_PORT_AVAILABLE, portlen); 01108 port[portlen-1]= 0; 01109 } 01110 01111 retval= 0; 01112 } 01113 01114 return retval; 01115 } 01116 01117 01118 01150 int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, char *errbuf, int errbuflen) 01151 { 01152 int retval; 01153 struct addrinfo *addrinfo; 01154 01155 if ( (retval= sock_initaddress(address, "22222" /* fake port */, NULL, &addrinfo, errbuf, errbuflen)) == -1 ) 01156 return retval; 01157 01158 if (addrinfo->ai_family == PF_INET) 01159 memcpy(sockaddr, addrinfo->ai_addr, sizeof(struct sockaddr_in) ); 01160 else 01161 memcpy(sockaddr, addrinfo->ai_addr, sizeof(struct sockaddr_in6) ); 01162 01163 if (addrinfo->ai_next != NULL) 01164 { 01165 freeaddrinfo(addrinfo); 01166 01167 if (errbuf) 01168 { 01169 snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned"); 01170 errbuf[errbuflen - 1]= 0; 01171 } 01172 01173 return -2; 01174 } 01175 01176 freeaddrinfo(addrinfo); 01177 return -1; 01178 } 01179 01180
documentation. Copyright (c) 2002-2003 Politecnico di Torino. All rights reserved.