* ./modules/audio_output/oss.c: compilation fixes.
[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.2 2002/08/08 00:35:11 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, i_opt_size;
138     struct sockaddr_in sock;
139
140     if( i_bind_port == 0 )
141     {
142         i_bind_port = config_GetInt( p_this, "server-port" );
143     }
144
145     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
146      * protocol */
147     if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
148     {
149         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
150         return( -1 );
151     }
152
153     /* We may want to reuse an already used socket */
154     i_opt = 1;
155     if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
156                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
157     {
158         msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)",
159                           strerror(errno));
160         close( i_handle );
161         return( -1 );
162     }
163
164     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
165      * packet loss caused by scheduling problems */
166     i_opt = 0x80000;
167     if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
168                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
169     {
170         msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)",
171                           strerror(errno));
172     }
173  
174     /* Check if we really got what we have asked for, because Linux, etc.
175      * will silently limit the max buffer size to net.core.rmem_max which
176      * is typically only 65535 bytes */
177     i_opt = 0;
178     i_opt_size = sizeof( i_opt );
179     if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
180                     (void*) &i_opt, &i_opt_size ) == -1 )
181     {
182         msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)",
183                           strerror(errno) );
184     }
185     else if( i_opt < 0x80000 )
186     {
187         msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x",
188                           i_opt, 0x80000 );
189     }
190     
191     
192     /* Build the local socket */
193
194 #ifdef WIN32
195     /* Under Win32 and for the multicast, we bind on INADDR_ANY,
196      * so let's call BuildAddr with "" instead of psz_bind_addr */
197     psz_bind_win32 = psz_bind_addr ;
198     
199     /* Check if this is a multicast socket */
200     if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) )
201     {
202         psz_bind_win32 = "";
203     }
204     if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 )
205 #else
206     if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )        
207 #endif    
208     {
209         close( i_handle );
210         return( -1 );
211     }
212  
213     /* Bind it */
214     if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
215     {
216         msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
217         close( i_handle );
218         return( -1 );
219     }
220
221     /* Allow broadcast reception if we bound on INADDR_ANY */
222     if( !*psz_bind_addr )
223     {
224         i_opt = 1;
225         if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
226                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
227         {
228             msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)",
229                        strerror(errno) );
230         }
231     }
232  
233     /* Join the multicast group if the socket is a multicast address */
234 #ifndef IN_MULTICAST
235 #   define IN_MULTICAST(a)         IN_CLASSD(a)
236 #endif
237
238 #ifndef WIN32
239     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
240     {
241         struct ip_mreq imr;
242         imr.imr_interface.s_addr = INADDR_ANY;
243         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
244 #else
245     if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) )
246     {
247         struct ip_mreq imr;
248         imr.imr_interface.s_addr = INADDR_ANY;
249         imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr);
250 #endif                
251         if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
252                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
253         {
254             msg_Err( p_this, "failed to join IP multicast group (%s)",
255                              strerror(errno) );
256             close( i_handle );
257             return( -1 );
258         }
259     }
260
261     if( *psz_server_addr )
262     {
263         /* Build socket for remote connection */
264         if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
265         {
266             msg_Err( p_this, "cannot build remote address" );
267             close( i_handle );
268             return( -1 );
269         }
270  
271         /* Connect the socket */
272         if( connect( i_handle, (struct sockaddr *) &sock,
273                      sizeof( sock ) ) == (-1) )
274         {
275             msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
276             close( i_handle );
277             return( -1 );
278         }
279     }
280
281     p_socket->i_handle = i_handle;
282     p_socket->i_mtu = config_GetInt( p_this, "mtu" );
283     return( 0 );
284 }
285
286 /*****************************************************************************
287  * OpenTCP: open a TCP socket
288  *****************************************************************************
289  * psz_server_addr, i_server_port : address and port used for the connect()
290  *   system call. If i_server_port == 0, 80 is used.
291  * Other parameters are ignored.
292  * This function returns -1 in case of error.
293  *****************************************************************************/
294 static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
295 {
296     char * psz_server_addr = p_socket->psz_server_addr;
297     int i_server_port = p_socket->i_server_port;
298
299     int i_handle;
300     struct sockaddr_in sock;
301
302     if( i_server_port == 0 )
303     {
304         i_server_port = 80;
305     }
306
307     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
308      * protocol */
309     if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 )
310     {
311         msg_Err( p_this, "cannot create socket (%s)", strerror(errno) );
312         return( -1 );
313     }
314
315     /* Build remote address */
316     if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
317     {
318         close( i_handle );
319         return( -1 );
320     }
321
322     /* Connect the socket */
323     if( connect( i_handle, (struct sockaddr *) &sock,
324                  sizeof( sock ) ) == (-1) )
325     {
326         msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) );
327         close( i_handle );
328         return( -1 );
329     }
330
331     p_socket->i_handle = i_handle;
332     p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
333
334     return( 0 );
335 }
336
337 /*****************************************************************************
338  * NetOpen: wrapper around OpenUDP and OpenTCP
339  *****************************************************************************/
340 static int NetOpen( vlc_object_t * p_this )
341 {
342     network_socket_t * p_socket = p_this->p_private;
343
344     if( p_socket->i_type == NETWORK_UDP )
345     {
346         return OpenUDP( p_this, p_socket );
347     }
348     else
349     {
350         return OpenTCP( p_this, p_socket );
351     }
352 }