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