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