]> git.sesse.net Git - vlc/blob - modules/misc/network/ipv6.c
- Work-around for IPv6 SSM with Winsock 2 from Olivier Levon
[vlc] / modules / misc / network / ipv6.c
1 /*****************************************************************************
2  * ipv6.c: IPv6 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2002-2006 the VideoLAN team
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  *          RĂ©mi Denis-Courmont <rem # videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <vlc/vlc.h>
34
35 #include <errno.h>
36
37 #ifdef WIN32
38 #   include <winsock2.h>
39 #   include <ws2tcpip.h>
40 #   define if_nametoindex( str ) atoi( str )
41 #else
42 #   include <sys/types.h>
43 #   include <unistd.h>
44 #   include <netdb.h>                                         /* hostent ... */
45 #   include <sys/socket.h>
46 #   include <netinet/in.h>
47 #   include <net/if.h>
48 #endif
49
50 #include "network.h"
51
52 #if defined(WIN32)
53 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
54 # define close closesocket
55 #endif
56
57 #ifndef MCAST_JOIN_SOURCE_GROUP
58 # ifdef WIN32
59 /* Most (all?) Mingw32 versions in use are yet to pick up Vista stuff */
60 #  define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
61 # else
62 #  define MCAST_JOIN_SOURCE_GROUP 46
63 # endif
64 struct group_source_req
65 {
66        uint32_t           gsr_interface;  /* interface index */
67        struct sockaddr_storage gsr_group;      /* group address */
68        struct sockaddr_storage gsr_source;     /* source address */
69 };
70 #endif
71
72 /*****************************************************************************
73  * Local prototypes
74  *****************************************************************************/
75 static int OpenUDP( vlc_object_t * );
76
77 /*****************************************************************************
78  * Module descriptor
79  *****************************************************************************/
80 vlc_module_begin();
81     set_description( _("UDP/IPv6 network abstraction layer") );
82     set_capability( "network", 40 );
83     set_callbacks( OpenUDP, NULL );
84 vlc_module_end();
85
86 /*****************************************************************************
87  * BuildAddr: utility function to build a struct sockaddr_in6
88  *****************************************************************************/
89 static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
90                       const char *psz_address, int i_port )
91 {
92     struct addrinfo hints, *res;
93     int i;
94
95     memset( &hints, 0, sizeof( hints ) );
96     hints.ai_family = AF_INET6;
97     hints.ai_socktype = SOCK_DGRAM;
98     hints.ai_flags = AI_PASSIVE;
99
100     i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
101     if( i )
102     {
103         msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
104         return -1;
105     }
106     if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
107     {
108         vlc_freeaddrinfo( res );
109         return -1;
110     }
111
112     memcpy( p_socket, res->ai_addr, res->ai_addrlen );
113     vlc_freeaddrinfo( res );
114     p_socket->sin6_port = htons( i_port );
115
116     return 0;
117 }
118
119 #if defined(WIN32) || defined(UNDER_CE)
120 # define WINSOCK_STRERROR_SIZE 20
121 static const char *winsock_strerror( char *buf )
122 {
123     snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
124               WSAGetLastError( ) );
125     buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
126     return buf;
127 }
128 #endif
129
130
131 /*****************************************************************************
132  * OpenUDP: open a UDP socket
133  *****************************************************************************
134  * psz_bind_addr, i_bind_port : address and port used for the bind()
135  *   system call. If psz_bind_addr == NULL, the socket is bound to
136  *   in6addr_any and broadcast reception is enabled. If psz_bind_addr is a
137  *   multicast (FF00::/8) address, 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 #if defined(WIN32) || defined(UNDER_CE)
154     char strerror_buf[WINSOCK_STRERROR_SIZE];
155 # define strerror( x ) winsock_strerror( strerror_buf )
156 #endif
157
158     p_socket->i_handle = -1;
159
160     /* Build the local socket */
161     if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )        
162         return 0;
163
164     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
165      * protocol */
166     if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
167     {
168         msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
169         return 0;
170     }
171
172 #ifdef IPV6_V6ONLY
173     val.i_int = p_socket->v6only;
174
175     if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val.i_int,
176                     sizeof( val.i_int ) ) )
177     {
178         msg_Warn( p_this, "IPV6_V6ONLY: %s", strerror( errno ) );
179         p_socket->v6only = 1;
180     }
181 #else
182     p_socket->v6only = 1;
183 #endif
184
185 #ifdef WIN32
186 # ifndef IPV6_PROTECTION_LEVEL
187 #   define IPV6_PROTECTION_LEVEL 23
188 #  endif
189     {
190         int i_val = 30 /*PROTECTION_LEVEL_UNRESTRICTED*/;
191         setsockopt( i_handle, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
192                     sizeof( i_val ) );
193     }
194 #endif
195
196     /* We may want to reuse an already used socket */
197     i_opt = 1;
198     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
199                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
200     {
201         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
202                          strerror(errno) );
203         close( i_handle );
204         return 0;
205     }
206
207     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
208      * packet loss caused by scheduling problems */
209     i_opt = 0x80000;
210     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
211                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
212     {
213         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
214                           strerror(errno) );
215     }
216
217 #if defined(WIN32)
218     /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
219     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
220     {
221         struct sockaddr_in6 sockany = sock;
222         sockany.sin6_addr = in6addr_any;
223         sockany.sin6_scope_id = 0;
224
225         /* Bind it */
226         if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
227         {
228             msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
229             close( i_handle );
230             return 0;
231         }
232     }
233     else
234 #endif
235     /* Bind it */
236     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
237     {
238         msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
239         close( i_handle );
240         return 0;
241     }
242
243     /* Join the multicast group if the socket is a multicast address */
244     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
245     {
246         if(*psz_server_addr)
247         {
248             struct group_source_req imr;
249             struct sockaddr_in6 *p_sin6;
250
251             imr.gsr_interface = 0;
252             imr.gsr_group.ss_family = AF_INET6;
253             imr.gsr_source.ss_family = AF_INET6;
254             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
255             p_sin6->sin6_addr = sock.sin6_addr;
256
257             /* Build socket for remote connection */
258             msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);
259
260             if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
261             {
262                 msg_Warn( p_this, "cannot build remote address" );
263                 close( i_handle );
264                 return 0;
265             }
266             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
267             p_sin6->sin6_addr = sock.sin6_addr;
268
269             msg_Dbg( p_this, "IPV6_ADD_SOURCE_MEMBERSHIP multicast request" );
270             if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
271                           (char *)&imr, sizeof(struct group_source_req) ) == -1 )
272             {
273
274                 msg_Err( p_this, "failed to join IP multicast group (%s)",
275                                                           strerror(errno) );
276             }
277         }
278         else
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     if( *psz_server_addr )
300     {
301         int ttl;
302
303         /* Build socket for remote connection */
304         if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
305         {
306             msg_Warn( p_this, "cannot build remote address" );
307             close( i_handle );
308             return 0;
309         }
310
311         /* Connect the socket */
312         if( connect( i_handle, (struct sockaddr *) &sock,
313                      sizeof( sock ) ) == (-1) )
314         {
315             msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
316             close( i_handle );
317             return 0;
318         }
319
320         /* Set the time-to-live */
321         ttl = p_socket->i_ttl;
322         if( ttl <= 0 )
323             ttl = config_GetInt( p_this, "ttl" );
324
325         if( ttl > 0 )
326         {
327             if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
328             {
329                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
330                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
331                 {
332                     msg_Err( p_this, "failed to set multicast ttl (%s)",
333                              strerror(errno) );
334                 }
335             }
336             else
337             {
338                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
339                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
340                 {
341                     msg_Err( p_this, "failed to set unicast ttl (%s)",
342                               strerror(errno) );
343                 }
344             }
345         }
346
347         /* Set multicast output interface */
348         if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
349         {
350             char *psz_mif = config_GetPsz( p_this, "miface" );
351             if( psz_mif != NULL )
352             {
353                 int intf = if_nametoindex( psz_mif );
354                 free( psz_mif  );
355
356                 if( intf != 0 )
357                 {
358                     if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
359                         &intf, sizeof( intf ) ) < 0 )
360                     {
361                         msg_Err( p_this, "%s as multicast interface: %s",
362                                  psz_mif, strerror(errno) );
363                         close( i_handle );
364                         return 0;
365                     }
366                 }
367                 else
368                 {
369                     msg_Err( p_this, "%s: bad IPv6 interface specification",
370                              psz_mif );
371                     close( i_handle );
372                     return 0;
373                 }
374             }
375         }
376     }
377
378     p_socket->i_handle = i_handle;
379
380     var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
381     var_Get( p_this, "mtu", &val );
382     p_socket->i_mtu = val.i_int;
383
384     return 0;
385 }