glibc 2.1 port for linupy (linux on a yopy pda)
[vlc.git] / modules / misc / network / ipv6.c
1 /*****************************************************************************
2  * ipv6.c: IPv6 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: ipv6.c,v 1.8 2003/02/07 23:36:55 marcari Exp $
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 #   include <net/if.h>
52 #   ifdef HAVE_ARPA_INET_H
53 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
54 #   endif
55 #endif
56
57 #include "network.h"
58
59 #if defined(WIN32)
60 static const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
61 /* the following will have to be removed when w32api defines them */
62 #ifndef IPPROTO_IPV6
63 #   define IPPROTO_IPV6 41 
64 #endif
65 #ifndef IPV6_JOIN_GROUP
66 #   define IPV6_JOIN_GROUP 20
67 #endif
68 #endif
69
70 /*****************************************************************************
71  * Local prototypes
72  *****************************************************************************/
73 static int Open( vlc_object_t * );
74
75 /*****************************************************************************
76  * Module descriptor
77  *****************************************************************************/
78 vlc_module_begin();
79     set_description( _("IPv6 network abstraction layer") );
80     set_capability( "network", 40 );
81     set_callbacks( Open, NULL );
82 vlc_module_end();
83
84 /*****************************************************************************
85  * BuildAddr: utility function to build a struct sockaddr_in6
86  *****************************************************************************/
87 static int BuildAddr( vlc_object_t * p_this, struct sockaddr_in6 * p_socket,
88                       const char * psz_bind_address, int i_port )
89 {
90     char * psz_multicast_interface = "";
91     char * psz_backup = strdup(psz_bind_address);
92     char * psz_address = psz_backup;
93
94 #if defined(WIN32)
95     /* Try to get getaddrinfo() and freeaddrinfo() from wship6.dll */
96     typedef int (CALLBACK * GETADDRINFO) ( const char *nodename,
97                                             const char *servname,
98                                             const struct addrinfo *hints,
99                                             struct addrinfo **res );
100     typedef void (CALLBACK * FREEADDRINFO) ( struct addrinfo FAR *ai );
101
102     struct addrinfo hints, *res;
103     GETADDRINFO _getaddrinfo = NULL;
104     FREEADDRINFO _freeaddrinfo = NULL;
105
106     HINSTANCE wship6_dll = LoadLibrary("wship6.dll");
107     if( wship6_dll )
108     {
109         _getaddrinfo = (GETADDRINFO) GetProcAddress( wship6_dll,
110                                                      "getaddrinfo" );
111         _freeaddrinfo = (FREEADDRINFO) GetProcAddress( wship6_dll,
112                                                        "freeaddrinfo" );
113     }
114     if( !_getaddrinfo || !_freeaddrinfo )
115     {
116         msg_Err( p_this, "no IPv6 stack installed" );
117         if( wship6_dll ) FreeLibrary( wship6_dll );
118         free( psz_backup );
119         return( -1 );
120     }
121 #endif
122
123     /* Reset struct */
124     memset( p_socket, 0, sizeof( struct sockaddr_in6 ) );
125     p_socket->sin6_family = AF_INET6;                              /* family */
126     p_socket->sin6_port = htons( i_port );
127     if( !*psz_address )
128     {
129         p_socket->sin6_addr = in6addr_any;
130     }
131     else if( psz_address[0] == '['
132               && psz_address[strlen(psz_address) - 1] == ']' )
133     {
134         psz_address++;
135         /* see if there is an interface name in there... */
136         if( (psz_multicast_interface = strchr(psz_address, '%')) != NULL )
137         {
138             *psz_multicast_interface = '\0';
139             psz_multicast_interface++;
140             msg_Dbg( p_this, "Interface name specified: \"%s\"",
141                              psz_multicast_interface );
142
143             /* now convert that interface name to an index */
144 #if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
145 #   if !defined( WIN32 )
146             p_socket->sin6_scope_id = if_nametoindex(psz_multicast_interface);
147 #   else
148             /* FIXME: for now we always use the default interface */
149             p_socket->sin6_scope_id = 0;
150 #   endif
151             msg_Warn( p_this, " = #%i", p_socket->sin6_scope_id );
152 #endif
153         }
154         psz_address[strlen(psz_address) - 1] = '\0' ;
155
156 #if !defined( WIN32 )
157         inet_pton(AF_INET6, psz_address, &p_socket->sin6_addr.s6_addr); 
158
159 #else
160         memset(&hints, 0, sizeof(hints));
161         hints.ai_family = AF_INET6;
162         hints.ai_flags = AI_NUMERICHOST;
163
164         if( _getaddrinfo( psz_address, NULL, &hints, &res ) )
165         {
166             FreeLibrary( wship6_dll );
167             free( psz_backup );
168             return( -1 );
169         }
170         memcpy( &p_socket->sin6_addr,
171                 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
172                 sizeof(struct in6_addr) );
173         _freeaddrinfo( res );
174
175 #endif
176     }
177     else
178     {
179 #ifdef HAVE_GETHOSTBYNAME2
180         struct hostent    * p_hostent;
181
182         /* We have a fqdn, try to find its address */
183         if ( (p_hostent = gethostbyname2( psz_address, AF_INET6 )) == NULL )
184         {
185             msg_Err( p_this, "ipv6 error: unknown host %s", psz_address );
186             free( psz_backup );
187             return( -1 );
188         }
189
190         /* Copy the first address of the host in the socket address */
191         memcpy( &p_socket->sin6_addr, p_hostent->h_addr_list[0],
192                  p_hostent->h_length );
193
194 #elif defined(WIN32)
195         if( _getaddrinfo( psz_address, NULL, &hints, &res ) )
196         {
197             FreeLibrary( wship6_dll );
198             free( psz_backup );
199             return( -1 );
200         }
201         memcpy( &p_socket->sin6_addr,
202                 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
203                 sizeof(struct in6_addr) );
204         _freeaddrinfo( res );
205
206 #else
207         msg_Err( p_this, "ipv6 error: IPv6 address %s is invalid",
208                  psz_address );
209         free( psz_backup );
210         return( -1 );
211 #endif
212     }
213
214 #if defined(WIN32)
215     FreeLibrary( wship6_dll );
216 #endif
217
218     free( psz_backup );
219     return 0;
220 }
221
222 /*****************************************************************************
223  * OpenUDP: open a UDP socket
224  *****************************************************************************
225  * psz_bind_addr, i_bind_port : address and port used for the bind()
226  *   system call. If psz_bind_addr == NULL, the socket is bound to
227  *   in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
228  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
229  *   join the multicast group.
230  * psz_server_addr, i_server_port : address and port used for the connect()
231  *   system call. It can avoid receiving packets from unauthorized IPs.
232  *   Its use leads to great confusion and is currently discouraged.
233  * This function returns -1 in case of error.
234  *****************************************************************************/
235 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
236 {
237     char * psz_bind_addr = p_socket->psz_bind_addr;
238     int i_bind_port = p_socket->i_bind_port;
239     char * psz_server_addr = p_socket->psz_server_addr;
240     int i_server_port = p_socket->i_server_port;
241
242     int i_handle, i_opt;
243     socklen_t i_opt_size;
244     struct sockaddr_in6 sock;
245
246     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
247      * protocol */
248     if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
249     {
250         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
251         return( -1 );
252     }
253
254     /* We may want to reuse an already used socket */
255     i_opt = 1;
256     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
257                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
258     {
259         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
260                          strerror(errno) );
261         close( i_handle );
262         return( -1 );
263     }
264
265     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
266      * packet loss caused by scheduling problems */
267     i_opt = 0x80000;
268     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
269                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
270     {
271         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
272                           strerror(errno) );
273     }
274  
275     /* Check if we really got what we have asked for, because Linux, etc.
276      * will silently limit the max buffer size to net.core.rmem_max which
277      * is typically only 65535 bytes */
278     i_opt = 0;
279     i_opt_size = sizeof( i_opt );
280     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
281                     (void*) &i_opt, &i_opt_size ) == -1 )
282     {
283         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
284                           strerror(errno) );
285     }
286     else if( i_opt < 0x80000 )
287     {
288         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
289                           i_opt, 0x80000 );
290     }
291     
292     /* Build the local socket */
293     if ( BuildAddr( p_this, &sock, psz_bind_addr, i_bind_port ) == -1 )        
294     {
295         close( i_handle );
296         return( -1 );
297     }
298  
299     /* Bind it */
300     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
301     {
302         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
303         close( i_handle );
304         return( -1 );
305     }
306
307     /* Allow broadcast reception if we bound on in6addr_any */
308     if( !*psz_bind_addr )
309     {
310         i_opt = 1;
311         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
312                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
313         {
314             msg_Warn( p_this, "ipv6 warning: cannot configure socket "
315                               "(SO_BROADCAST: %s)", strerror(errno) );
316         }
317     }
318  
319     /* Join the multicast group if the socket is a multicast address */
320 #if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
321     if( IN6_IS_ADDR_MULTICAST(&sock.sin6_addr) )
322     {
323         struct ipv6_mreq     imr;
324         int                  res;
325
326         imr.ipv6mr_interface = sock.sin6_scope_id;
327         imr.ipv6mr_multiaddr = sock.sin6_addr;
328         res = setsockopt(i_handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &imr,
329                          sizeof(imr));
330
331         if( res == -1 )
332         {
333             msg_Err( p_this, "setsockopt JOIN_GROUP failed" );
334         } 
335     }
336 #else
337     msg_Warn( p_this, "setsockopt JOIN_GROUP not supported with glibc < 2.2" );
338 #endif
339
340
341     if( *psz_server_addr )
342     {
343         /* Build socket for remote connection */
344         if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
345         {
346             msg_Err( p_this, "cannot build remote address" );
347             close( i_handle );
348             return( -1 );
349         }
350  
351         /* Connect the socket */
352         if( connect( i_handle, (struct sockaddr *) &sock,
353                      sizeof( sock ) ) == (-1) )
354         {
355             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
356             close( i_handle );
357             return( -1 );
358         }
359     }
360
361     p_socket->i_handle = i_handle;
362     p_socket->i_mtu = config_GetInt( p_this, "mtu" );
363
364     return( 0 );
365 }
366
367 /*****************************************************************************
368  * OpenTCP: open a TCP socket
369  *****************************************************************************
370  * psz_server_addr, i_server_port : address and port used for the connect()
371  *   system call. If i_server_port == 0, 80 is used.
372  * Other parameters are ignored.
373  * This function returns -1 in case of error.
374  *****************************************************************************/
375 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
376 {
377     char * psz_server_addr = p_socket->psz_server_addr;
378     int i_server_port = p_socket->i_server_port;
379
380     int i_handle;
381     struct sockaddr_in6 sock;
382
383     if( i_server_port == 0 )
384     {
385         i_server_port = 80;
386     }
387
388     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET6 domain, automatic (0)
389      * protocol */
390     if( (i_handle = socket( AF_INET6, SOCK_STREAM, 0 )) == -1 )
391     {
392         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
393         return( -1 );
394     }
395
396     /* Build remote address */
397     if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
398     {
399         close( i_handle );
400         return( -1 );
401     }
402
403     /* Connect the socket */
404     if( connect( i_handle, (struct sockaddr *) &sock,
405                  sizeof( sock ) ) == (-1) )
406     {
407         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
408         close( i_handle );
409         return( -1 );
410     }
411
412     p_socket->i_handle = i_handle;
413     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
414
415     return( 0 );
416 }
417
418 /*****************************************************************************
419  * Open: wrapper around OpenUDP and OpenTCP
420  *****************************************************************************/
421 static int Open( vlc_object_t * p_this )
422 {
423     network_socket_t * p_socket = p_this->p_private;
424
425     if( p_socket->i_type == NETWORK_UDP )
426     {
427         return OpenUDP( p_this, p_socket );
428     }
429     else
430     {
431         return OpenTCP( p_this, p_socket );
432     }
433 }