Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

ipv4.c

00001 /*****************************************************************************
00002  * ipv4.c: IPv4 network abstraction layer
00003  *****************************************************************************
00004  * Copyright (C) 2001-2005 the VideoLAN team
00005  * $Id: ipv4.c 12947 2005-10-23 16:43:48Z gbazin $
00006  *
00007  * Authors: Christophe Massiot <[email protected]>
00008  *          Mathias Kretschmer <[email protected]>
00009  *          Alexis de Lattre <[email protected]>
00010  *          RĂ©mi Denis-Courmont <rem # videolan.org>
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00025  *****************************************************************************/
00026 
00027 /*****************************************************************************
00028  * Preamble
00029  *****************************************************************************/
00030 #include <stdlib.h>
00031 #include <string.h>
00032 
00033 #include <vlc/vlc.h>
00034 #include <errno.h>
00035 
00036 #ifdef HAVE_SYS_TYPES_H
00037 #   include <sys/types.h>
00038 #endif
00039 #ifdef HAVE_SYS_STAT_H
00040 #   include <sys/stat.h>
00041 #endif
00042 #ifdef HAVE_FCNTL_H
00043 #   include <fcntl.h>
00044 #endif
00045 
00046 #ifdef HAVE_UNISTD_H
00047 #   include <unistd.h>
00048 #endif
00049 
00050 #if defined(WIN32) || defined(UNDER_CE)
00051 #   if defined(UNDER_CE) && defined(sockaddr_storage)
00052 #       undef sockaddr_storage
00053 #   endif
00054 #   include <winsock2.h>
00055 #   include <ws2tcpip.h>
00056 #   include <iphlpapi.h>
00057 #   define close closesocket
00058 #   if defined(UNDER_CE)
00059 #       undef IP_MULTICAST_TTL
00060 #       define IP_MULTICAST_TTL 3
00061 #       undef IP_ADD_MEMBERSHIP
00062 #       define IP_ADD_MEMBERSHIP 5
00063 #   endif
00064 #else
00065 #   include <netdb.h>                                         /* hostent ... */
00066 #   include <sys/socket.h>
00067 #   include <netinet/in.h>
00068 #   ifdef HAVE_ARPA_INET_H
00069 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
00070 #   endif
00071 #endif
00072 
00073 #include "network.h"
00074 
00075 #ifndef INADDR_ANY
00076 #   define INADDR_ANY  0x00000000
00077 #endif
00078 #ifndef INADDR_NONE
00079 #   define INADDR_NONE 0xFFFFFFFF
00080 #endif
00081 #ifndef IN_MULTICAST
00082 #   define IN_MULTICAST(a) IN_CLASSD(a)
00083 #endif
00084 
00085 
00086 /*****************************************************************************
00087  * Local prototypes
00088  *****************************************************************************/
00089 static int OpenUDP( vlc_object_t * );
00090 
00091 /*****************************************************************************
00092  * Module descriptor
00093  *****************************************************************************/
00094 #define MIFACE_TEXT N_("Multicast output interface")
00095 #define MIFACE_LONGTEXT N_( \
00096     "Indicate here the multicast output interface. " \
00097     "This overrides the routing table.")
00098 
00099 vlc_module_begin();
00100     set_shortname( "IPv4" );
00101     set_description( _("UDP/IPv4 network abstraction layer") );
00102     set_capability( "network", 50 );
00103     set_category( CAT_INPUT );
00104     set_subcategory( SUBCAT_INPUT_GENERAL );
00105     set_callbacks( OpenUDP, NULL );
00106     add_string( "miface-addr", NULL, NULL, MIFACE_TEXT, MIFACE_LONGTEXT, VLC_TRUE );
00107 vlc_module_end();
00108 
00109 /*****************************************************************************
00110  * BuildAddr: utility function to build a struct sockaddr_in
00111  *****************************************************************************/
00112 static int BuildAddr( struct sockaddr_in * p_socket,
00113                       const char * psz_address, int i_port )
00114 {
00115     /* Reset struct */
00116     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
00117     p_socket->sin_family = AF_INET;                                /* family */
00118     p_socket->sin_port = htons( (uint16_t)i_port );
00119     if( !*psz_address )
00120     {
00121         p_socket->sin_addr.s_addr = INADDR_ANY;
00122     }
00123     else
00124     {
00125         struct hostent    * p_hostent;
00126 
00127         /* Try to convert address directly from in_addr - this will work if
00128          * psz_address is dotted decimal. */
00129 #ifdef HAVE_ARPA_INET_H
00130         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
00131 #else
00132         p_socket->sin_addr.s_addr = inet_addr( psz_address );
00133         if( p_socket->sin_addr.s_addr == INADDR_NONE )
00134 #endif
00135         {
00136             /* We have a fqdn, try to find its address */
00137             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
00138             {
00139                 return( -1 );
00140             }
00141 
00142             /* Copy the first address of the host in the socket address */
00143             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
00144                      p_hostent->h_length );
00145         }
00146     }
00147     return( 0 );
00148 }
00149 
00150 #if defined(WIN32) || defined(UNDER_CE)
00151 # define WINSOCK_STRERROR_SIZE 20
00152 static const char *winsock_strerror( char *buf )
00153 {
00154     snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
00155               WSAGetLastError( ) );
00156     buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
00157     return buf;
00158 }
00159 #endif
00160 
00161 
00162 /*****************************************************************************
00163  * OpenUDP: open a UDP socket
00164  *****************************************************************************
00165  * psz_bind_addr, i_bind_port : address and port used for the bind()
00166  *   system call. If psz_bind_addr == "", the socket is bound to
00167  *   INADDR_ANY and broadcast reception is enabled. If psz_bind_addr is a
00168  *   multicast (class D) address, join the multicast group.
00169  * psz_server_addr, i_server_port : address and port used for the connect()
00170  *   system call. It can avoid receiving packets from unauthorized IPs.
00171  *   Its use leads to great confusion and is currently discouraged.
00172  * This function returns -1 in case of error.
00173  *****************************************************************************/
00174 static int OpenUDP( vlc_object_t * p_this )
00175 {
00176     network_socket_t * p_socket = p_this->p_private;
00177     const char * psz_bind_addr = p_socket->psz_bind_addr;
00178     int i_bind_port = p_socket->i_bind_port;
00179     const char * psz_server_addr = p_socket->psz_server_addr;
00180     int i_server_port = p_socket->i_server_port;
00181 
00182     int i_handle, i_opt;
00183     struct sockaddr_in sock;
00184     vlc_value_t val;
00185 #if defined(WIN32) || defined(UNDER_CE)
00186     char strerror_buf[WINSOCK_STRERROR_SIZE];
00187 # define strerror( x ) winsock_strerror( strerror_buf )
00188 #endif
00189 
00190     /* If IP_ADD_SOURCE_MEMBERSHIP is not defined in the headers
00191        (because it's not in glibc for example), we have to define the
00192        headers required for IGMPv3 here */
00193 #ifndef IP_ADD_SOURCE_MEMBERSHIP
00194     #define IP_ADD_SOURCE_MEMBERSHIP  39
00195     struct ip_mreq_source {
00196         struct in_addr  imr_multiaddr;
00197         struct in_addr  imr_interface;
00198         struct in_addr  imr_sourceaddr;
00199      };
00200 #endif
00201 
00202     p_socket->i_handle = -1;
00203 
00204     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
00205      * protocol */
00206     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
00207     {
00208         msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
00209         return 0;
00210     }
00211 
00212     /* We may want to reuse an already used socket */
00213     i_opt = 1;
00214     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
00215                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
00216     {
00217         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
00218                           strerror(errno));
00219         close( i_handle );
00220         return 0;
00221     }
00222 
00223 #ifdef SO_REUSEPORT
00224     i_opt = 1;
00225     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEPORT,
00226                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
00227     {
00228         msg_Warn( p_this, "cannot configure socket (SO_REUSEPORT)" );
00229     }
00230 #endif
00231 
00232     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
00233      * packet loss caused by scheduling problems */
00234 #if !defined( SYS_BEOS )
00235     i_opt = 0x80000;
00236     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i_opt,
00237                     sizeof( i_opt ) ) == -1 )
00238         msg_Dbg( p_this, "cannot configure socket (SO_RCVBUF: %s)",
00239                           strerror(errno));
00240     i_opt = 0x80000;
00241     if( setsockopt( i_handle, SOL_SOCKET, SO_SNDBUF, (void *) &i_opt,
00242                     sizeof( i_opt ) ) == -1 )
00243         msg_Dbg( p_this, "cannot configure socket (SO_SNDBUF: %s)",
00244                           strerror(errno));
00245 #endif
00246 
00247     /* Build the local socket */
00248 
00249 #if defined( WIN32 ) || defined( UNDER_CE )
00250     /* Under Win32 and for multicasting, we bind to INADDR_ANY,
00251      * so let's call BuildAddr with "" instead of psz_bind_addr */
00252     if( BuildAddr( &sock, IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) ?
00253                    "" : psz_bind_addr, i_bind_port ) == -1 )
00254 #else
00255     if( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
00256 #endif
00257     {
00258         msg_Dbg( p_this, "could not build local address" );
00259         close( i_handle );
00260         return 0;
00261     }
00262 
00263     /* Bind it */
00264     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
00265     {
00266         msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
00267         close( i_handle );
00268         return 0;
00269     }
00270 
00271 #if defined( WIN32 ) || defined( UNDER_CE )
00272     /* Restore the sock struct so we can spare a few #ifdef WIN32 later on */
00273     if( IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
00274     {
00275         if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
00276         {
00277             msg_Dbg( p_this, "could not build local address" );
00278             close( i_handle );
00279             return 0;
00280         }
00281     }
00282 #endif
00283 
00284 #if !defined( SYS_BEOS )
00285     /* Allow broadcast reception if we bound on INADDR_ANY */
00286     if( !*psz_bind_addr )
00287     {
00288         i_opt = 1;
00289         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, (void*) &i_opt,
00290                         sizeof( i_opt ) ) == -1 )
00291             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
00292                        strerror(errno) );
00293     }
00294 #endif
00295 
00296 #if !defined( SYS_BEOS )
00297     /* Join the multicast group if the socket is a multicast address */
00298     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
00299     {
00300         /* Determine interface to be used for multicast */
00301         char * psz_if_addr = config_GetPsz( p_this, "miface-addr" );
00302 
00303         /* If we have a source address, we use IP_ADD_SOURCE_MEMBERSHIP
00304            so that IGMPv3 aware OSes running on IGMPv3 aware networks
00305            will do an IGMPv3 query on the network */
00306         if( *psz_server_addr )
00307         {
00308             struct ip_mreq_source imr;
00309 
00310             imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
00311             imr.imr_sourceaddr.s_addr = inet_addr(psz_server_addr);
00312 
00313             if( psz_if_addr != NULL && *psz_if_addr
00314                 && inet_addr(psz_if_addr) != INADDR_NONE )
00315             {
00316                 imr.imr_interface.s_addr = inet_addr(psz_if_addr);
00317             }
00318             else
00319             {
00320                 imr.imr_interface.s_addr = INADDR_ANY;
00321             }
00322             if( psz_if_addr != NULL ) free( psz_if_addr );
00323 
00324             msg_Dbg( p_this, "IP_ADD_SOURCE_MEMBERSHIP multicast request" );
00325             /* Join Multicast group with source filter */
00326             if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
00327                          (char*)&imr,
00328                          sizeof(struct ip_mreq_source) ) == -1 )
00329             {
00330                 msg_Err( p_this, "failed to join IP multicast group (%s)",
00331                                   strerror(errno) );
00332                 msg_Err( p_this, "are you sure your OS supports IGMPv3?" );
00333                 close( i_handle );
00334                 return 0;
00335             }
00336          }
00337          /* If there is no source address, we use IP_ADD_MEMBERSHIP */
00338          else
00339          {
00340              struct ip_mreq imr;
00341 
00342              imr.imr_interface.s_addr = INADDR_ANY;
00343              imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
00344              if( psz_if_addr != NULL && *psz_if_addr
00345                 && inet_addr(psz_if_addr) != INADDR_NONE )
00346             {
00347                 imr.imr_interface.s_addr = inet_addr(psz_if_addr);
00348             }
00349 #if defined (WIN32) || defined (UNDER_CE)
00350             else
00351             {
00352                                 typedef DWORD (CALLBACK * GETBESTINTERFACE) ( IPAddr, PDWORD );
00353                                 typedef DWORD (CALLBACK * GETIPADDRTABLE) ( PMIB_IPADDRTABLE, PULONG, BOOL );
00354 
00355                 GETBESTINTERFACE OurGetBestInterface;
00356                                 GETIPADDRTABLE OurGetIpAddrTable;
00357                 HINSTANCE hiphlpapi = LoadLibrary(_T("Iphlpapi.dll"));
00358                 DWORD i_index;
00359 
00360                 if( hiphlpapi )
00361                 {
00362                     OurGetBestInterface =
00363                         (void *)GetProcAddress( hiphlpapi,
00364                                                 _T("GetBestInterface") );
00365                     OurGetIpAddrTable =
00366                         (void *)GetProcAddress( hiphlpapi,
00367                                                 _T("GetIpAddrTable") );
00368                 }
00369 
00370                 if( hiphlpapi && OurGetBestInterface && OurGetIpAddrTable &&
00371                     OurGetBestInterface( sock.sin_addr.s_addr,
00372                                          &i_index ) == NO_ERROR )
00373                 {
00374                     PMIB_IPADDRTABLE p_table;
00375                     DWORD i = 0;
00376 
00377                     msg_Dbg( p_this, "Winsock best interface is %lu",
00378                              (unsigned long)i_index );
00379                     OurGetIpAddrTable( NULL, &i, 0 );
00380 
00381                     p_table = (PMIB_IPADDRTABLE)malloc( i );
00382                     if( p_table != NULL )
00383                     {
00384                         if( OurGetIpAddrTable( p_table, &i, 0 ) == NO_ERROR )
00385                         {
00386                             for( i = 0; i < p_table->dwNumEntries; i-- )
00387                             {
00388                                 if( p_table->table[i].dwIndex == i_index )
00389                                 {
00390                                     imr.imr_interface.s_addr =
00391                                                      p_table->table[i].dwAddr;
00392                                     msg_Dbg( p_this, "using interface 0x%08x",
00393                                              p_table->table[i].dwAddr );
00394                                 }
00395                             }
00396                         }
00397                         else msg_Warn( p_this, "GetIpAddrTable failed" );
00398                         free( p_table );
00399                     }
00400                 }
00401                 else msg_Dbg( p_this, "GetBestInterface failed" );
00402 
00403                 if( hiphlpapi ) FreeLibrary( hiphlpapi );
00404             }
00405 #endif
00406             if( psz_if_addr != NULL ) free( psz_if_addr );
00407 
00408             msg_Dbg( p_this, "IP_ADD_MEMBERSHIP multicast request" );
00409             /* Join Multicast group without source filter */
00410             if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
00411                             (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
00412             {
00413                 msg_Err( p_this, "failed to join IP multicast group (%s)",
00414                                   strerror(errno) );
00415                 close( i_handle );
00416                 return 0;
00417             }
00418          }
00419     }
00420 #endif
00421 
00422     if( *psz_server_addr )
00423     {
00424         /* Build socket for remote connection */
00425         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
00426         {
00427             msg_Warn( p_this, "cannot build remote address" );
00428             close( i_handle );
00429             return 0;
00430         }
00431 
00432         /* Connect the socket */
00433         if( connect( i_handle, (struct sockaddr *) &sock,
00434                      sizeof( sock ) ) == (-1) )
00435         {
00436             msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
00437             close( i_handle );
00438             return 0;
00439         }
00440 
00441 #if !defined( SYS_BEOS )
00442         if( IN_MULTICAST( ntohl(inet_addr(psz_server_addr) ) ) )
00443         {
00444             /* set the time-to-live */
00445             int i_ttl = p_socket->i_ttl;
00446             unsigned char ttl;
00447             
00448             /* set the multicast interface */
00449             char * psz_mif_addr = config_GetPsz( p_this, "miface-addr" );
00450             if( psz_mif_addr )
00451             {
00452                 struct in_addr intf;
00453                 intf.s_addr = inet_addr(psz_mif_addr);
00454                 free( psz_mif_addr  );
00455 
00456                 if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_IF,
00457                                 &intf, sizeof( intf ) ) < 0 )
00458                 {
00459                     msg_Dbg( p_this, "failed to set multicast interface (%s).", strerror(errno) );
00460                     close( i_handle );
00461                     return 0;
00462                 }
00463             }
00464 
00465             if( i_ttl < 1 )
00466             {
00467                 if( var_Get( p_this, "ttl", &val ) != VLC_SUCCESS )
00468                 {
00469                     var_Create( p_this, "ttl",
00470                                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00471                     var_Get( p_this, "ttl", &val );
00472                 }
00473                 i_ttl = val.i_int;
00474             }
00475             if( i_ttl < 1 ) i_ttl = 1;
00476             ttl = (unsigned char) i_ttl;
00477 
00478             /* There is some confusion in the world whether IP_MULTICAST_TTL 
00479              * takes a byte or an int as an argument.
00480              * BSD seems to indicate byte so we are going with that and use
00481              * int as a fallback to be safe */
00482             if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
00483                             &ttl, sizeof( ttl ) ) < 0 )
00484             {
00485                 msg_Dbg( p_this, "failed to set ttl (%s). Let's try it "
00486                          "the integer way.", strerror(errno) );
00487                 if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
00488                                 &i_ttl, sizeof( i_ttl ) ) <0 )
00489                 {
00490                     msg_Err( p_this, "failed to set ttl (%s)",
00491                              strerror(errno) );
00492                     close( i_handle );
00493                     return 0;
00494                 }
00495             }
00496         }
00497 #endif
00498     }
00499 
00500     p_socket->i_handle = i_handle;
00501 
00502     if( var_Get( p_this, "mtu", &val ) != VLC_SUCCESS )
00503     {
00504         var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00505         var_Get( p_this, "mtu", &val );
00506     }
00507     p_socket->i_mtu = val.i_int;
00508 
00509     return 0;
00510 }

Generated on Tue Dec 20 10:14:48 2005 for vlc-0.8.4a by  doxygen 1.4.2