]> git.sesse.net Git - vlc/blob - plugins/network/ipv6.c
* IPv6 network module, courtesy of Alexis Guillard <alexis.guillard@bt.com>,
[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.1 2002/03/04 23:56:37 massiot Exp $
6  *
7  * Authors: Alexis Guillard <alexis.guillard@bt.com>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <videolan/vlc.h>
36
37 #ifdef HAVE_UNISTD_H
38 #   include <unistd.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 )
40 #   include <io.h>
41 #endif
42
43 #ifdef WIN32
44 #   include <winsock2.h>
45 #   include <ws2tcpip.h>
46 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
47 #   include <netdb.h>                                         /* hostent ... */
48 #   include <sys/socket.h>
49 #   include <netinet/in.h>
50 #   ifdef HAVE_ARPA_INET_H
51 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
52 #   endif
53 #endif
54
55 #include "network.h"
56
57 /* Default MTU used for UDP socket. FIXME: we should issue some ioctl()
58  * call to get that value from the interface driver. */
59 #define DEFAULT_MTU 1500
60
61 /*****************************************************************************
62  * Local prototypes
63  *****************************************************************************/
64 static void getfunctions( function_list_t * );
65 static int  NetworkOpen( struct network_socket_s * );
66
67 /*****************************************************************************
68  * Build configuration tree.
69  *****************************************************************************/
70 MODULE_CONFIG_START
71 MODULE_CONFIG_STOP
72  
73 MODULE_INIT_START
74     SET_DESCRIPTION( "IPv6 network abstraction layer" )
75     ADD_CAPABILITY( NETWORK, 40 )
76     ADD_SHORTCUT( "ipv6" )
77 MODULE_INIT_STOP
78  
79 MODULE_ACTIVATE_START
80     getfunctions( &p_module->p_functions->network );
81 MODULE_ACTIVATE_STOP
82  
83 MODULE_DEACTIVATE_START
84 MODULE_DEACTIVATE_STOP
85
86 /*****************************************************************************
87  * Functions exported as capabilities. They are declared as static so that
88  * we don't pollute the namespace too much.
89  *****************************************************************************/
90 static void getfunctions( function_list_t * p_function_list )
91 {
92 #define f p_function_list->functions.network
93     f.pf_open = NetworkOpen;
94 #undef f
95 }
96
97 /*****************************************************************************
98  * BuildAddr: utility function to build a struct sockaddr_in6
99  *****************************************************************************/
100 static int BuildAddr( struct sockaddr_in6 * p_socket,
101                       char * psz_address, int i_port )
102 {
103     /* Reset struct */
104     memset( p_socket, 0, sizeof( struct sockaddr_in6 ) );
105     p_socket->sin6_family = AF_INET6;                               /* family */
106     p_socket->sin6_port = htons( i_port );
107     if( psz_address == NULL )
108     {
109         p_socket->sin6_addr = in6addr_any;
110     }
111     else if( *psz_address != '['
112               || psz_address[strlen(psz_address) - 1] != ']' )
113     {
114         intf_ErrMsg( "ipv6: IPv6 address is invalid, discarding" );
115         return( -1 );
116     } 
117     else
118     {
119         psz_address++;
120         psz_address[strlen(psz_address) - 1] = '\0' ;
121         inet_pton(AF_INET6, psz_address, &p_socket->sin6_addr.s6_addr); 
122     }
123     return( 0 );
124 }
125
126 /*****************************************************************************
127  * OpenUDP: open a UDP socket
128  *****************************************************************************
129  * psz_bind_addr, i_bind_port : address and port used for the bind()
130  *   system call. If psz_bind_addr == NULL, the socket is bound to
131  *   in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
132  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
133  *   join the multicast group.
134  * psz_server_addr, i_server_port : address and port used for the connect()
135  *   system call. It can avoid receiving packets from unauthorized IPs.
136  *   Its use leads to great confusion and is currently discouraged.
137  * This function returns -1 in case of error.
138  *****************************************************************************/
139 static int OpenUDP( network_socket_t * p_socket )
140 {
141     char * psz_bind_addr = p_socket->psz_bind_addr;
142     int i_bind_port = p_socket->i_bind_port;
143     char * psz_server_addr = p_socket->psz_server_addr;
144     int i_server_port = p_socket->i_server_port;
145
146     int i_handle, i_opt, i_opt_size;
147     struct sockaddr_in6 sock;
148
149     if( i_bind_port == 0 )
150     {
151         i_bind_port = config_GetIntVariable( INPUT_PORT_VAR );
152     }
153
154     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
155      * protocol */
156     if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
157     {
158         intf_ErrMsg( "ipv6 error: cannot create socket (%s)", strerror(errno) );
159         return( -1 );
160     }
161
162     /* We may want to reuse an already used socket */
163     i_opt = 1;
164     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
165                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
166     {
167         intf_ErrMsg( "ipv6 error: cannot configure socket (SO_REUSEADDR: %s)",
168                      strerror(errno));
169         close( i_handle );
170         return( -1 );
171     }
172
173     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
174      * packet loss caused by scheduling problems */
175     i_opt = 0x80000;
176     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
177                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
178     {
179         intf_WarnMsg( 1,
180                       "ipv6 warning: cannot configure socket (SO_RCVBUF: %s)",
181                       strerror(errno));
182     }
183  
184     /* Check if we really got what we have asked for, because Linux, etc.
185      * will silently limit the max buffer size to net.core.rmem_max which
186      * is typically only 65535 bytes */
187     i_opt = 0;
188     i_opt_size = sizeof( i_opt );
189     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
190                     (void*) &i_opt, &i_opt_size ) == -1 )
191     {
192         intf_WarnMsg( 1, "ipv6 warning: cannot query socket (SO_RCVBUF: %s)",
193                          strerror(errno));
194     }
195     else if( i_opt < 0x80000 )
196     {
197         intf_WarnMsg( 1, "ipv6 warning: socket buffer size is 0x%x"
198                          " instead of 0x%x", i_opt, 0x80000 );
199     }
200     
201     /* Build the local socket */
202     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )        
203     {
204         close( i_handle );
205         return( -1 );
206     }
207  
208     /* Bind it */
209     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
210     {
211         intf_ErrMsg( "ipv6 error: cannot bind socket (%s)", strerror(errno) );
212         close( i_handle );
213         return( -1 );
214     }
215
216     /* Allow broadcast reception if we bound on in6addr_any */
217     if( psz_bind_addr == NULL )
218     {
219         i_opt = 1;
220         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
221                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
222         {
223             intf_WarnMsg( 1,
224                     "ipv6 warning: cannot configure socket (SO_BROADCAST: %s)",
225                     strerror(errno));
226         }
227     }
228  
229     /* Join the multicast group if the socket is a multicast address */
230     /* FIXME: To be written */
231
232     if( psz_server_addr != NULL )
233     {
234         /* Build socket for remote connection */
235         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
236         {
237             intf_ErrMsg( "ipv6 error: cannot build remote address" );
238             close( i_handle );
239             return( -1 );
240         }
241  
242         /* Connect the socket */
243         if( connect( i_handle, (struct sockaddr *) &sock,
244                      sizeof( sock ) ) == (-1) )
245         {
246             intf_ErrMsg( "ipv6 error: cannot connect socket (%s)",
247                          strerror(errno) );
248             close( i_handle );
249             return( -1 );
250         }
251     }
252
253     p_socket->i_handle = i_handle;
254     p_socket->i_mtu = DEFAULT_MTU;
255     return( 0 );
256 }
257
258 /*****************************************************************************
259  * OpenTCP: open a TCP socket
260  *****************************************************************************
261  * psz_server_addr, i_server_port : address and port used for the connect()
262  *   system call. If i_server_port == 0, 80 is used.
263  * Other parameters are ignored.
264  * This function returns -1 in case of error.
265  *****************************************************************************/
266 static int OpenTCP( network_socket_t * p_socket )
267 {
268     char * psz_server_addr = p_socket->psz_server_addr;
269     int i_server_port = p_socket->i_server_port;
270
271     int i_handle;
272     struct sockaddr_in6 sock;
273
274     if( i_server_port == 0 )
275     {
276         i_server_port = 80;
277     }
278
279     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET6 domain, automatic (0)
280      * protocol */
281     if( (i_handle = socket( AF_INET6, SOCK_STREAM, 0 )) == -1 )
282     {
283         intf_ErrMsg( "ipv6 error: cannot create socket (%s)", strerror(errno) );
284         return( -1 );
285     }
286
287     /* Build remote address */
288     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
289     {
290         close( i_handle );
291         return( -1 );
292     }
293
294     /* Connect the socket */
295     if( connect( i_handle, (struct sockaddr *) &sock,
296                  sizeof( sock ) ) == (-1) )
297     {
298         intf_ErrMsg( "ipv6 error: cannot connect socket (%s)",
299                      strerror(errno) );
300         close( i_handle );
301         return( -1 );
302     }
303
304     p_socket->i_handle = i_handle;
305     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
306
307     return( 0 );
308 }
309
310 /*****************************************************************************
311  * NetworkOpen: wrapper around OpenUDP and OpenTCP
312  *****************************************************************************/
313 static int NetworkOpen( network_socket_t * p_socket )
314 {
315     if( p_socket->i_type == NETWORK_UDP )
316     {
317         return OpenUDP( p_socket );
318     }
319     else
320     {
321         return OpenTCP( p_socket );
322     }
323 }
324
325