]> git.sesse.net Git - vlc/blob - modules/misc/network/ipv4.c
* Some changes to preferences categorization (This really needs a lot more work)
[vlc] / modules / misc / network / ipv4.c
1 /*****************************************************************************
2  * ipv4.c: IPv4 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2001-2005 VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Mathias Kretschmer <mathias@research.att.com>
9  *          Alexis de Lattre <alexis@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <vlc/vlc.h>
33 #include <errno.h>
34
35 #ifdef HAVE_SYS_TYPES_H
36 #   include <sys/types.h>
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 #   include <sys/stat.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #   include <fcntl.h>
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #endif
48
49 #if defined(WIN32) || defined(UNDER_CE)
50 #   if defined(UNDER_CE) && defined(sockaddr_storage)
51 #       undef sockaddr_storage
52 #   endif
53 #   include <winsock2.h>
54 #   include <ws2tcpip.h>
55 #   define close closesocket
56 #   if defined(UNDER_CE)
57 #       undef IP_MULTICAST_TTL
58 #       define IP_MULTICAST_TTL 3
59 #       undef IP_ADD_MEMBERSHIP
60 #       define IP_ADD_MEMBERSHIP 5
61 #   endif
62 #else
63 #   include <netdb.h>                                         /* hostent ... */
64 #   include <sys/socket.h>
65 #   include <netinet/in.h>
66 #   ifdef HAVE_ARPA_INET_H
67 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
68 #   endif
69 #endif
70
71 #include "network.h"
72
73 #ifndef INADDR_ANY
74 #   define INADDR_ANY  0x00000000
75 #endif
76 #ifndef INADDR_NONE
77 #   define INADDR_NONE 0xFFFFFFFF
78 #endif
79 #ifndef IN_MULTICAST
80 #   define IN_MULTICAST(a) IN_CLASSD(a)
81 #endif
82 #ifndef PF_INET
83 #    define PF_INET AF_INET                                          /* BeOS */
84 #endif
85
86
87 /*****************************************************************************
88  * Local prototypes
89  *****************************************************************************/
90 static int NetOpen( vlc_object_t * );
91
92 /*****************************************************************************
93  * Module descriptor
94  *****************************************************************************/
95 #define MIFACE_TEXT N_("Multicast output interface")
96 #define MIFACE_LONGTEXT N_( \
97     "Indicate here the multicast output interface. " \
98     "This overrides the routing table.")
99
100 vlc_module_begin();
101     set_description( _("IPv4 network abstraction layer") );
102     set_capability( "network", 50 );
103     set_category( CAT_INPUT );
104     set_subcategory( SUBCAT_INPUT_ADVANCED );
105     set_callbacks( NetOpen, NULL );
106     add_string( "miface-addr", NULL, NULL, MIFACE_TEXT, MIFACE_LONGTEXT, VLC_TRUE );
107 vlc_module_end();
108
109 /*****************************************************************************
110  * BuildAddr: utility function to build a struct sockaddr_in
111  *****************************************************************************/
112 static int BuildAddr( struct sockaddr_in * p_socket,
113                       const char * psz_address, int i_port )
114 {
115     /* Reset struct */
116     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
117     p_socket->sin_family = AF_INET;                                /* family */
118     p_socket->sin_port = htons( (uint16_t)i_port );
119     if( !*psz_address )
120     {
121         p_socket->sin_addr.s_addr = INADDR_ANY;
122     }
123     else
124     {
125         struct hostent    * p_hostent;
126
127         /* Try to convert address directly from in_addr - this will work if
128          * psz_address is dotted decimal. */
129 #ifdef HAVE_ARPA_INET_H
130         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
131 #else
132         p_socket->sin_addr.s_addr = inet_addr( psz_address );
133         if( p_socket->sin_addr.s_addr == INADDR_NONE )
134 #endif
135         {
136             /* We have a fqdn, try to find its address */
137             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
138             {
139                 return( -1 );
140             }
141
142             /* Copy the first address of the host in the socket address */
143             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
144                      p_hostent->h_length );
145         }
146     }
147     return( 0 );
148 }
149
150 /*****************************************************************************
151  * OpenUDP: open a UDP socket
152  *****************************************************************************
153  * psz_bind_addr, i_bind_port : address and port used for the bind()
154  *   system call. If psz_bind_addr == "", the socket is bound to
155  *   INADDR_ANY and broadcast reception is enabled. If i_bind_port == 0,
156  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
157  *   join the multicast group.
158  * psz_server_addr, i_server_port : address and port used for the connect()
159  *   system call. It can avoid receiving packets from unauthorized IPs.
160  *   Its use leads to great confusion and is currently discouraged.
161  * This function returns -1 in case of error.
162  *****************************************************************************/
163 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
164 {
165     char * psz_bind_addr = p_socket->psz_bind_addr;
166     int i_bind_port = p_socket->i_bind_port;
167     char * psz_server_addr = p_socket->psz_server_addr;
168     int i_server_port = p_socket->i_server_port;
169
170     int i_handle, i_opt;
171     socklen_t i_opt_size;
172     struct sockaddr_in sock;
173     vlc_value_t val;
174
175     /* If IP_ADD_SOURCE_MEMBERSHIP is not defined in the headers
176        (because it's not in glibc for example), we have to define the
177        headers required for IGMPv3 here */
178 #ifndef IP_ADD_SOURCE_MEMBERSHIP
179     #define IP_ADD_SOURCE_MEMBERSHIP  39
180     struct ip_mreq_source {
181         struct in_addr  imr_multiaddr;
182         struct in_addr  imr_interface;
183         struct in_addr  imr_sourceaddr;
184      };
185 #endif
186
187     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
188      * protocol */
189     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
190     {
191 #if defined(WIN32) || defined(UNDER_CE)
192         msg_Warn( p_this, "cannot create socket (%i)", WSAGetLastError() );
193 #else
194         msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
195 #endif
196         return( -1 );
197     }
198
199     /* We may want to reuse an already used socket */
200     i_opt = 1;
201     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
202                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
203     {
204 #if defined(WIN32) || defined(UNDER_CE)
205         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %i)",
206                   WSAGetLastError() );
207 #else
208         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
209                           strerror(errno));
210 #endif
211         close( i_handle );
212         return( -1 );
213     }
214
215 #ifdef SO_REUSEPORT
216     i_opt = 1;
217     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEPORT,
218                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
219     {
220         msg_Warn( p_this, "cannot configure socket (SO_REUSEPORT)" );
221     }
222 #endif
223
224     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
225      * packet loss caused by scheduling problems */
226     i_opt = 0x80000;
227 #if !defined( SYS_BEOS )
228     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i_opt, sizeof( i_opt ) ) == -1 )
229     {
230 #if defined(WIN32) || defined(UNDER_CE)
231         msg_Dbg( p_this, "cannot configure socket (SO_RCVBUF: %i)",
232                  WSAGetLastError() );
233 #else
234         msg_Dbg( p_this, "cannot configure socket (SO_RCVBUF: %s)",
235                           strerror(errno));
236 #endif
237     }
238 #endif
239
240 #if !defined( SYS_BEOS )
241     /* Check if we really got what we have asked for, because Linux, etc.
242      * will silently limit the max buffer size to net.core.rmem_max which
243      * is typically only 65535 bytes */
244     i_opt = 0;
245     i_opt_size = sizeof( i_opt );
246     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void*) &i_opt, &i_opt_size ) == -1 )
247     {
248 #if defined(WIN32) || defined(UNDER_CE)
249         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %i)",
250                   WSAGetLastError() );
251 #else
252         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
253                           strerror(errno) );
254 #endif
255     }
256     else if( i_opt < 0x80000 )
257     {
258         msg_Dbg( p_this, "socket buffer size is 0x%x instead of 0x%x",
259                          i_opt, 0x80000 );
260     }
261 #endif
262
263
264     /* Build the local socket */
265
266 #if defined( WIN32 ) || defined( UNDER_CE )
267     /* Under Win32 and for multicasting, we bind to INADDR_ANY,
268      * so let's call BuildAddr with "" instead of psz_bind_addr */
269     if( BuildAddr( &sock, IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) ?
270                    "" : psz_bind_addr, i_bind_port ) == -1 )
271 #else
272     if( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
273 #endif
274     {
275         msg_Dbg( p_this, "could not build local address" );
276         close( i_handle );
277         return( -1 );
278     }
279
280     /* Bind it */
281     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
282     {
283 #if defined(WIN32) || defined(UNDER_CE)
284         msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError() );
285 #else
286         msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
287 #endif
288         close( i_handle );
289         return( -1 );
290     }
291
292 #if defined( WIN32 ) || defined( UNDER_CE )
293     /* Restore the sock struct so we can spare a few #ifdef WIN32 later on */
294     if( IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
295     {
296         if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
297         {
298             msg_Dbg( p_this, "could not build local address" );
299             close( i_handle );
300             return( -1 );
301         }
302     }
303 #endif
304
305 #if !defined( SYS_BEOS )
306     /* Allow broadcast reception if we bound on INADDR_ANY */
307     if( !*psz_bind_addr )
308     {
309         i_opt = 1;
310         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, (void*) &i_opt, sizeof( i_opt ) ) == -1 )
311         {
312 #if defined(WIN32) || defined(UNDER_CE)
313             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %i)",
314                       WSAGetLastError() );
315 #else
316             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
317                        strerror(errno) );
318 #endif
319         }
320     }
321 #endif
322
323 #if !defined( SYS_BEOS )
324     /* Join the multicast group if the socket is a multicast address */
325     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
326     {
327         /* Determine interface to be used for multicast */
328         char * psz_if_addr = config_GetPsz( p_this, "iface-addr" );
329
330         /* If we have a source address, we use IP_ADD_SOURCE_MEMBERSHIP
331            so that IGMPv3 aware OSes running on IGMPv3 aware networks
332            will do an IGMPv3 query on the network */
333         if( *psz_server_addr )
334         {
335             struct ip_mreq_source imr;
336
337             imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
338             imr.imr_sourceaddr.s_addr = inet_addr(psz_server_addr);
339
340             if( psz_if_addr != NULL && *psz_if_addr
341                 && inet_addr(psz_if_addr) != INADDR_NONE )
342             {
343                 imr.imr_interface.s_addr = inet_addr(psz_if_addr);
344             }
345             else
346             {
347                 imr.imr_interface.s_addr = INADDR_ANY;
348             }
349             if( psz_if_addr != NULL ) free( psz_if_addr );
350
351             msg_Dbg( p_this, "IP_ADD_SOURCE_MEMBERSHIP multicast request" );
352             /* Join Multicast group with source filter */
353             if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
354                          (char*)&imr,
355                          sizeof(struct ip_mreq_source) ) == -1 )
356             {
357 #if defined(WIN32) || defined(UNDER_CE)
358                 msg_Err( p_this, "failed to join IP multicast group (%i)",
359                          WSAGetLastError() );
360 #else
361                 msg_Err( p_this, "failed to join IP multicast group (%s)",
362                                   strerror(errno) );
363 #endif
364                 msg_Err( p_this, "are you sure your OS supports IGMPv3?" );
365                 close( i_handle );
366                 return( -1 );
367             }
368          }
369          /* If there is no source address, we use IP_ADD_MEMBERSHIP */
370          else
371          {
372              struct ip_mreq imr;
373
374              imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
375              if( psz_if_addr != NULL && *psz_if_addr
376                 && inet_addr(psz_if_addr) != INADDR_NONE )
377             {
378                 imr.imr_interface.s_addr = inet_addr(psz_if_addr);
379             }
380             else
381             {
382                 imr.imr_interface.s_addr = INADDR_ANY;
383             }
384             if( psz_if_addr != NULL ) free( psz_if_addr );
385
386             msg_Dbg( p_this, "IP_ADD_MEMBERSHIP multicast request" );
387             /* Join Multicast group without source filter */
388             if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
389                             (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
390             {
391 #if defined(WIN32) || defined(UNDER_CE)
392                 msg_Err( p_this, "failed to join IP multicast group (%i)",
393                          WSAGetLastError() );
394 #else
395                 msg_Err( p_this, "failed to join IP multicast group (%s)",
396                                   strerror(errno) );
397 #endif
398                 close( i_handle );
399                 return( -1 );
400             }
401          }
402     }
403 #endif
404
405     if( *psz_server_addr )
406     {
407         /* Build socket for remote connection */
408         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
409         {
410             msg_Warn( p_this, "cannot build remote address" );
411             close( i_handle );
412             return( -1 );
413         }
414
415         /* Connect the socket */
416         if( connect( i_handle, (struct sockaddr *) &sock,
417                      sizeof( sock ) ) == (-1) )
418         {
419 #if defined(WIN32) || defined(UNDER_CE)
420             msg_Warn( p_this, "cannot connect socket (%i)", WSAGetLastError());
421 #else
422             msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
423 #endif
424             close( i_handle );
425             return( -1 );
426         }
427
428 #if !defined( SYS_BEOS )
429         if( IN_MULTICAST( ntohl(inet_addr(psz_server_addr) ) ) )
430         {
431             /* set the time-to-live */
432             int i_ttl = p_socket->i_ttl;
433             unsigned char ttl;
434             
435             /* set the multicast interface */
436             char * psz_mif_addr = config_GetPsz( p_this, "miface-addr" );
437             if( psz_mif_addr )
438             {
439                 struct in_addr intf;
440                 intf.s_addr = inet_addr(psz_mif_addr);
441                 free( psz_mif_addr  );
442
443                 if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_IF,
444                                 &intf, sizeof( intf ) ) < 0 )
445                 {
446                     msg_Dbg( p_this, "failed to set multicast interface (%s).", strerror(errno) );
447                     close( i_handle );
448                     return ( -1 );
449                 }
450             }
451
452             if( i_ttl < 1 )
453             {
454                 if( var_Get( p_this, "ttl", &val ) != VLC_SUCCESS )
455                 {
456                     var_Create( p_this, "ttl",
457                                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
458                     var_Get( p_this, "ttl", &val );
459                 }
460                 i_ttl = val.i_int;
461             }
462             if( i_ttl < 1 ) i_ttl = 1;
463             ttl = (unsigned char) i_ttl;
464
465             /* There is some confusion in the world whether IP_MULTICAST_TTL 
466              * takes a byte or an int as an argument.
467              * BSD seems to indicate byte so we are going with that and use
468              * int as a fallback to be safe */
469             if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
470                             &ttl, sizeof( ttl ) ) < 0 )
471             {
472                 msg_Dbg( p_this, "failed to set ttl (%s). Let's try it "
473                          "the integer way.", strerror(errno) );
474                 if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
475                                 &i_ttl, sizeof( i_ttl ) ) <0 )
476                 {
477                     msg_Err( p_this, "failed to set ttl (%s)",
478                              strerror(errno) );
479                     close( i_handle );
480                     return( -1 );
481                 }
482             }
483         }
484 #endif
485     }
486
487     p_socket->i_handle = i_handle;
488
489     if( var_Get( p_this, "mtu", &val ) != VLC_SUCCESS )
490     {
491         var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
492         var_Get( p_this, "mtu", &val );
493     }
494     p_socket->i_mtu = val.i_int;
495     return( 0 );
496 }
497
498 /*****************************************************************************
499  * NetOpen: wrapper around OpenUDP, ListenTCP and OpenTCP
500  *****************************************************************************/
501 static int NetOpen( vlc_object_t * p_this )
502 {
503     network_socket_t * p_socket = p_this->p_private;
504
505     return OpenUDP( p_this, p_socket );
506 }