]> git.sesse.net Git - vlc/blob - modules/misc/network/ipv6.c
Do not define constant manually without platform check!
[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
42 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
43 # define close closesocket
44
45 #ifndef MCAST_JOIN_SOURCE_GROUP
46 /*
47  * I hate manual definitions: Error-prone. Portability hell.
48  * Developers shall use UP-TO-DATE compilers. Full point.
49  * If you remove the warning, you remove the whole ifndef.
50  */
51 # warning Your C headers are out-of-date. Please update.
52
53 /* Most (all?) Mingw32 versions in use are yet to pick up Vista stuff */
54 #  define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
55 struct group_source_req
56 {
57        uint32_t           gsr_interface;  /* interface index */
58        struct sockaddr_storage gsr_group;      /* group address */
59        struct sockaddr_storage gsr_source;     /* source address */
60 };
61 #endif
62
63 #else
64 #   include <sys/types.h>
65 #   include <unistd.h>
66 #   include <netdb.h>                                         /* hostent ... */
67 #   include <sys/socket.h>
68 #   include <netinet/in.h>
69 #   include <net/if.h>
70 #endif
71
72 #include "network.h"
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 static int OpenUDP( vlc_object_t * );
78
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82 vlc_module_begin();
83     set_description( _("UDP/IPv6 network abstraction layer") );
84     set_capability( "network", 40 );
85     set_callbacks( OpenUDP, NULL );
86 vlc_module_end();
87
88 /*****************************************************************************
89  * BuildAddr: utility function to build a struct sockaddr_in6
90  *****************************************************************************/
91 static int BuildAddr( vlc_object_t *p_this, struct sockaddr_in6 *p_socket,
92                       const char *psz_address, int i_port )
93 {
94     struct addrinfo hints, *res;
95     int i;
96
97     memset( &hints, 0, sizeof( hints ) );
98     hints.ai_family = AF_INET6;
99     hints.ai_socktype = SOCK_DGRAM;
100     hints.ai_flags = AI_PASSIVE;
101
102     i = vlc_getaddrinfo( p_this, psz_address, 0, &hints, &res );
103     if( i )
104     {
105         msg_Dbg( p_this, "%s: %s", psz_address, vlc_gai_strerror( i ) );
106         return -1;
107     }
108     if ( res->ai_addrlen > sizeof (struct sockaddr_in6) )
109     {
110         vlc_freeaddrinfo( res );
111         return -1;
112     }
113
114     memcpy( p_socket, res->ai_addr, res->ai_addrlen );
115     vlc_freeaddrinfo( res );
116     p_socket->sin6_port = htons( i_port );
117
118     return 0;
119 }
120
121 #if defined(WIN32) || defined(UNDER_CE)
122 # define WINSOCK_STRERROR_SIZE 20
123 static const char *winsock_strerror( char *buf )
124 {
125     snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
126               WSAGetLastError( ) );
127     buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
128     return buf;
129 }
130 #endif
131
132
133 /*****************************************************************************
134  * OpenUDP: open a UDP socket
135  *****************************************************************************
136  * psz_bind_addr, i_bind_port : address and port used for the bind()
137  *   system call. If psz_bind_addr == NULL, the socket is bound to
138  *   in6addr_any and broadcast reception is enabled. If psz_bind_addr is a
139  *   multicast (FF00::/8) address, join the multicast group.
140  * psz_server_addr, i_server_port : address and port used for the connect()
141  *   system call. It can avoid receiving packets from unauthorized IPs.
142  *   Its use leads to great confusion and is currently discouraged.
143  * This function returns -1 in case of error.
144  *****************************************************************************/
145 static int OpenUDP( vlc_object_t * p_this )
146 {
147     network_socket_t *p_socket = p_this->p_private;
148     const char *psz_bind_addr = p_socket->psz_bind_addr;
149     int i_bind_port = p_socket->i_bind_port;
150     const char *psz_server_addr = p_socket->psz_server_addr;
151     int i_server_port = p_socket->i_server_port;
152     int i_handle, i_opt;
153     struct sockaddr_in6 sock;
154     vlc_value_t val;
155 #if defined(WIN32) || defined(UNDER_CE)
156     char strerror_buf[WINSOCK_STRERROR_SIZE];
157 # define strerror( x ) winsock_strerror( strerror_buf )
158 #endif
159
160     p_socket->i_handle = -1;
161
162     /* Build the local socket */
163     if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )        
164         return 0;
165
166     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
167      * protocol */
168     if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
169     {
170         msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
171         return 0;
172     }
173
174 #ifdef IPV6_V6ONLY
175     val.i_int = p_socket->v6only;
176
177     if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val.i_int,
178                     sizeof( val.i_int ) ) )
179     {
180         msg_Warn( p_this, "IPV6_V6ONLY: %s", strerror( errno ) );
181         p_socket->v6only = 1;
182     }
183 #else
184     p_socket->v6only = 1;
185 #endif
186
187 #ifdef WIN32
188 # ifndef IPV6_PROTECTION_LEVEL
189 #   define IPV6_PROTECTION_LEVEL 23
190 #  endif
191     {
192         int i_val = 10 /*PROTECTION_LEVEL_UNRESTRICTED*/;
193         setsockopt( i_handle, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
194                     sizeof( i_val ) );
195     }
196 #endif
197
198     /* We may want to reuse an already used socket */
199     i_opt = 1;
200     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
201                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
202     {
203         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
204                          strerror(errno) );
205         close( i_handle );
206         return 0;
207     }
208
209     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
210      * packet loss caused by scheduling problems */
211     i_opt = 0x80000;
212     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
213                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
214     {
215         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
216                           strerror(errno) );
217     }
218
219 #if defined(WIN32)
220     /* Under Win32 and for multicasting, we bind to IN6ADDR_ANY */
221     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
222     {
223         struct sockaddr_in6 sockany = sock;
224         sockany.sin6_addr = in6addr_any;
225         sockany.sin6_scope_id = 0;
226
227         /* Bind it */
228         if( bind( i_handle, (struct sockaddr *)&sockany, sizeof( sock ) ) < 0 )
229         {
230             msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
231             close( i_handle );
232             return 0;
233         }
234     }
235     else
236 #endif
237     /* Bind it */
238     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
239     {
240         msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) );
241         close( i_handle );
242         return 0;
243     }
244
245     /* Join the multicast group if the socket is a multicast address */
246     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
247     {
248         if(*psz_server_addr)
249         {
250             struct group_source_req imr;
251             struct sockaddr_in6 *p_sin6;
252
253             imr.gsr_interface = 0;
254             imr.gsr_group.ss_family = AF_INET6;
255             imr.gsr_source.ss_family = AF_INET6;
256             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_group;
257             p_sin6->sin6_addr = sock.sin6_addr;
258
259             /* Build socket for remote connection */
260             msg_Dbg( p_this, "psz_server_addr : %s", psz_server_addr);
261
262             if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) )
263             {
264                 msg_Warn( p_this, "cannot build remote address" );
265                 close( i_handle );
266                 return 0;
267             }
268             p_sin6 = (struct sockaddr_in6 *)&imr.gsr_source;
269             p_sin6->sin6_addr = sock.sin6_addr;
270
271             msg_Dbg( p_this, "MCAST_JOIN_SOURCE_GROUP multicast request" );
272             if( setsockopt( i_handle, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP,
273                           (char *)&imr, sizeof(struct group_source_req) ) == -1 )
274             {
275
276                 msg_Err( p_this, "Source specific multicast failed (%s) -"
277                           " check if your OS really supports MLDv2",
278                           strerror(errno) );
279             }
280         }
281         else
282         {
283             struct ipv6_mreq     imr;
284             int                  res;
285
286             imr.ipv6mr_interface = sock.sin6_scope_id;
287             imr.ipv6mr_multiaddr = sock.sin6_addr;
288             msg_Dbg( p_this, "IPV6_JOIN_GROUP multicast request" );
289             res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
290 #if defined(WIN32)
291                          sizeof(imr) + 4); /* Doesn't work without this */
292 #else
293                          sizeof(imr));
294 #endif
295
296             if( res == -1 )
297             {
298                 msg_Err( p_this, "cannot join multicast group" );
299             } 
300         }
301     }
302     else
303     if( *psz_server_addr )
304     {
305         int ttl;
306
307         /* Build socket for remote connection */
308         if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
309         {
310             msg_Warn( p_this, "cannot build remote address" );
311             close( i_handle );
312             return 0;
313         }
314
315         /* Connect the socket */
316         if( connect( i_handle, (struct sockaddr *) &sock,
317                      sizeof( sock ) ) == (-1) )
318         {
319             msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
320             close( i_handle );
321             return 0;
322         }
323
324         /* Set the time-to-live */
325         ttl = p_socket->i_ttl;
326         if( ttl <= 0 )
327             ttl = config_GetInt( p_this, "ttl" );
328
329         if( ttl > 0 )
330         {
331             if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
332             {
333                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
334                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
335                 {
336                     msg_Err( p_this, "failed to set multicast ttl (%s)",
337                              strerror(errno) );
338                 }
339             }
340             else
341             {
342                 if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
343                                 (void *)&ttl, sizeof( ttl ) ) < 0 )
344                 {
345                     msg_Err( p_this, "failed to set unicast ttl (%s)",
346                               strerror(errno) );
347                 }
348             }
349         }
350
351         /* Set multicast output interface */
352         if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
353         {
354             char *psz_mif = config_GetPsz( p_this, "miface" );
355             if( psz_mif != NULL )
356             {
357                 int intf = if_nametoindex( psz_mif );
358                 free( psz_mif  );
359
360                 if( intf != 0 )
361                 {
362                     if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
363                         &intf, sizeof( intf ) ) < 0 )
364                     {
365                         msg_Err( p_this, "%s as multicast interface: %s",
366                                  psz_mif, strerror(errno) );
367                         close( i_handle );
368                         return 0;
369                     }
370                 }
371                 else
372                 {
373                     msg_Err( p_this, "%s: bad IPv6 interface specification",
374                              psz_mif );
375                     close( i_handle );
376                     return 0;
377                 }
378             }
379         }
380     }
381
382     p_socket->i_handle = i_handle;
383
384     var_Create( p_this, "mtu", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
385     var_Get( p_this, "mtu", &val );
386     p_socket->i_mtu = val.i_int;
387
388     return 0;
389 }