]> git.sesse.net Git - vlc/blob - plugins/network/ipv4.c
293276f3d2da462bfec3dc2b8af0988a8d22d023
[vlc] / plugins / network / ipv4.c
1 /*****************************************************************************
2  * ipv4.c: IPv4 network abstraction layer
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ipv4.c,v 1.3 2002/03/02 03:53:55 xav Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <videolan/vlc.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #elif defined( _MSC_VER ) && defined( _WIN32 )
39 #   include <io.h>
40 #endif
41
42 #ifdef WIN32
43 #   include <winsock2.h>
44 #   include <ws2tcpip.h>
45 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
46 #   include <netdb.h>                                         /* hostent ... */
47 #   include <sys/socket.h>
48 #   include <netinet/in.h>
49 #   ifdef HAVE_ARPA_INET_H
50 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
51 #   endif
52 #endif
53
54 #include "network.h"
55
56 /* Default MTU used for UDP socket. FIXME: we should issue some ioctl()
57  * call to get that value from the interface driver. */
58 #define DEFAULT_MTU 1500
59
60 /*****************************************************************************
61  * Local prototypes
62  *****************************************************************************/
63 static void getfunctions( function_list_t * );
64 static int  NetworkOpen( struct network_socket_s * );
65
66 /*****************************************************************************
67  * Build configuration tree.
68  *****************************************************************************/
69 MODULE_CONFIG_START
70 MODULE_CONFIG_STOP
71  
72 MODULE_INIT_START
73     SET_DESCRIPTION( "IPv4 network abstraction layer" )
74     ADD_CAPABILITY( NETWORK, 50 )
75     ADD_SHORTCUT( "ipv4" )
76 MODULE_INIT_STOP
77  
78 MODULE_ACTIVATE_START
79     getfunctions( &p_module->p_functions->network );
80 MODULE_ACTIVATE_STOP
81  
82 MODULE_DEACTIVATE_START
83 MODULE_DEACTIVATE_STOP
84
85 /*****************************************************************************
86  * Functions exported as capabilities. They are declared as static so that
87  * we don't pollute the namespace too much.
88  *****************************************************************************/
89 static void getfunctions( function_list_t * p_function_list )
90 {
91 #define f p_function_list->functions.network
92     f.pf_open = NetworkOpen;
93 #undef f
94 }
95
96 /*****************************************************************************
97  * BuildAddr: utility function to build a struct sockaddr_in
98  *****************************************************************************/
99 static int BuildAddr( struct sockaddr_in * p_socket,
100                       const char * psz_address, int i_port )
101 {
102     /* Reset struct */
103     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
104     p_socket->sin_family = AF_INET;                                /* family */
105     p_socket->sin_port = htons( i_port );
106     if( psz_address == NULL )
107     {
108         p_socket->sin_addr.s_addr = INADDR_ANY;
109     }
110     else
111     {
112         struct hostent    * p_hostent;
113  
114         /* Try to convert address directly from in_addr - this will work if
115          * psz_address is dotted decimal. */
116 #ifdef HAVE_ARPA_INET_H
117         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
118 #else
119         if( (p_socket->sin_addr.s_addr = inet_addr( psz_address )) == -1 )
120 #endif
121         {
122             /* We have a fqdn, try to find its address */
123             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
124             {
125                 intf_ErrMsg( "BuildLocalAddr: unknown host %s", psz_address );
126                 return( -1 );
127             }
128
129             /* Copy the first address of the host in the socket address */
130             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
131                      p_hostent->h_length );
132         }
133     }
134     return( 0 );
135 }
136
137 /*****************************************************************************
138  * OpenUDP: open a UDP socket
139  *****************************************************************************
140  * psz_bind_addr, i_bind_port : address and port used for the bind()
141  *   system call. If psz_bind_addr == NULL, the socket is bound to
142  *   INADDR_ANY and broadcast reception is enabled. If i_bind_port == 0,
143  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
144  *   join the multicast group.
145  * psz_server_addr, i_server_port : address and port used for the connect()
146  *   system call. It can avoid receiving packets from unauthorized IPs.
147  *   Its use leads to great confusion and is currently discouraged.
148  * This function returns -1 in case of error.
149  *****************************************************************************/
150 static int OpenUDP( network_socket_t * p_socket )
151 {
152     char * psz_bind_addr = p_socket->psz_bind_addr;
153     int i_bind_port = p_socket->i_bind_port;
154     char * psz_server_addr = p_socket->psz_server_addr;
155     int i_server_port = p_socket->i_server_port;
156     char * psz_bind_win32;              /* WIN32 multicast kludge */
157
158     int i_handle, i_opt, i_opt_size;
159     struct sockaddr_in sock;
160
161     if( i_bind_port == 0 )
162     {
163         i_bind_port = config_GetIntVariable( INPUT_PORT_VAR );
164     }
165
166     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
167      * protocol */
168     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
169     {
170         intf_ErrMsg( "ipv4 error: cannot create socket (%s)", strerror(errno) );
171         return( -1 );
172     }
173
174     /* We may want to reuse an already used socket */
175     i_opt = 1;
176     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
177                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
178     {
179         intf_ErrMsg( "ipv4 error: cannot configure socket (SO_REUSEADDR: %s)",
180                      strerror(errno));
181         close( i_handle );
182         return( -1 );
183     }
184
185     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
186      * packet loss caused by scheduling problems */
187     i_opt = 0x80000;
188     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
189                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
190     {
191         intf_WarnMsg( 1,
192                       "ipv4 warning: cannot configure socket (SO_RCVBUF: %s)",
193                       strerror(errno));
194     }
195  
196     /* Check if we really got what we have asked for, because Linux, etc.
197      * will silently limit the max buffer size to net.core.rmem_max which
198      * is typically only 65535 bytes */
199     i_opt = 0;
200     i_opt_size = sizeof( i_opt );
201     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
202                     (void*) &i_opt, &i_opt_size ) == -1 )
203     {
204         intf_WarnMsg( 1, "ipv4 warning: cannot query socket (SO_RCVBUF: %s)",
205                          strerror(errno));
206     }
207     else if( i_opt < 0x80000 )
208     {
209         intf_WarnMsg( 1, "ipv4 warning: socket buffer size is 0x%x"
210                          " instead of 0x%x", i_opt, 0x80000 );
211     }
212     
213 /* Under Win32 and for the multicast, we bind on INADDR_ANY, so let's call BuildAddr with NULL instead of psz_bind_addr */
214     
215     /* Build the local socket */
216
217 #ifdef WIN32
218     
219 #ifndef IN_MULTICAST
220 #   define IN_MULTICAST(a)         IN_CLASSD(a)
221 #endif
222
223     psz_bind_win32 = psz_bind_addr ;
224     
225 /* Check if this is a multicast socket */
226
227     if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
228     {
229             psz_bind_win32 = NULL ;
230     }
231     if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 )
232 #else
233     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )     
234 #endif  
235     {
236         close( i_handle );
237         return( -1 );
238     }
239  
240     /* Bind it */
241     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
242     {
243         intf_ErrMsg( "ipv4 error: cannot bind socket (%s)", strerror(errno) );
244         close( i_handle );
245         return( -1 );
246     }
247
248     /* Allow broadcast reception if we bound on INADDR_ANY */
249     if( psz_bind_addr == NULL )
250     {
251         i_opt = 1;
252         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
253                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
254         {
255             intf_WarnMsg( 1,
256                     "ipv4 warning: cannot configure socket (SO_BROADCAST: %s)",
257                     strerror(errno));
258         }
259     }
260  
261     /* Join the multicast group if the socket is a multicast address */
262 #ifndef IN_MULTICAST
263 #   define IN_MULTICAST(a)         IN_CLASSD(a)
264 #endif
265
266 #ifndef WIN32
267     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
268     {
269         struct ip_mreq imr;
270         imr.imr_interface.s_addr = INADDR_ANY;
271         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
272 #else
273     if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) )
274     {
275         struct ip_mreq imr;
276         imr.imr_interface.s_addr = INADDR_ANY;
277         imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr);
278 #endif                      
279         if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
280                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
281         {
282             intf_ErrMsg( "ipv4 error: failed to join IP multicast group (%s)",
283                          strerror(errno) );
284             close( i_handle );
285             return( -1 );
286         }
287     }
288
289     if( psz_server_addr != NULL )
290     {
291         /* Build socket for remote connection */
292         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
293         {
294             intf_ErrMsg( "ipv4 error: cannot build remote address" );
295             close( i_handle );
296             return( -1 );
297         }
298  
299         /* Connect the socket */
300         if( connect( i_handle, (struct sockaddr *) &sock,
301                      sizeof( sock ) ) == (-1) )
302         {
303             intf_ErrMsg( "ipv4 error: cannot connect socket (%s)",
304                          strerror(errno) );
305             close( i_handle );
306             return( -1 );
307         }
308     }
309
310     p_socket->i_handle = i_handle;
311     p_socket->i_mtu = DEFAULT_MTU;
312     return( 0 );
313 }
314
315 /*****************************************************************************
316  * OpenTCP: open a TCP socket
317  *****************************************************************************
318  * psz_server_addr, i_server_port : address and port used for the connect()
319  *   system call. If i_server_port == 0, 80 is used.
320  * Other parameters are ignored.
321  * This function returns -1 in case of error.
322  *****************************************************************************/
323 static int OpenTCP( network_socket_t * p_socket )
324 {
325     char * psz_server_addr = p_socket->psz_server_addr;
326     int i_server_port = p_socket->i_server_port;
327
328     int i_handle;
329     struct sockaddr_in sock;
330
331     if( i_server_port == 0 )
332     {
333         i_server_port = 80;
334     }
335
336     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
337      * protocol */
338     if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
339     {
340         intf_ErrMsg( "ipv4 error: cannot create socket (%s)", strerror(errno) );
341         return( -1 );
342     }
343
344     /* Build remote address */
345     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
346     {
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         intf_ErrMsg( "ipv4 error: cannot connect socket (%s)",
356                      strerror(errno) );
357         close( i_handle );
358         return( -1 );
359     }
360
361     p_socket->i_handle = i_handle;
362     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
363
364     return( 0 );
365 }
366
367 /*****************************************************************************
368  * NetworkOpen: wrapper around OpenUDP and OpenTCP
369  *****************************************************************************/
370 static int NetworkOpen( network_socket_t * p_socket )
371 {
372     if( p_socket->i_type == NETWORK_UDP )
373     {
374         return OpenUDP( p_socket );
375     }
376     else
377     {
378         return OpenTCP( p_socket );
379     }
380 }