]> git.sesse.net Git - vlc/blob - modules/misc/network/ipv4.c
5d96b1c41a1e479cd529f03b84722e2574807dfd
[vlc] / modules / misc / network / ipv4.c
1 /*****************************************************************************
2  * ipv4.c: IPv4 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ipv4.c,v 1.11 2003/01/02 23:50:55 massiot Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Mathias Kretschmer <mathias@research.att.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <string.h>
30 #include <vlc/vlc.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 #   include <sys/types.h>
34 #endif
35 #ifdef HAVE_SYS_STAT_H
36 #   include <sys/stat.h>
37 #endif
38 #ifdef HAVE_ERRNO_H
39 #   include <errno.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #   include <fcntl.h>
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
48 #   include <io.h>
49 #endif
50
51 #if defined( UNDER_CE )
52 #   include <winsock.h>
53 #elif defined( WIN32 )
54 #   include <winsock2.h>
55 #   include <ws2tcpip.h>
56 #   ifndef IN_MULTICAST
57 #       define IN_MULTICAST(a) IN_CLASSD(a)
58 #   endif
59 #else
60 #   include <netdb.h>                                         /* hostent ... */
61 #   include <sys/socket.h>
62 #   include <netinet/in.h>
63 #   ifdef HAVE_ARPA_INET_H
64 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
65 #   endif
66 #endif
67
68 #include "network.h"
69
70 /*****************************************************************************
71  * Local prototypes
72  *****************************************************************************/
73 static int NetOpen( vlc_object_t * );
74
75 /*****************************************************************************
76  * Module descriptor
77  *****************************************************************************/
78 vlc_module_begin();
79     set_description( _("IPv4 network abstraction layer") );
80     set_capability( "network", 50 );
81     set_callbacks( NetOpen, NULL );
82 vlc_module_end();
83
84 /*****************************************************************************
85  * BuildAddr: utility function to build a struct sockaddr_in
86  *****************************************************************************/
87 static int BuildAddr( struct sockaddr_in * p_socket,
88                       const char * psz_address, int i_port )
89 {
90     /* Reset struct */
91     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
92     p_socket->sin_family = AF_INET;                                /* family */
93     p_socket->sin_port = htons( (uint16_t)i_port );
94     if( !*psz_address )
95     {
96         p_socket->sin_addr.s_addr = INADDR_ANY;
97     }
98     else
99     {
100         struct hostent    * p_hostent;
101
102         /* Try to convert address directly from in_addr - this will work if
103          * psz_address is dotted decimal. */
104 #ifdef HAVE_ARPA_INET_H
105         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
106 #else
107         if( (p_socket->sin_addr.s_addr = inet_addr( psz_address )) == -1 )
108 #endif
109         {
110             /* We have a fqdn, try to find its address */
111             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
112             {
113                 return( -1 );
114             }
115
116             /* Copy the first address of the host in the socket address */
117             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
118                      p_hostent->h_length );
119         }
120     }
121     return( 0 );
122 }
123
124 /*****************************************************************************
125  * OpenUDP: open a UDP socket
126  *****************************************************************************
127  * psz_bind_addr, i_bind_port : address and port used for the bind()
128  *   system call. If psz_bind_addr == "", the socket is bound to
129  *   INADDR_ANY and broadcast reception is enabled. If i_bind_port == 0,
130  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
131  *   join the multicast group.
132  * psz_server_addr, i_server_port : address and port used for the connect()
133  *   system call. It can avoid receiving packets from unauthorized IPs.
134  *   Its use leads to great confusion and is currently discouraged.
135  * This function returns -1 in case of error.
136  *****************************************************************************/
137 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
138 {
139     char * psz_bind_addr = p_socket->psz_bind_addr;
140     int i_bind_port = p_socket->i_bind_port;
141     char * psz_server_addr = p_socket->psz_server_addr;
142     int i_server_port = p_socket->i_server_port;
143 #if defined( WIN32 ) && !defined( UNDER_CE )
144     char * psz_bind_win32;        /* WIN32 multicast kludge */
145 #endif
146
147     int i_handle, i_opt;
148     socklen_t i_opt_size;
149     struct sockaddr_in sock;
150
151     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
152      * protocol */
153     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
154     {
155 #ifdef HAVE_ERRNO_H
156         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
157 #else
158         msg_Err( p_this, "cannot create socket" );
159 #endif
160         return( -1 );
161     }
162
163     /* We may want to reuse an already used socket */
164     i_opt = 1;
165     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
166                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
167     {
168 #ifdef HAVE_ERRNO_H
169         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
170                           strerror(errno));
171 #else
172         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR)" );
173 #endif
174 #if defined( WIN32 ) || defined( UNDER_CE )
175         closesocket( i_handle );
176 #else
177         close( i_handle );
178 #endif
179         return( -1 );
180     }
181
182     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
183      * packet loss caused by scheduling problems */
184     i_opt = 0x80000;
185 #if defined( SYS_BEOS )
186     if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK,
187 #else
188     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
189 #endif
190                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
191     {
192 #ifdef HAVE_ERRNO_H
193         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
194                           strerror(errno));
195 #else
196         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF)" );
197 #endif
198     }
199
200     /* Check if we really got what we have asked for, because Linux, etc.
201      * will silently limit the max buffer size to net.core.rmem_max which
202      * is typically only 65535 bytes */
203     i_opt = 0;
204     i_opt_size = sizeof( i_opt );
205 #if defined( SYS_BEOS )
206     if( getsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK,
207 #else
208     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
209 #endif
210                     (void*) &i_opt, &i_opt_size ) == -1 )
211     {
212 #ifdef HAVE_ERRNO_H
213         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
214                           strerror(errno) );
215 #else
216         msg_Warn( p_this, "cannot query socket (SO_RCVBUF)" );
217 #endif
218     }
219     else if( i_opt < 0x80000 )
220     {
221         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
222                           i_opt, 0x80000 );
223     }
224
225
226     /* Build the local socket */
227
228 #if defined( WIN32 ) && !defined( UNDER_CE )
229     /* Under Win32 and for the multicast, we bind on INADDR_ANY,
230      * so let's call BuildAddr with "" instead of psz_bind_addr */
231     psz_bind_win32 = psz_bind_addr ;
232
233     /* Check if this is a multicast socket */
234     if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
235     {
236         psz_bind_win32 = "";
237     }
238     if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 )
239 #else
240     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
241 #endif
242     {
243         msg_Dbg( p_this, "could not build local address" );
244 #if defined( WIN32 ) || defined( UNDER_CE )
245         closesocket( i_handle );
246 #else
247         close( i_handle );
248 #endif
249         return( -1 );
250     }
251
252     /* Bind it */
253     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
254     {
255 #ifdef HAVE_ERRNO_H
256         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
257 #else
258         msg_Err( p_this, "cannot bind socket" );
259 #endif
260 #if defined( WIN32 ) || defined( UNDER_CE )
261         closesocket( i_handle );
262 #else
263         close( i_handle );
264 #endif
265         return( -1 );
266     }
267
268     /* Allow broadcast reception if we bound on INADDR_ANY */
269     if( !*psz_bind_addr )
270     {
271         i_opt = 1;
272 #if defined( SYS_BEOS )
273         if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK,
274 #else
275         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
276 #endif
277                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
278         {
279 #ifdef HAVE_ERRNO_H
280             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
281                        strerror(errno) );
282 #else
283             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST)" );
284 #endif
285         }
286     }
287
288 #if !defined( UNDER_CE ) && !defined( SYS_BEOS )
289     /* Join the multicast group if the socket is a multicast address */
290 #ifndef IN_MULTICAST
291 #   define IN_MULTICAST(a)         IN_CLASSD(a)
292 #endif
293
294 #ifndef WIN32
295     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
296     {
297         struct ip_mreq imr;
298         char * psz_if_addr = config_GetPsz( p_this, "iface-addr" );
299         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
300 #else
301     if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) )
302     {
303         struct ip_mreq imr;
304         char * psz_if_addr = config_GetPsz( p_this, "iface-addr" );
305         imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr);
306 #endif
307         if ( psz_if_addr != NULL && *psz_if_addr
308               && inet_addr(psz_if_addr) != -1 )
309         {
310             imr.imr_interface.s_addr = inet_addr(psz_if_addr);
311         }
312         else
313         {
314             imr.imr_interface.s_addr = INADDR_ANY;
315         }
316         if ( psz_if_addr != NULL ) free( psz_if_addr );
317
318         if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
319                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
320         {
321 #ifdef HAVE_ERRNO_H
322             msg_Warn( p_this, "failed to join IP multicast group (%s)",
323                               strerror(errno) );
324 #else
325             msg_Warn( p_this, "failed to join IP multicast group" );
326 #endif
327 #if defined( WIN32 ) || defined( UNDER_CE )
328             closesocket( i_handle );
329 #else
330             close( i_handle );
331 #endif
332             return( -1 );
333         }
334     }
335 #endif /* UNDER_CE */
336
337     if( *psz_server_addr )
338     {
339         /* Build socket for remote connection */
340         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
341         {
342             msg_Err( p_this, "cannot build remote address" );
343 #if defined( WIN32 ) || defined( UNDER_CE )
344             closesocket( i_handle );
345 #else
346             close( i_handle );
347 #endif
348             return( -1 );
349         }
350
351         /* Connect the socket */
352         if( connect( i_handle, (struct sockaddr *) &sock,
353                      sizeof( sock ) ) == (-1) )
354         {
355 #ifdef HAVE_ERRNO_H
356             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
357 #else
358             msg_Err( p_this, "cannot connect socket" );
359 #endif
360 #if defined( WIN32 ) || defined( UNDER_CE )
361             closesocket( i_handle );
362 #else
363             close( i_handle );
364 #endif
365             return( -1 );
366         }
367     }
368
369     p_socket->i_handle = i_handle;
370     p_socket->i_mtu = config_GetInt( p_this, "mtu" );
371     return( 0 );
372 }
373
374 /*****************************************************************************
375  * OpenTCP: open a TCP socket
376  *****************************************************************************
377  * psz_server_addr, i_server_port : address and port used for the connect()
378  *   system call. If i_server_port == 0, 80 is used.
379  * Other parameters are ignored.
380  * This function returns -1 in case of error.
381  *****************************************************************************/
382 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
383 {
384     char * psz_server_addr = p_socket->psz_server_addr;
385     int i_server_port = p_socket->i_server_port;
386
387     int i_handle;
388     struct sockaddr_in sock;
389
390     if( i_server_port == 0 )
391     {
392         i_server_port = 80;
393     }
394
395     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
396      * protocol */
397     if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
398     {
399 #ifdef HAVE_ERRNO_H
400         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
401 #else
402         msg_Err( p_this, "cannot create socket" );
403 #endif
404         return( -1 );
405     }
406
407     /* Build remote address */
408     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
409     {
410         msg_Dbg( p_this, "could not build local address" );
411 #if defined( WIN32 ) || defined( UNDER_CE )
412         closesocket( i_handle );
413 #else
414         close( i_handle );
415 #endif
416         return( -1 );
417     }
418
419     /* Connect the socket */
420     if( connect( i_handle, (struct sockaddr *) &sock,
421                  sizeof( sock ) ) == (-1) )
422     {
423 #ifdef HAVE_ERRNO_H
424         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
425 #else
426         msg_Err( p_this, "cannot connect socket" );
427 #endif
428 #if defined( WIN32 ) || defined( UNDER_CE )
429         closesocket( i_handle );
430 #else
431         close( i_handle );
432 #endif
433         return( -1 );
434     }
435
436     p_socket->i_handle = i_handle;
437     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
438
439     return( 0 );
440 }
441
442 /*****************************************************************************
443  * NetOpen: wrapper around OpenUDP and OpenTCP
444  *****************************************************************************/
445 static int NetOpen( vlc_object_t * p_this )
446 {
447     network_socket_t * p_socket = p_this->p_private;
448
449     if( p_socket->i_type == NETWORK_UDP )
450     {
451         return OpenUDP( p_this, p_socket );
452     }
453     else
454     {
455         return OpenTCP( p_this, p_socket );
456     }
457 }