]> git.sesse.net Git - vlc/blob - plugins/network/ipv6.c
* IPv6 multicast support, courtesy of Remco Poortinga <poortinga@telin.nl> ;
[vlc] / plugins / network / ipv6.c
1 /*****************************************************************************
2  * ipv6.c: IPv6 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: ipv6.c,v 1.11 2002/06/09 22:57:00 massiot Exp $
6  *
7  * Authors: Alexis Guillard <alexis.guillard@bt.com>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Remco Poortinga <poortinga@telin.nl>
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 <sys/types.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36 #include <vlc/vlc.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #elif defined( _MSC_VER ) && defined( _WIN32 )
41 #   include <io.h>
42 #endif
43
44 #ifdef WIN32
45 #   include <winsock2.h>
46 #   include <ws2tcpip.h>
47 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
48 #   include <netdb.h>                                         /* hostent ... */
49 #   include <sys/socket.h>
50 #   include <netinet/in.h>
51 #   ifdef HAVE_ARPA_INET_H
52 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
53 #   endif
54 #endif
55
56 #include "network.h"
57
58 /* Default MTU used for UDP socket. FIXME: we should issue some ioctl()
59  * call to get that value from the interface driver. */
60 #define DEFAULT_MTU 1500
61
62 #if defined(WIN32)
63 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
64 #endif
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static void getfunctions( function_list_t * );
70 static int  NetworkOpen( vlc_object_t *, network_socket_t * );
71
72 /*****************************************************************************
73  * Build configuration tree.
74  *****************************************************************************/
75 MODULE_CONFIG_START
76 MODULE_CONFIG_STOP
77  
78 MODULE_INIT_START
79     SET_DESCRIPTION( _("IPv6 network abstraction layer") )
80     ADD_CAPABILITY( NETWORK, 40 )
81 MODULE_INIT_STOP
82  
83 MODULE_ACTIVATE_START
84     getfunctions( &p_module->p_functions->network );
85 MODULE_ACTIVATE_STOP
86  
87 MODULE_DEACTIVATE_START
88 MODULE_DEACTIVATE_STOP
89
90 /*****************************************************************************
91  * Functions exported as capabilities. They are declared as static so that
92  * we don't pollute the namespace too much.
93  *****************************************************************************/
94 static void getfunctions( function_list_t * p_function_list )
95 {
96 #define f p_function_list->functions.network
97     f.pf_open = NetworkOpen;
98 #undef f
99 }
100
101 /*****************************************************************************
102  * BuildAddr: utility function to build a struct sockaddr_in6
103  *****************************************************************************/
104 static int BuildAddr( struct sockaddr_in6 * p_socket,
105                       char * psz_address, int i_port )
106 {
107     char * psz_multicast_interface = "";
108
109 #if defined(WIN32)
110     /* Try to get getaddrinfo() and freeaddrinfo() from wship6.dll */
111     typedef int (CALLBACK * GETADDRINFO) ( const char *nodename,
112                                             const char *servname,
113                                             const struct addrinfo *hints,
114                                             struct addrinfo **res );
115     typedef void (CALLBACK * FREEADDRINFO) ( struct addrinfo FAR *ai );
116
117     struct addrinfo hints, *res;
118     GETADDRINFO _getaddrinfo = NULL;
119     FREEADDRINFO _freeaddrinfo = NULL;
120
121     HINSTANCE wship6_dll = LoadLibrary("wship6.dll");
122     if( wship6_dll )
123     {
124         _getaddrinfo = (GETADDRINFO) GetProcAddress( wship6_dll,
125                                                      "getaddrinfo" );
126         _freeaddrinfo = (FREEADDRINFO) GetProcAddress( wship6_dll,
127                                                        "freeaddrinfo" );
128     }
129     if( !_getaddrinfo || !_freeaddrinfo )
130     {
131 //X        msg_Err( p_this, "no IPv6 stack installed" );
132         if( wship6_dll ) FreeLibrary( wship6_dll );
133         return( -1 );
134     }
135 #endif
136
137     /* Reset struct */
138     memset( p_socket, 0, sizeof( struct sockaddr_in6 ) );
139     p_socket->sin6_family = AF_INET6;                              /* family */
140     p_socket->sin6_port = htons( i_port );
141     if( !*psz_address )
142     {
143         p_socket->sin6_addr = in6addr_any;
144     }
145     else if( psz_address[0] == '['
146               && psz_address[strlen(psz_address) - 1] == ']' )
147     {
148         psz_address++;
149         /* see if there is an interface name in there... */
150         if( (psz_multicast_interface = strchr(psz_address, '%')) != NULL )
151         {
152             *psz_multicast_interface = '\0';
153             psz_multicast_interface++;
154             intf_WarnMsg( 3, "Interface name specified: \"%s\"",
155                           psz_multicast_interface );
156             /* now convert that interface name to an index */
157             p_socket->sin6_scope_id = if_nametoindex(psz_multicast_interface);
158             intf_WarnMsg( 3, " = #%i\n", p_socket->sin6_scope_id );
159         }
160         psz_address[strlen(psz_address) - 1] = '\0' ;
161
162 #if !defined( WIN32 )
163         inet_pton(AF_INET6, psz_address, &p_socket->sin6_addr.s6_addr); 
164
165 #else
166         memset(&hints, 0, sizeof(hints));
167         hints.ai_family = AF_INET6;
168         hints.ai_flags = AI_NUMERICHOST;
169
170         if( _getaddrinfo( psz_address, NULL, &hints, &res ) )
171         {
172             FreeLibrary( wship6_dll );
173             return( -1 );
174         }
175         memcpy( &p_socket->sin6_addr,
176                 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
177                 sizeof(struct in6_addr) );
178         _freeaddrinfo( res );
179
180 #endif
181     }
182     else
183     {
184 #ifdef HAVE_GETHOSTBYNAME2
185         struct hostent    * p_hostent;
186
187         /* We have a fqdn, try to find its address */
188         if ( (p_hostent = gethostbyname2( psz_address, AF_INET6 )) == NULL )
189         {
190 //X            intf_ErrMsg( "ipv6 error: unknown host %s", psz_address );
191             return( -1 );
192         }
193
194         /* Copy the first address of the host in the socket address */
195         memcpy( &p_socket->sin6_addr, p_hostent->h_addr_list[0],
196                  p_hostent->h_length );
197
198 #elif defined(WIN32)
199         if( _getaddrinfo( psz_address, NULL, &hints, &res ) )
200         {
201             FreeLibrary( wship6_dll );
202             return( -1 );
203         }
204         memcpy( &p_socket->sin6_addr,
205                 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
206                 sizeof(struct in6_addr) );
207         _freeaddrinfo( res );
208
209 #else
210 //X        intf_ErrMsg( "ipv6 error: IPv6 address %s is invalid", psz_address );
211         return( -1 );
212 #endif
213     }
214
215 #if defined(WIN32)
216     FreeLibrary( wship6_dll );
217 #endif
218
219     return( 0 );
220 }
221
222 /*****************************************************************************
223  * OpenUDP: open a UDP socket
224  *****************************************************************************
225  * psz_bind_addr, i_bind_port : address and port used for the bind()
226  *   system call. If psz_bind_addr == NULL, the socket is bound to
227  *   in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
228  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
229  *   join the multicast group.
230  * psz_server_addr, i_server_port : address and port used for the connect()
231  *   system call. It can avoid receiving packets from unauthorized IPs.
232  *   Its use leads to great confusion and is currently discouraged.
233  * This function returns -1 in case of error.
234  *****************************************************************************/
235 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
236 {
237     char * psz_bind_addr = p_socket->psz_bind_addr;
238     int i_bind_port = p_socket->i_bind_port;
239     char * psz_server_addr = p_socket->psz_server_addr;
240     int i_server_port = p_socket->i_server_port;
241
242     int i_handle, i_opt, i_opt_size;
243     struct sockaddr_in6 sock;
244
245     if( i_bind_port == 0 )
246     {
247 //X        i_bind_port = config_GetInt( "server-port" );
248     }
249
250     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
251      * protocol */
252     if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
253     {
254         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
255         return( -1 );
256     }
257
258     /* We may want to reuse an already used socket */
259     i_opt = 1;
260     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
261                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
262     {
263         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
264                          strerror(errno) );
265         close( i_handle );
266         return( -1 );
267     }
268
269     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
270      * packet loss caused by scheduling problems */
271     i_opt = 0x80000;
272     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
273                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
274     {
275         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
276                           strerror(errno) );
277     }
278  
279     /* Check if we really got what we have asked for, because Linux, etc.
280      * will silently limit the max buffer size to net.core.rmem_max which
281      * is typically only 65535 bytes */
282     i_opt = 0;
283     i_opt_size = sizeof( i_opt );
284     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
285                     (void*) &i_opt, &i_opt_size ) == -1 )
286     {
287         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
288                           strerror(errno) );
289     }
290     else if( i_opt < 0x80000 )
291     {
292         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
293                           i_opt, 0x80000 );
294     }
295     
296     /* Build the local socket */
297     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )        
298     {
299         close( i_handle );
300         return( -1 );
301     }
302  
303     /* Bind it */
304     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
305     {
306         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
307         close( i_handle );
308         return( -1 );
309     }
310
311     /* Allow broadcast reception if we bound on in6addr_any */
312     if( !*psz_bind_addr )
313     {
314         i_opt = 1;
315         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
316                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
317         {
318             msg_Warn( p_this, "ipv6 warning: cannot configure socket "
319                               "(SO_BROADCAST: %s)", strerror(errno) );
320         }
321     }
322  
323     /* Join the multicast group if the socket is a multicast address */
324     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
325     {
326         struct ipv6_mreq     imr;
327         int                  res;
328
329         imr.ipv6mr_interface = sock.sin6_scope_id;
330         imr.ipv6mr_multiaddr = sock.sin6_addr;
331         res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr,
332                          sizeof(imr));
333
334         if( res == -1 )
335         {
336             intf_ErrMsg( "ipv6 error: setsockopt JOIN_GROUP failed" );
337         }
338     }
339
340
341     if( *psz_server_addr )
342     {
343         /* Build socket for remote connection */
344         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
345         {
346             msg_Err( p_this, "cannot build remote address" );
347             close( i_handle );
348             return( -1 );
349         }
350  
351         /* Connect the socket */
352         if( connect( i_handle, (struct sockaddr *) &sock,
353                      sizeof( sock ) ) == (-1) )
354         {
355             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
356             close( i_handle );
357             return( -1 );
358         }
359     }
360
361     p_socket->i_handle = i_handle;
362     p_socket->i_mtu = DEFAULT_MTU;
363     return( 0 );
364 }
365
366 /*****************************************************************************
367  * OpenTCP: open a TCP socket
368  *****************************************************************************
369  * psz_server_addr, i_server_port : address and port used for the connect()
370  *   system call. If i_server_port == 0, 80 is used.
371  * Other parameters are ignored.
372  * This function returns -1 in case of error.
373  *****************************************************************************/
374 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
375 {
376     char * psz_server_addr = p_socket->psz_server_addr;
377     int i_server_port = p_socket->i_server_port;
378
379     int i_handle;
380     struct sockaddr_in6 sock;
381
382     if( i_server_port == 0 )
383     {
384         i_server_port = 80;
385     }
386
387     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET6 domain, automatic (0)
388      * protocol */
389     if( (i_handle = socket( AF_INET6, SOCK_STREAM, 0 )) == -1 )
390     {
391         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
392         return( -1 );
393     }
394
395     /* Build remote address */
396     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
397     {
398         close( i_handle );
399         return( -1 );
400     }
401
402     /* Connect the socket */
403     if( connect( i_handle, (struct sockaddr *) &sock,
404                  sizeof( sock ) ) == (-1) )
405     {
406         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
407         close( i_handle );
408         return( -1 );
409     }
410
411     p_socket->i_handle = i_handle;
412     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
413
414     return( 0 );
415 }
416
417 /*****************************************************************************
418  * NetworkOpen: wrapper around OpenUDP and OpenTCP
419  *****************************************************************************/
420 static int NetworkOpen( vlc_object_t * p_this, network_socket_t * p_socket )
421 {
422     if( p_socket->i_type == NETWORK_UDP )
423     {
424         return OpenUDP( p_this, p_socket );
425     }
426     else
427     {
428         return OpenTCP( p_this, p_socket );
429     }
430 }