]> git.sesse.net Git - vlc/blob - modules/misc/network/ipv6.c
721cb5e0665c29d1fc9d4ad12925957d8a9fa3f6
[vlc] / modules / misc / network / ipv6.c
1 /*****************************************************************************
2  * ipv6.c: IPv6 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2002-2005 VideoLAN (Centrale Réseaux) and its contributors
5  * $Id$
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 #endif
52
53 #include "network.h"
54
55 #if defined(WIN32)
56 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
57 /* the following will have to be removed when w32api defines them */
58 #   ifndef IPPROTO_IPV6
59 #      define IPPROTO_IPV6 41 
60 #   endif
61 #   ifndef IPV6_JOIN_GROUP
62 #      define IPV6_JOIN_GROUP 12
63 #   endif
64 #   ifndef IPV6_MULTICAST_HOPS
65 #      define IPV6_MULTICAST_HOPS 10
66 #   endif
67 #   ifndef IPV6_UNICAST_HOPS
68 #      define IPV6_UNICAST_HOPS 4
69 #   endif
70 #   define close closesocket
71 #endif
72
73 #ifndef MCAST_JOIN_SOURCE_GROUP
74 #   define MCAST_JOIN_SOURCE_GROUP         46
75 struct group_source_req
76 {
77        uint32_t           gsr_interface;  /* interface index */
78        struct sockaddr_storage gsr_group;      /* group address */
79        struct sockaddr_storage gsr_source;     /* source address */
80 };
81 #endif
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static int OpenUDP( vlc_object_t * );
87
88 /*****************************************************************************
89  * Module descriptor
90  *****************************************************************************/
91 vlc_module_begin();
92     set_description( _("IPv6 network abstraction layer") );
93     set_capability( "network", 40 );
94     set_callbacks( OpenUDP, NULL );
95 vlc_module_end();
96
97 /*****************************************************************************
98  * BuildAddr: utility function to build a struct sockaddr_in6
99  *****************************************************************************/
100 static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
101                       const char *psz_address, int i_port )
102 {
103     struct addrinfo hints, *res;
104     int i;
105
106     memset( &hints, 0, sizeof( hints ) );
107     hints.ai_family = AF_INET6;
108     hints.ai_socktype = SOCK_DGRAM;
109     hints.ai_flags = AI_PASSIVE;
110
111     i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
112     if( i )
113     {
114         msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
115         return -1;
116     }
117     if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
118     {
119         vlc_freeaddrinfo( res );
120         return -1;
121     }
122
123     memcpy( p_socket, res->ai_addr, res->ai_addrlen );
124     vlc_freeaddrinfo( res );
125     p_socket->sin6_port = htons( i_port );
126
127     return 0;
128 }
129
130 /*****************************************************************************
131  * OpenUDP: open a UDP socket
132  *****************************************************************************
133  * psz_bind_addr, i_bind_port : address and port used for the bind()
134  *   system call. If psz_bind_addr == NULL, the socket is bound to
135  *   in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
136  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
137  *   join the multicast group.
138  * psz_server_addr, i_server_port : address and port used for the connect()
139  *   system call. It can avoid receiving packets from unauthorized IPs.
140  *   Its use leads to great confusion and is currently discouraged.
141  * This function returns -1 in case of error.
142  *****************************************************************************/
143 static int OpenUDP( vlc_object_t * p_this )
144 {
145     network_socket_t *p_socket = p_this->p_private;
146     const char *psz_bind_addr = p_socket->psz_bind_addr;
147     int i_bind_port = p_socket->i_bind_port;
148     const char *psz_server_addr = p_socket->psz_server_addr;
149     int i_server_port = p_socket->i_server_port;
150     int i_handle, i_opt;
151     struct sockaddr_in6 sock;
152     vlc_value_t val;
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         msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
159         return( -1 );
160     }
161
162 #ifdef WIN32
163 # ifdef IPV6_PROTECTION_LEVEL
164         if( ptr->ai_family == AF_INET6 )
165         {
166             i_val = PROTECTION_LEVEL_UNRESTRICTED;
167             setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
168                         sizeof( i_val ) );
169         }
170 # else
171 #  warning You are using outdated headers for Winsock !
172 # endif
173 #endif
174
175     /* We may want to reuse an already used socket */
176     i_opt = 1;
177     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
178                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
179     {
180         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
181                          strerror(errno) );
182         close( i_handle );
183         return( -1 );
184     }
185
186     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
187      * packet loss caused by scheduling problems */
188     i_opt = 0x80000;
189     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
190                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
191     {
192         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
193                           strerror(errno) );
194     }
195
196     /* Build the local socket */
197     if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )        
198     {
199         close( i_handle );
200         return( -1 );
201     }
202
203 #if defined(WIN32)
204     /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
205     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
206     {
207         struct sockaddr_in6 sockany = sock;
208         sockany.sin6_addr = in6addr_any;
209         sockany.sin6_scope_id = 0;
210
211         /* Bind it */
212         if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
213         {
214             msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
215             close( i_handle );
216             return( -1 );
217         }
218     }
219     else
220 #endif
221     /* Bind it */
222     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
223     {
224         msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
225         close( i_handle );
226         return( -1 );
227     }
228
229     /* Allow broadcast reception if we bound on in6addr_any */
230     if( !*psz_bind_addr )
231     {
232         i_opt = 1;
233         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
234                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
235         {
236             msg_Warn( p_this, "IPv6 warning: cannot configure socket "
237                               "(SO_BROADCAST: %s)", strerror(errno) );
238         }
239     }
240
241     /* Join the multicast group if the socket is a multicast address */
242 #if defined( WIN32 ) || defined( HAVE_IF_NAMETOINDEX )
243     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
244     {
245         if(*psz_server_addr)
246         {
247             struct group_source_req imr;
248             struct sockaddr_in6 *p_sin6;
249
250             imr.gsr_interface = 0;
251             imr.gsr_group.ss_family = AF_INET6;
252             imr.gsr_source.ss_family = AF_INET6;
253             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
254             p_sin6->sin6_addr = sock.sin6_addr;
255
256             /* Build socket for remote connection */
257             msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);
258
259             if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
260             {
261                 msg_Warn( p_this, "cannot build remote address" );
262                 close( i_handle );
263                 return( -1 );
264             }
265             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
266             p_sin6->sin6_addr = sock.sin6_addr;
267
268             msg_Dbg( p_this, "IPV6_ADD_SOURCE_MEMBERSHIP multicast request" );
269             if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
270                           (char *)&imr, sizeof(struct group_source_req) ) == -1 )
271             {
272
273                 msg_Err( p_this, "failed to join IP multicast group (%s)",
274                                                           strerror(errno) );
275             }    
276         }
277         else
278         {
279         
280             struct ipv6_mreq     imr;
281             int                  res;
282
283             imr.ipv6mr_interface = sock.sin6_scope_id;
284             imr.ipv6mr_multiaddr = sock.sin6_addr;
285             res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
286 #if defined(WIN32)
287                          sizeof(imr) + 4); /* Doesn't work without this */
288 #else
289                          sizeof(imr));
290 #endif
291
292             if( res == -1 )
293             {
294                 msg_Err( p_this, "cannot join multicast group" );
295             } 
296         }
297     }
298 #else
299     msg_Warn( p_this, "Multicast IPv6 is not supported on your OS" );
300 #endif
301
302
303     if( *psz_server_addr )
304     {
305         int ttl = p_socket->i_ttl;
306         if( ttl < 1 )
307         {
308             ttl = config_GetInt( p_this, "ttl" );
309         }
310         if( ttl < 1 ) ttl = 1;
311
312         /* Build socket for remote connection */
313         if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
314         {
315             msg_Warn( p_this, "cannot build remote address" );
316             close( i_handle );
317             return( -1 );
318         }
319
320         /* Connect the socket */
321         if( connect( i_handle, (struct sockaddr *) &sock,
322                      sizeof( sock ) ) == (-1) )
323         {
324             msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
325             close( i_handle );
326             return( -1 );
327         }
328
329         /* Set the time-to-live */
330         if( ttl > 1 )
331         {
332 #if defined( WIN32 ) || defined( HAVE_IF_NAMETOINDEX )
333             if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
334             {
335                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
336                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
337                 {
338                     msg_Err( p_this, "failed to set multicast ttl (%s)",
339                              strerror(errno) );
340                 }
341             }
342             else
343 #endif
344             {
345                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
346                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
347                 {
348                     msg_Err( p_this, "failed to set unicast ttl (%s)",
349                               strerror(errno) );
350                 }
351             }
352         }
353     }
354
355     p_socket->i_handle = i_handle;
356
357     var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
358     var_Get( p_this, "mtu", &val );
359     p_socket->i_mtu = val.i_int;
360
361     return( 0 );
362 }