Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals  

sockets.c

Go to the documentation of this file.
00001 /*! \file sockets.c
00002     This file implements low level network functionalities.
00003  */
00004 
00005 #include "sockets.h"
00006 
00007 static char *get_doted_ip (unsigned long int ip);
00008 
00009 /*! \brief Maximum number of characters for the error message.
00010  */
00011 
00012 #define SOCKET_ERROR_SIZE  4096
00013 
00014 /*! \brief Maximum number of network interfaces fot the host.
00015  */
00016 
00017 #define MAX_INTERFACE      16
00018 
00019 /*! \brief Buffer that will contain the last error message.
00020  */
00021 
00022 static char last_error[SOCKET_ERROR_SIZE];
00023 
00024 /*! \brief Returns a pointer to the last error message.
00025     \author Denis BEURIVE
00026     \return The function returns a pointer to a zero terminated string of characters that represents
00027             the last error message.
00028  */
00029 
00030 char *get_last_socket_error() { return last_error; }
00031 
00032 /*! \brief Fake logging service.
00033     \author Denis BEURIVE
00034  */
00035 
00036 static int my_fake_syslog (const char *file, const char * fmt,...) { return 0; }
00037 
00038 
00039 /*! \brief Create a UDP socket and bind it to a given port and a given network interface.
00040            Note that this socket is allowed to send broadcast messages.
00041     \author Denis BEURIVE
00042     \param port Port number: the socket is binded to this port.
00043     \param add Pointer to a sockaddr_in data structure that will be used to store the internet address
00044            of the created socket.
00045     \param host Pointer to a zero terminated string of characters that represents the host name
00046            (or IP address) of the network interface to bind with the socket.
00047     \param interface Pointer to a buffer used to store the name of the network interface.
00048            This buffer MUST be at least IFNAMSIZ bytes long.
00049     \param len Size, in bytes, of the buffer 'buff'.
00050     \param broadcast Pointer to a 4 bytes long integer used to store the binary representation of the IP
00051            address associated with broadcasts (in network byte order) - for the interface.
00052     \param netmask Pointer to a 4 bytes long integer used to store the binary representation of the netmask
00053            (in network byte order) - for the interface.
00054     \param myip  Pointer to a 4 bytes long integer used to store the binary representation of the IP address
00055            associated with the interace.
00056     \param syslog Pointer to a function used to log messages. The function signature must be :<P>
00057            int syslog (const char *file, const char * fmt,...)<P>
00058            Upon successful completion, the function returns the value 0.<P>
00059            To desactivate logging, just set this pointer to NULL.<P>
00060            If NULL, logging service is not activated (no matter the value of 'log').
00061     \param log Pointer to the log file.
00062            If NULL, logging service is not activated (no matter the value of 'syslog').
00063     \return Upon successful completion, the function returns the newly created socket descriptor.
00064             Otherwise, the function returns the value -1.
00065  */
00066 
00067 int create_socket (
00068                      int port,
00069                      struct sockaddr_in *add,
00070                      char *host,
00071                      char *interface,
00072                      int len,
00073                      unsigned long int *broadcast,
00074                      unsigned long int *netmask,
00075                      unsigned long int *myip,
00076                      int (*syslog)(const char *file, const char * fmt,...),
00077                      char *log
00078                   )
00079 {
00080   int                desc, longueur, on, i, number_of_interfaces, length;
00081   struct ifreq       *ifr, if_req;
00082   struct hostent     *h;
00083   struct ifconf      if_conf;
00084   unsigned long int  ip, fip;
00085   char               *buffer, interfaces_names[MAX_INTERFACE][IFNAMSIZ];
00086   char               *log_file;
00087   int                (*my_syslog) (const char *file, const char * fmt,...);
00088 
00089 
00090 
00091   if ((syslog != NULL) && (log != NULL))
00092   {
00093      my_syslog = syslog; 
00094      log_file  = log;
00095   }
00096   else { my_syslog = my_fake_syslog; }
00097 
00098   memset ((void*)last_error, 0, SOCKET_ERROR_SIZE);
00099   for (i=0; i<MAX_INTERFACE; i++) { memset ((void*)&(interfaces_names[i][0]), 0, IFNAMSIZ); }
00100   *myip = 0;
00101 
00102   /* ------------------------------------------------------- */
00103   /* Sanity check                                            */
00104   /* ------------------------------------------------------- */
00105 
00106   if (len < IFNAMSIZ)
00107   {
00108     snprintf (last_error, SOCKET_ERROR_SIZE - 1, 
00109               "Invalid lenght for interface's buffer (segmentation fault avoided!)");
00110     return -1;
00111   }
00112 
00113   memset ((void*)interface, 0, len);
00114 
00115   /* ------------------------------------------------------- */
00116   /* Get the IP address of the host 'host'                   */
00117   /* ------------------------------------------------------- */
00118 
00119   h = gethostbyname(host);
00120 
00121   if (h == NULL)
00122   {
00123     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "System call gethostbyname(%s) failed - %s)",
00124               host,
00125               strerror(errno));
00126     return -1;
00127   }
00128 
00129   if (h->h_length != 4)
00130   {
00131     snprintf (last_error, SOCKET_ERROR_SIZE - 1, 
00132               "The size of the Internet address returned by gethostbyname(%s) - %d - is not valid",
00133               host,
00134               h->h_length);
00135     return -1;
00136   }
00137 
00138   memcpy ((void*)&ip, h->h_addr_list[0], h->h_length);
00139 
00140   my_syslog (log_file, "[INFO] [%s:%d] IP address of host %s is %s",
00141              __FILE__, __LINE__,
00142              host,
00143              get_doted_ip(ip));
00144 
00145   *myip = ip;
00146 
00147   /* ------------------------------------------------------- */
00148   /* Create the UDP socket                                   */
00149   /* ------------------------------------------------------- */
00150 
00151   if ((desc = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
00152   {
00153     snprintf (last_error, SOCKET_ERROR_SIZE - 1, 
00154               "System call socket(INET, SOCK_DGRAM, IPPROTO_UDP) failed - %s", strerror(errno));
00155     return -1;
00156   }
00157 
00158   /* ------------------------------------------------------- */
00159   /* Get the list of all interfaces available                */
00160   /* ------------------------------------------------------- */
00161 
00162   memset ((void*)&if_conf, 0, sizeof(struct ifconf));
00163 
00164   buffer = (char*)malloc(MAX_INTERFACE * sizeof(struct ifreq));
00165   if (buffer == NULL)
00166   {
00167     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "Could not allocate memory (this should never happen)");
00168     return -1;
00169   }
00170 
00171   if_conf.ifc_len = MAX_INTERFACE * sizeof(struct ifreq);
00172   if_conf.ifc_buf = buffer;
00173 
00174   if (ioctl(desc, SIOCGIFCONF, &if_conf) == -1)
00175   {
00176     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "System call ioctl(SIOCGIFCONF) failed - %s", strerror(errno));
00177     free (buffer);
00178     return -1;
00179   }
00180 
00181   /* ------------------------------------------------------- */
00182   /* Get the list of all interfaces names                    */
00183   /* ------------------------------------------------------- */
00184 
00185   number_of_interfaces = 0;
00186   for (i=0; i<(if_conf.ifc_len)/sizeof(struct ifreq); i++)
00187   {  
00188     ifr = ((struct ifreq*)buffer) + i;  
00189 
00190     /* ----------------------------------------------------- */
00191     /* Skip interfaces that are not connected to the network */
00192     /* ----------------------------------------------------- */
00193 
00194     if (ifr->ifr_addr.sa_family != AF_INET) { continue; }
00195 
00196     /* ----------------------------------------------------- */
00197     /* Get the name                                          */
00198     /* ----------------------------------------------------- */
00199 
00200     memcpy (interfaces_names[number_of_interfaces++], ifr->ifr_name, IFNAMSIZ);
00201   }
00202 
00203   free (buffer);
00204 
00205   /* ------------------------------------------------------- */
00206   /* Find the interface with the wanted IP address           */
00207   /* ------------------------------------------------------- */
00208 
00209   for (i=0; i<number_of_interfaces; i++)
00210   {
00211      /* ---------------------------------------------------- */
00212      /* Get the IP address of the specified interface        */
00213      /* Note: See file /usr/include/if.h for details about   */
00214      /*       the 'ifreq' structure.                         */
00215      /*       See 'man ioctl_list' for the list of all       */
00216      /*       posible interface's actions.                   */
00217      /* ----------------------------------------------------- */
00218    
00219      memset ((void*)&if_req, 0, sizeof(struct ifreq));
00220      strcpy (if_req.ifr_name, interfaces_names[i]);
00221    
00222      if (ioctl(desc, SIOCGIFADDR, &if_req) == -1)
00223      {
00224        snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00225                  "System call ioctl(SIOCGIFADDR) failed - %s", strerror(errno));
00226        return -1;
00227      }
00228    
00229      fip = ((struct sockaddr_in*)(&(if_req.ifr_addr)))->sin_addr.s_addr;
00230 
00231      if (fip == ip) { strcpy (interface, interfaces_names[i]); }
00232    
00233      my_syslog (log_file, "[INFO] [%s:%d] Found interface %s (%s)",
00234                 __FILE__, __LINE__,
00235                 interfaces_names[i],
00236                 get_doted_ip(fip));
00237   } 
00238 
00239   /* ------------------------------------------------------- */
00240   /* Make sure that we found the interface                   */
00241   /* ------------------------------------------------------- */
00242  
00243   if (interface[0] == 0)
00244   {
00245     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "No interface found for alias %s (%s)", host, get_doted_ip(ip));
00246     return -1;
00247   } 
00248  
00249   /* ------------------------------------------------------- */
00250   /* Get the network mask (for information)                  */
00251   /* ------------------------------------------------------- */
00252 
00253   memset ((void*)&if_req, 0, sizeof(struct ifreq));
00254   strcpy (if_req.ifr_name, interface);
00255 
00256   if (ioctl(desc, SIOCGIFNETMASK, &if_req) == -1)
00257   {
00258     snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00259               "System call ioctl(SIOCGIFNETMASK) failed - %s", strerror(errno));
00260     return -1;
00261   }
00262 
00263   *netmask = ((struct sockaddr_in*)(&(if_req.ifr_netmask)))->sin_addr.s_addr;
00264 
00265   my_syslog (log_file, "[INFO] [%s:%d] Netmask assigned to %s is: %s",
00266              __FILE__, __LINE__,
00267              interface,
00268              get_doted_ip(*netmask));
00269 
00270   /* ------------------------------------------------------- */
00271   /* Get broadcast address (for information)                 */
00272   /* ------------------------------------------------------- */
00273 
00274   memset ((void*)&if_req, 0, sizeof(struct ifreq));
00275   strcpy (if_req.ifr_name, interface);
00276 
00277   if (ioctl(desc, SIOCGIFBRDADDR, &if_req) == -1)
00278   {
00279     snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00280               "System call ioctl(SIOCGIFNETMASK) failed - %s", strerror(errno));
00281     return -1;
00282   }
00283 
00284   *broadcast = ((struct sockaddr_in*)(&(if_req.ifr_broadaddr)))->sin_addr.s_addr;
00285 
00286   my_syslog (log_file, "[INFO] [%s:%d] Broadcast address assigned to %s is: %s",
00287              __FILE__, __LINE__,
00288              interface,
00289              get_doted_ip(*broadcast));
00290 
00291   /* ------------------------------------------------------- */
00292   /* make the socket quickly reusable                        */
00293   /* ------------------------------------------------------- */
00294 
00295   on = 1;
00296   if (setsockopt(desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
00297   {
00298     snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00299               "System call setsockopt(SO_REUSEADDR) failed - %s", strerror(errno));
00300     return -1;
00301   }
00302 
00303   /* ------------------------------------------------------- */
00304   /* Bind the socket to the given port                       */
00305   /*                                                         */
00306   /* Important note: Don't try to bind the socket to any     */
00307   /* specific IP address. You must bind it to 'INADDR_ANY'.  */
00308   /* The value 'INADDR_ANY' means "any IP address associated */
00309   /* with the host". This includes:                          */
00310   /* (1) All IP addresses of all network interfaces.         */
00311   /* (2) The broadcast address.                              */
00312   /* (3) Any assigned multicast address.                     */
00313   /*                                                         */
00314   /* If you bind the socket to a specific IP address (for    */
00315   /* example 10.33.33.2), then the socket can receive pac-   */
00316   /* -kets which destination IP address is 10.33.33.2. All   */
00317   /* other destination IP address are ignored (the broadcast */
00318   /* IP address included!). This means that you can not re-  */
00319   /* -ceive broadcasts.                                      */
00320   /*                                                         */
00321   /* In order to associate a specific network interface to   */
00322   /* the socket, we use the socket's option SO_BINDTODEVICE. */
00323   /* The combination (INADDR_ANY, SO_BINDTODEVICE/eth0)      */
00324   /* means "any UP address associated to the interface eth0, */
00325   /* and only eth0. This includes the broadcast address and  */
00326   /* any multicast addresses.                                */
00327   /* ------------------------------------------------------- */
00328 
00329   longueur = (int)sizeof (struct sockaddr_in);
00330 
00331   memset ((void*)add, 0, longueur);
00332   add->sin_family      = AF_INET; 
00333   add->sin_port        = htons((unsigned short)port); 
00334   add->sin_addr.s_addr = INADDR_ANY;
00335 
00336   if (bind(desc, (struct sockaddr*)add, longueur) == -1)
00337   {
00338     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "System call bind() failed - %s", strerror(errno));
00339     return -1;
00340   }
00341 
00342   /* ------------------------------------------------------- */
00343   /* "Link" the socket to a given interface                  */
00344   /* ------------------------------------------------------- */
00345 
00346   if (setsockopt(desc, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface)+1) < 0)
00347   {
00348     snprintf (last_error, SOCKET_ERROR_SIZE - 1, "System call setsockopt(SO_BINDTODEVICE, %s) failed - %s",
00349               interface,
00350               strerror(errno));
00351     return -1;
00352   }
00353 
00354   my_syslog (log_file,
00355              "[INFO] [%s:%d] Binding server to IP address %s - (%s) on port %d - %s (%s)",
00356              __FILE__, __LINE__,
00357              get_doted_ip(add->sin_addr.s_addr),
00358              interface,
00359              port,
00360              host,
00361              get_doted_ip(ip));
00362 
00363   /* ------------------------------------------------------- */
00364   /* Allow socket to receive / send broadcast messages       */
00365   /* ------------------------------------------------------- */
00366 
00367   on = 1;
00368   if (setsockopt(desc, SOL_SOCKET, SO_BROADCAST, &on, sizeof(int)) < 0)
00369   {
00370     snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00371               "System call setsockopt(SO_BROADCAST) failed - %s", strerror(errno));
00372     return -1;
00373   }
00374 
00375   /* ------------------------------------------------------- */
00376   /* Make sure that we can handle broadcasts                 */
00377   /* ------------------------------------------------------- */
00378  
00379   on = 0;
00380   length = sizeof(int);
00381   if (getsockopt(desc, SOL_SOCKET, SO_BROADCAST, &on, &length) < 0)
00382   {
00383     snprintf (last_error, SOCKET_ERROR_SIZE - 1,
00384               "System call getsockopt(SO_BROADCAST) failed - %s", strerror(errno));
00385     return -1;
00386   }
00387 
00388   if (on == 1)
00389   {
00390     my_syslog (log_file, "[INFO] [%s:%d] Broadcast is activated", __FILE__, __LINE__);
00391   }
00392   else
00393   {
00394     my_syslog (log_file, "[INFO] [%s:%d] Broadcast is __NOT__ activated",
00395                __FILE__, __LINE__);
00396   }
00397 
00398   return (desc);
00399 };
00400 
00401 /*! \brief Return the IP address and the port number of a remote end point.
00402     \author Denis BEURIVE
00403     \param sender Pointer to the address of the destant end point.
00404     \param ip Pointer to a string of character used to store the IP address.
00405            This string should be, at least MAX_IP_ADDRESS_STR_SIZE characters long.
00406     \param port Pointer to an integer used to store the port number.
00407  */
00408 
00409 void get_remote_address (struct sockaddr_in *sender, char *ip, int *port)
00410 {
00411    int  i;
00412    char *a;
00413 
00414    a = inet_ntoa(sender->sin_addr);
00415    for (i=0; i<MAX_IP_ADDRESS_STR_SIZE-1; i++) { ip[i] = *(a+i); };
00416    ip[MAX_IP_ADDRESS_STR_SIZE-1] = 0;
00417    *port = ntohs (sender->sin_port);
00418 }
00419 
00420 /*! \brief Convert IP address in binary representation (4 bytes in network byte order)
00421            into standard numbers-and-dots notation.
00422     \author Denis BEURIVE
00423     \param ip Binary representation of the IP address to convert (4 bytes in network byte order).
00424     \return The function returns a zero terminated string that represents the IP address in standard
00425             numbers-and-dots notation.
00426     \warning The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
00427              Do not try to free it!
00428  */
00429 
00430 static char *get_doted_ip (unsigned long int ip)
00431 {
00432   struct in_addr     add;
00433 
00434   add.s_addr = ip;
00435 
00436   return inet_ntoa(add);
00437 }
00438 

Generated on Mon Jun 19 12:31:06 2006 for MyDhcp_V2 by doxygen1.2.15