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 
00035 #include "sockutils.h"
00036 #include <string.h> // for strerror
00037 #include <errno.h>  // for the errno variable
00038 #include <stdio.h>  // for the stderr file
00039 
00054 // WinSock Initialization
00055 #ifdef WIN32
00056     #define WINSOCK_MAJOR_VERSION 2     
00057     #define WINSOCK_MINOR_VERSION 2     
00058     int sockcount= 0;                   
00059 #endif
00060 
00061 // Some minor differences between UNIX and Win32
00062 #ifdef WIN32
00063     #define SHUT_WR SD_SEND         
00064     #define snprintf _snprintf      
00065 #endif
00066 
00067 
00068 
00069 /****************************************************
00070  *                                                  *
00071  * Locally defined functions                        *
00072  *                                                  *
00073  ****************************************************/
00074 
00075 int sock_ismcastaddr(const struct sockaddr *saddr);
00076 
00077 
00078 
00079 /*
00080     \brief Global variable; needed to keep the message due to an error that we want to discard.
00081     
00082     This can happen, for instance, because we already have an error message and we want to keep 
00083     the first one.
00084 */
00085 char fakeerrbuf[SOCK_ERRBUF_SIZE + 1];
00086 
00087 
00088 
00089 
00090 
00091 /****************************************************
00092  *                                                  *
00093  * Function bodies                                 *
00094  *                                                  *
00095  ****************************************************/
00096 
00097 
00116 void sock_geterror(const char *caller, char *string, int size)
00117 {
00118 #ifdef WIN32
00119     int retval;
00120     int code;
00121     char message[SOCK_ERRBUF_SIZE];
00122     
00123         code= GetLastError();
00124     
00125         retval= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
00126                       FORMAT_MESSAGE_MAX_WIDTH_MASK,
00127                       NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00128                       (LPSTR) message, SOCK_ERRBUF_SIZE, NULL);
00129     
00130         if (retval == 0)
00131         {
00132             snprintf(string, size, "%sUnable to get the exact error message", caller);
00133             return;
00134         }
00135     
00136         snprintf(string, size, "%s%s (code %d)", caller, message, code);
00137 
00138 #else
00139     char *message;
00140     
00141         message= strerror(errno);
00142         snprintf(string, size, "%s%s (code %d)", caller, message, errno);
00143 #endif
00144 }
00145 
00146 
00147 
00160 int sock_init(char *errbuf)
00161 {
00162 #ifdef WIN32
00163     if (sockcount == 0)
00164     {
00165     WSADATA wsaData;            // helper variable needed to initialize Winsock
00166 
00167         // Ask for Winsock version 2.2.
00168         if ( WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
00169         {
00170             snprintf(errbuf, SOCK_ERRBUF_SIZE, "Failed to initialize Winsock\n");
00171             WSACleanup();
00172             return -1;
00173         }
00174     }
00175 
00176     sockcount++;
00177 #endif
00178 
00179     return 0;
00180 }
00181 
00182 
00183 
00192 void sock_cleanup()
00193 {
00194 #ifdef WIN32
00195     sockcount--;
00196     if (sockcount == 0)
00197         WSACleanup();
00198 #endif
00199 }
00200 
00201 
00202 
00208 int sock_ismcastaddr(const struct sockaddr *saddr)
00209 {
00210     if (saddr->sa_family == PF_INET)
00211     {
00212         struct sockaddr_in *saddr4 = (struct sockaddr_in *) saddr;
00213         if (IN_MULTICAST(ntohl(saddr4->sin_addr.s_addr))) return 0;
00214         else return -1;
00215     }
00216     else
00217     {
00218         struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) saddr;
00219         if (IN6_IS_ADDR_MULTICAST(&saddr6->sin6_addr)) return 0;
00220         else return -1;
00221     }
00222 }
00223 
00224 
00225 
00254 int sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf)
00255 {
00256 SOCKET sock;
00257 
00258     sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
00259     if (sock == -1)
00260     {
00261         sock_geterror("socket(): ", errbuf, SOCK_ERRBUF_SIZE);
00262         return -1;
00263     }
00264 
00265 
00266     // This is a server socket
00267     if (server)
00268     {
00269 #ifdef BSD
00270         // Force the use of IPv6-only addresses; in BSD you can accept both v4 and v6
00271         // connections if you have a "NULL" pointer as the nodename in the getaddrinfo()
00272         // This behaviour is not clear in the RFC 2553, so each system implements the
00273         // bind() differently from this point of view
00274 
00275         if (addrinfo->ai_family == PF_INET6)
00276         {
00277         int on;
00278 
00279             if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (int)) == -1)
00280             {
00281                 snprintf(errbuf, SOCK_ERRBUF_SIZE, "setsockopt(IPV6_BINDV6ONLY)");
00282                 return -1;
00283             }
00284         } 
00285 #endif
00286 
00287         // WARNING: if the address is a mcast one, I should place the proper Win32 code here
00288         if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
00289         {
00290             sock_geterror("bind(): ", errbuf, SOCK_ERRBUF_SIZE);
00291             return -1;
00292         }
00293 
00294         if (addrinfo->ai_socktype == SOCK_STREAM)
00295             if (listen(sock, nconn) == -1)
00296             {
00297                 sock_geterror("listen(): ", errbuf, SOCK_ERRBUF_SIZE);
00298                 return -1;
00299             }
00300 
00301         // server side ended
00302         return sock;
00303     }
00304     else    // we're the client
00305     {
00306         if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1)
00307         {
00308             sock_geterror("Is libpcap/WinPcap properly installed on the other host? connect() failed: ", errbuf, SOCK_ERRBUF_SIZE);
00309             closesocket(sock);
00310             return -1;
00311         }
00312 
00313         return sock;
00314     }
00315 }
00316 
00317 
00318 
00319 
00334 int sock_close(SOCKET sock, char *errbuf)
00335 {
00336     // SHUT_WR: subsequent calls to the send function are disallowed. 
00337     // For TCP sockets, a FIN will be sent after all data is sent and 
00338     // acknowledged by the Server.
00339     if (shutdown(sock, SHUT_WR) )
00340     {
00341         sock_geterror("shutdown(): ", errbuf, SOCK_ERRBUF_SIZE);
00342         // close the socket anyway
00343         closesocket(sock);
00344         return -1;
00345     }
00346 
00347     closesocket(sock);
00348     return 0;
00349 }
00350 
00351 
00352 
00353 
00354 
00355 
00386 int sock_validaddr(const char *address, const char *port,
00387                             struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf)
00388 {
00389 int retval;
00390     
00391     retval = getaddrinfo(address, port, hints, addrinfo);
00392     if (retval != 0)
00393     {
00394         // if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
00395         // error routines (WSAGetLastError() in Win32 anderrono in UNIX)
00396         snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval));
00397         return -1;
00398     }
00404     // This software only supports PF_INET and PF_INET6.
00405     if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6))
00406     {
00407         snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo(): socket type not supported");
00408         return -1;
00409     }
00410 
00411     if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) )
00412     {
00413         snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
00414         return -1;
00415     }
00416 
00417     return 0;
00418 }
00419 
00420 
00421 
00443 int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf)
00444 {
00445 int nsent;
00446 
00447 send:
00448 #ifdef linux
00449 /*
00450     Another pain... in Linux there's this flag
00451     MSG_NOSIGNAL
00452         Requests not to send SIGPIPE on  errors  on  stream
00453         oriented sockets when the other end breaks the con­
00454         nection. The EPIPE error is still returned.
00455 */
00456     nsent = send(socket, buffer, size, MSG_NOSIGNAL);
00457 #else
00458     nsent = send(socket, buffer, size, 0);
00459 #endif
00460 
00461     if (nsent == -1)
00462     {
00463         sock_geterror("send(): ", errbuf, SOCK_ERRBUF_SIZE);
00464         return -1;
00465     }
00466 
00467     if (nsent != size)
00468     {
00469         size-= nsent;
00470         buffer+= nsent;
00471         goto send;
00472     }
00473 
00474     return 0;
00475 }
00476 
00477 
00524 int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf)
00525 {
00526 
00527     if ((*offset + size) > totsize)
00528     {
00529         snprintf(errbuf, SOCK_ERRBUF_SIZE, "Not enough space in the temporary send buffer.");
00530         return -1;
00531     };
00532 
00533     if (!checkonly)
00534         memcpy(tempbuf + (*offset), buffer, size);
00535 
00536     (*offset)+= size;
00537 
00538     return 0;
00539 }
00540 
00541 
00542 
00566 int sock_recv(SOCKET sock, char *buffer, int size, char *errbuf)
00567 {
00568 int nread;
00569 int totread= 0;
00570     // We can obtain the same result using the MSG_WAITALL flag
00571     // However, this is not supported by recv() in Win32
00572 
00573     if (size == 0)
00574     {
00575         SOCK_ASSERT("I have been requested to read zero bytes", 1);
00576         return 0;
00577     }
00578 
00579 again:
00580     nread= recv(sock, &(buffer[totread]), size - totread, 0);
00581 
00582     if (nread == -1)
00583     {
00584         sock_geterror("recv(): ", errbuf, SOCK_ERRBUF_SIZE);
00585         return -1;
00586     }
00587 
00588     if (nread == 0)
00589     {
00590         snprintf(errbuf, SOCK_ERRBUF_SIZE, "The other host terminated the connection.");
00591         return -1;
00592     }
00593 
00594     totread+= nread;
00595 
00596     if (totread != size)
00597         goto again;
00598 
00599     return totread;
00600 }
00601 
00602 
00603 
00635 int sock_recv_dgram(SOCKET sock, char *buffer, int size, char *errbuf)
00636 {
00637 int nread;
00638 
00639     nread= recv(sock, buffer, size, 0);
00640 
00641     if (nread == -1)
00642     {
00643         sock_geterror("recv(): ", errbuf, SOCK_ERRBUF_SIZE);
00644         return -1;
00645     }
00646 
00647     return nread;
00648 }
00649 
00650 
00672 int sock_discard(SOCKET sock, int size, char *errbuf)
00673 {
00674 #define TEMP_BUF_SIZE 65536
00675 
00676 char buffer[TEMP_BUF_SIZE];     // network buffer, to be used when the message is discarded
00677 
00678     // A static allocation avoids the need of a 'malloc()' each time we want to discard a message
00679     // Our feeling is that a buffer if 65536 is enough for mot of the application;
00680     // in case this is not enough, the "while" loop discards the message by calling the 
00681     // sockrecv() several times.
00682 
00683     while (size > TEMP_BUF_SIZE)
00684     {
00685         if (sock_recv(sock, buffer, TEMP_BUF_SIZE, errbuf) == -1)
00686             return -1;
00687         size-= TEMP_BUF_SIZE;
00688     }
00689 
00690     // If there is still data to be discarded
00691     // In this case, the data can fit into the temporaty buffer
00692     if (size)
00693     {
00694         if (sock_recv(sock, buffer, size, errbuf) == -1)
00695             return -1;
00696     }
00697 
00698     SOCK_ASSERT("I'm currently discarding data\n", 1);
00699 
00700     return 0;
00701 }
00702 
00703 
00704 
00726 int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf)
00727 {
00728     // checks if the connecting host is among the ones allowed
00729     if (hostlist[0])
00730     {
00731     char *token;                    // temp, needed to separate items into the hostlist
00732     struct addrinfo *addrinfo, *ai_next;
00733     char *temphostlist;
00734 
00735         temphostlist= (char *) malloc (strlen(hostlist) + 1);
00736         if (temphostlist == NULL)
00737         {
00738             sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, SOCK_ERRBUF_SIZE);
00739             return -1;
00740         }
00741         
00742         // The problem is that strtok modifies the original variable by putting '0' at the end of each token
00743         // So, we have to create a new temporary string in which the original content is kept
00744         strcpy(temphostlist, hostlist);
00745 
00746         token= strtok(temphostlist, sep);
00747 
00748         while( token != NULL )
00749         {
00750         struct addrinfo hints;
00751         int retval;
00752 
00753             addrinfo = NULL;
00754             memset(&hints, 0, sizeof (struct addrinfo) );
00755             hints.ai_family = PF_UNSPEC;
00756             hints.ai_socktype= SOCK_STREAM;
00757     
00758             retval = getaddrinfo(token, "0", &hints, &addrinfo);
00759             if (retval != 0)
00760             {
00761                 snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval));
00762                 SOCK_ASSERT(errbuf, 1);
00763 
00764                 // Get next token
00765                 token = strtok( NULL, sep);
00766                 continue;
00767             }
00768 
00769             // ai_next is required to preserve the content of addrinfo, in order to deallocate it properly
00770             ai_next= addrinfo;
00771             while(ai_next)
00772             {
00773                 if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0)
00774                 {
00775                     free(temphostlist);
00776                     return 0;
00777                 }
00778 
00779                 // If we are here, it means that the current address does not matches
00780                 // Let's try with the next one in the header chain
00781                 ai_next= ai_next->ai_next;
00782             }
00783 
00784             freeaddrinfo(addrinfo);
00785             addrinfo= NULL;
00786             // Get next token
00787             token = strtok( NULL, sep);
00788         }
00789 
00790         if (addrinfo)
00791         {
00792             freeaddrinfo(addrinfo);
00793             addrinfo= NULL;
00794         }
00795 
00796         snprintf(errbuf, SOCK_ERRBUF_SIZE, "The host is not in the allowed host list. Connection refused.");
00797         free(temphostlist);
00798         return -1;
00799     }
00800 
00801     return 0;
00802 }
00803 
00804 
00823 int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second)
00824 {
00825     if (first->ss_family == second->ss_family)
00826     {
00827         if (first->ss_family == AF_INET)
00828         {
00829             if (memcmp(     &(((struct sockaddr_in *) first)->sin_addr), 
00830                             &(((struct sockaddr_in *) second)->sin_addr),
00831                             sizeof(struct in_addr) ) == 0)
00832                                 return 0;
00833         }
00834         else // address family is AF_INET6
00835         {
00836             if (memcmp(     &(((struct sockaddr_in6 *) first)->sin6_addr), 
00837                             &(((struct sockaddr_in6 *) second)->sin6_addr),
00838                             sizeof(struct in6_addr) ) == 0)
00839                                 return 0;
00840         }
00841     }
00842 
00843     return -1;
00844 }
00845 

documentation. Copyright (c) 2002-2003 Politecnico di Torino. All rights reserved.