* ./include/modules_inner.h: support for several modules with the same
[vlc.git] / 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.3 2002/08/08 22:28:22 sam 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 int NetOpen( vlc_object_t * );
64
65 /*****************************************************************************
66  * Module descriptor
67  *****************************************************************************/
68 vlc_module_begin();
69     set_description( _("IPv4 network abstraction layer") );
70     set_capability( "network", 50 );
71     set_callbacks( NetOpen, NULL );
72 vlc_module_end();
73
74 /*****************************************************************************
75  * BuildAddr: utility function to build a struct sockaddr_in
76  *****************************************************************************/
77 static int BuildAddr( struct sockaddr_in * p_socket,
78                       const char * psz_address, int i_port )
79 {
80     /* Reset struct */
81     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
82     p_socket->sin_family = AF_INET;                                /* family */
83     p_socket->sin_port = htons( i_port );
84     if( !*psz_address )
85     {
86         p_socket->sin_addr.s_addr = INADDR_ANY;
87     }
88     else
89     {
90         struct hostent    * p_hostent;
91  
92         /* Try to convert address directly from in_addr - this will work if
93          * psz_address is dotted decimal. */
94 #ifdef HAVE_ARPA_INET_H
95         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
96 #else
97         if( (p_socket->sin_addr.s_addr = inet_addr( psz_address )) == -1 )
98 #endif
99         {
100             /* We have a fqdn, try to find its address */
101             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
102             {
103                 return( -1 );
104             }
105
106             /* Copy the first address of the host in the socket address */
107             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
108                      p_hostent->h_length );
109         }
110     }
111     return( 0 );
112 }
113
114 /*****************************************************************************
115  * OpenUDP: open a UDP socket
116  *****************************************************************************
117  * psz_bind_addr, i_bind_port : address and port used for the bind()
118  *   system call. If psz_bind_addr == "", the socket is bound to
119  *   INADDR_ANY and broadcast reception is enabled. If i_bind_port == 0,
120  *   1234 is used. If psz_bind_addr is a multicast (class D) address,
121  *   join the multicast group.
122  * psz_server_addr, i_server_port : address and port used for the connect()
123  *   system call. It can avoid receiving packets from unauthorized IPs.
124  *   Its use leads to great confusion and is currently discouraged.
125  * This function returns -1 in case of error.
126  *****************************************************************************/
127 static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket )
128 {
129     char * psz_bind_addr = p_socket->psz_bind_addr;
130     int i_bind_port = p_socket->i_bind_port;
131     char * psz_server_addr = p_socket->psz_server_addr;
132     int i_server_port = p_socket->i_server_port;
133 #ifdef WIN32
134     char * psz_bind_win32;        /* WIN32 multicast kludge */
135 #endif
136
137     int i_handle, i_opt;
138     unsigned int i_opt_size;
139     struct sockaddr_in sock;
140
141     if( i_bind_port == 0 )
142     {
143         i_bind_port = config_GetInt( p_this, "server-port" );
144     }
145
146     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
147      * protocol */
148     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
149     {
150         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
151         return( -1 );
152     }
153
154     /* We may want to reuse an already used socket */
155     i_opt = 1;
156     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
157                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
158     {
159         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
160                           strerror(errno));
161         close( i_handle );
162         return( -1 );
163     }
164
165     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
166      * packet loss caused by scheduling problems */
167     i_opt = 0x80000;
168     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
169                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
170     {
171         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
172                           strerror(errno));
173     }
174  
175     /* Check if we really got what we have asked for, because Linux, etc.
176      * will silently limit the max buffer size to net.core.rmem_max which
177      * is typically only 65535 bytes */
178     i_opt = 0;
179     i_opt_size = sizeof( i_opt );
180     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
181                     (void*) &i_opt, &i_opt_size ) == -1 )
182     {
183         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
184                           strerror(errno) );
185     }
186     else if( i_opt < 0x80000 )
187     {
188         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
189                           i_opt, 0x80000 );
190     }
191     
192     
193     /* Build the local socket */
194
195 #ifdef WIN32
196     /* Under Win32 and for the multicast, we bind on INADDR_ANY,
197      * so let's call BuildAddr with "" instead of psz_bind_addr */
198     psz_bind_win32 = psz_bind_addr ;
199     
200     /* Check if this is a multicast socket */
201     if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
202     {
203         psz_bind_win32 = "";
204     }
205     if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 )
206 #else
207     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )        
208 #endif    
209     {
210         close( i_handle );
211         return( -1 );
212     }
213  
214     /* Bind it */
215     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
216     {
217         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
218         close( i_handle );
219         return( -1 );
220     }
221
222     /* Allow broadcast reception if we bound on INADDR_ANY */
223     if( !*psz_bind_addr )
224     {
225         i_opt = 1;
226         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
227                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
228         {
229             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
230                        strerror(errno) );
231         }
232     }
233  
234     /* Join the multicast group if the socket is a multicast address */
235 #ifndef IN_MULTICAST
236 #   define IN_MULTICAST(a)         IN_CLASSD(a)
237 #endif
238
239 #ifndef WIN32
240     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
241     {
242         struct ip_mreq imr;
243         imr.imr_interface.s_addr = INADDR_ANY;
244         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
245 #else
246     if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) )
247     {
248         struct ip_mreq imr;
249         imr.imr_interface.s_addr = INADDR_ANY;
250         imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr);
251 #endif                
252         if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
253                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
254         {
255             msg_Err( p_this, "failed to join IP multicast group (%s)",
256                              strerror(errno) );
257             close( i_handle );
258             return( -1 );
259         }
260     }
261
262     if( *psz_server_addr )
263     {
264         /* Build socket for remote connection */
265         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
266         {
267             msg_Err( p_this, "cannot build remote address" );
268             close( i_handle );
269             return( -1 );
270         }
271  
272         /* Connect the socket */
273         if( connect( i_handle, (struct sockaddr *) &sock,
274                      sizeof( sock ) ) == (-1) )
275         {
276             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
277             close( i_handle );
278             return( -1 );
279         }
280     }
281
282     p_socket->i_handle = i_handle;
283     p_socket->i_mtu = config_GetInt( p_this, "mtu" );
284     return( 0 );
285 }
286
287 /*****************************************************************************
288  * OpenTCP: open a TCP socket
289  *****************************************************************************
290  * psz_server_addr, i_server_port : address and port used for the connect()
291  *   system call. If i_server_port == 0, 80 is used.
292  * Other parameters are ignored.
293  * This function returns -1 in case of error.
294  *****************************************************************************/
295 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
296 {
297     char * psz_server_addr = p_socket->psz_server_addr;
298     int i_server_port = p_socket->i_server_port;
299
300     int i_handle;
301     struct sockaddr_in sock;
302
303     if( i_server_port == 0 )
304     {
305         i_server_port = 80;
306     }
307
308     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
309      * protocol */
310     if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
311     {
312         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
313         return( -1 );
314     }
315
316     /* Build remote address */
317     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
318     {
319         close( i_handle );
320         return( -1 );
321     }
322
323     /* Connect the socket */
324     if( connect( i_handle, (struct sockaddr *) &sock,
325                  sizeof( sock ) ) == (-1) )
326     {
327         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
328         close( i_handle );
329         return( -1 );
330     }
331
332     p_socket->i_handle = i_handle;
333     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
334
335     return( 0 );
336 }
337
338 /*****************************************************************************
339  * NetOpen: wrapper around OpenUDP and OpenTCP
340  *****************************************************************************/
341 static int NetOpen( vlc_object_t * p_this )
342 {
343     network_socket_t * p_socket = p_this->p_private;
344
345     if( p_socket->i_type == NETWORK_UDP )
346     {
347         return OpenUDP( p_this, p_socket );
348     }
349     else
350     {
351         return OpenTCP( p_this, p_socket );
352     }
353 }