Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

sockutils.c

Go to the documentation of this file.
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.