]> git.sesse.net Git - vlc/blob - plugins/network/ipv4.c
0c2d1f1a72b62376b1ae49794072b351a5e12d95
[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.14 2002/07/19 21:14:13 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 <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34
35 #include <vlc/vlc.h>
36
37 #ifdef HAVE_UNISTD_H
38 #   include <unistd.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 )
40 #   include <io.h>
41 #endif
42
43 #ifdef WIN32
44 #   include <winsock2.h>
45 #   include <ws2tcpip.h>
46 #   ifndef IN_MULTICAST
47 #       define IN_MULTICAST(a) IN_CLASSD(a)
48 #   endif
49 #else
50 #   include <netdb.h>                                         /* hostent ... */
51 #   include <sys/socket.h>
52 #   include <netinet/in.h>
53 #   ifdef HAVE_ARPA_INET_H
54 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
55 #   endif
56 #endif
57
58 #include "network.h"
59
60 /*****************************************************************************
61  * Local prototypes
62  *****************************************************************************/
63 static void getfunctions( function_list_t * );
64 static int  NetworkOpen( vlc_object_t *, network_socket_t * );
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 MODULE_INIT_STOP
76  
77 MODULE_ACTIVATE_START
78     getfunctions( &p_module->p_functions->network );
79 MODULE_ACTIVATE_STOP
80  
81 MODULE_DEACTIVATE_START
82 MODULE_DEACTIVATE_STOP
83
84 /*****************************************************************************
85  * Functions exported as capabilities. They are declared as static so that
86  * we don't pollute the namespace too much.
87  *****************************************************************************/
88 static void getfunctions( function_list_t * p_function_list )
89 {
90 #define f p_function_list->functions.network
91     f.pf_open = NetworkOpen;
92 #undef f
93 }
94
95 /*****************************************************************************
96  * BuildAddr: utility function to build a struct sockaddr_in
97  *****************************************************************************/
98 static int BuildAddr( struct sockaddr_in * p_socket,
99                       const char * psz_address, int i_port )
100 {
101     /* Reset struct */
102     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
103     p_socket->sin_family = AF_INET;                                /* family */
104     p_socket->sin_port = htons( i_port );
105     if( !*psz_address )
106     {
107         p_socket->sin_addr.s_addr = INADDR_ANY;
108     }
109     else
110     {
111         struct hostent    * p_hostent;
112  
113         /* Try to convert address directly from in_addr - this will work if
114          * psz_address is dotted decimal. */
115 #ifdef HAVE_ARPA_INET_H
116         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
117 #else
118         if( (p_socket->sin_addr.s_addr = inet_addr( psz_address )) == -1 )
119 #endif
120         {
121             /* We have a fqdn, try to find its address */
122             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
123             {
124 //X                intf_ErrMsg( "BuildLocalAddr: unknown host %s", psz_address );
125                 return( -1 );
126             }
127
128             /* Copy the first address of the host in the socket address */
129             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
130                      p_hostent->h_length );
131         }
132     }
133     return( 0 );
134 }
135
136 /*****************************************************************************
137  * OpenUDP: open a UDP socket
138  *****************************************************************************
139  * psz_bind_addr, i_bind_port : address and port used for the bind()
140  *   system call. If psz_bind_addr == "", the socket is bound to
141  *   INADDR_ANY and broadcast reception is enabled. If i_bind_port == 0,
142  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
143  *   join the multicast group.
144  * psz_server_addr, i_server_port : address and port used for the connect()
145  *   system call. It can avoid receiving packets from unauthorized IPs.
146  *   Its use leads to great confusion and is currently discouraged.
147  * This function returns -1 in case of error.
148  *****************************************************************************/
149 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
150 {
151     char * psz_bind_addr = p_socket->psz_bind_addr;
152     int i_bind_port = p_socket->i_bind_port;
153     char * psz_server_addr = p_socket->psz_server_addr;
154     int i_server_port = p_socket->i_server_port;
155 #ifdef WIN32
156     char * psz_bind_win32;        /* WIN32 multicast kludge */
157 #endif
158
159     int i_handle, i_opt, i_opt_size;
160     struct sockaddr_in sock;
161
162     if( i_bind_port == 0 )
163     {
164         i_bind_port = config_GetInt( p_this, "server-port" );
165     }
166
167     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
168      * protocol */
169     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
170     {
171         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
172         return( -1 );
173     }
174
175     /* We may want to reuse an already used socket */
176     i_opt = 1;
177     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
178                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
179     {
180         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
181                           strerror(errno));
182         close( i_handle );
183         return( -1 );
184     }
185
186     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
187      * packet loss caused by scheduling problems */
188     i_opt = 0x80000;
189     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
190                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
191     {
192         msg_Warn( p_this, "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         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
205                           strerror(errno) );
206     }
207     else if( i_opt < 0x80000 )
208     {
209         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
210                           i_opt, 0x80000 );
211     }
212     
213     
214     /* Build the local socket */
215
216 #ifdef WIN32
217     /* Under Win32 and for the multicast, we bind on INADDR_ANY,
218      * so let's call BuildAddr with "" instead of psz_bind_addr */
219     psz_bind_win32 = psz_bind_addr ;
220     
221     /* Check if this is a multicast socket */
222     if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
223     {
224         psz_bind_win32 = "";
225     }
226     if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 )
227 #else
228     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )        
229 #endif    
230     {
231         close( i_handle );
232         return( -1 );
233     }
234  
235     /* Bind it */
236     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
237     {
238         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
239         close( i_handle );
240         return( -1 );
241     }
242
243     /* Allow broadcast reception if we bound on INADDR_ANY */
244     if( !*psz_bind_addr )
245     {
246         i_opt = 1;
247         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
248                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
249         {
250             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
251                        strerror(errno) );
252         }
253     }
254  
255     /* Join the multicast group if the socket is a multicast address */
256 #ifndef IN_MULTICAST
257 #   define IN_MULTICAST(a)         IN_CLASSD(a)
258 #endif
259
260 #ifndef WIN32
261     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
262     {
263         struct ip_mreq imr;
264         imr.imr_interface.s_addr = INADDR_ANY;
265         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
266 #else
267     if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) )
268     {
269         struct ip_mreq imr;
270         imr.imr_interface.s_addr = INADDR_ANY;
271         imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr);
272 #endif                
273         if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
274                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
275         {
276             msg_Err( p_this, "failed to join IP multicast group (%s)",
277                              strerror(errno) );
278             close( i_handle );
279             return( -1 );
280         }
281     }
282
283     if( *psz_server_addr )
284     {
285         /* Build socket for remote connection */
286         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
287         {
288             msg_Err( p_this, "cannot build remote address" );
289             close( i_handle );
290             return( -1 );
291         }
292  
293         /* Connect the socket */
294         if( connect( i_handle, (struct sockaddr *) &sock,
295                      sizeof( sock ) ) == (-1) )
296         {
297             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
298             close( i_handle );
299             return( -1 );
300         }
301     }
302
303     p_socket->i_handle = i_handle;
304     p_socket->i_mtu = config_GetInt( p_this, "mtu" );
305     return( 0 );
306 }
307
308 /*****************************************************************************
309  * OpenTCP: open a TCP socket
310  *****************************************************************************
311  * psz_server_addr, i_server_port : address and port used for the connect()
312  *   system call. If i_server_port == 0, 80 is used.
313  * Other parameters are ignored.
314  * This function returns -1 in case of error.
315  *****************************************************************************/
316 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
317 {
318     char * psz_server_addr = p_socket->psz_server_addr;
319     int i_server_port = p_socket->i_server_port;
320
321     int i_handle;
322     struct sockaddr_in sock;
323
324     if( i_server_port == 0 )
325     {
326         i_server_port = 80;
327     }
328
329     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
330      * protocol */
331     if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
332     {
333         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
334         return( -1 );
335     }
336
337     /* Build remote address */
338     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
339     {
340         close( i_handle );
341         return( -1 );
342     }
343
344     /* Connect the socket */
345     if( connect( i_handle, (struct sockaddr *) &sock,
346                  sizeof( sock ) ) == (-1) )
347     {
348         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
349         close( i_handle );
350         return( -1 );
351     }
352
353     p_socket->i_handle = i_handle;
354     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
355
356     return( 0 );
357 }
358
359 /*****************************************************************************
360  * NetworkOpen: wrapper around OpenUDP and OpenTCP
361  *****************************************************************************/
362 static int NetworkOpen( vlc_object_t * p_this, network_socket_t * p_socket )
363 {
364     if( p_socket->i_type == NETWORK_UDP )
365     {
366         return OpenUDP( p_this, p_socket );
367     }
368     else
369     {
370         return OpenTCP( p_this, p_socket );
371     }
372 }