]> git.sesse.net Git - vlc/blob - src/network/udp.c
Sort out io.c
[vlc] / src / network / udp.c
1 /*****************************************************************************
2  * udp.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *          RĂ©mi Denis-Courmont <rem # videolan.org>
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 <vlc/vlc.h>
30
31 #include <errno.h>
32
33 #ifdef HAVE_FCNTL_H
34 #   include <fcntl.h>
35 #endif
36 #ifdef HAVE_SYS_TIME_H
37 #    include <sys/time.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #   include <unistd.h>
41 #endif
42
43 #include "network.h"
44
45 #ifndef INADDR_ANY
46 #   define INADDR_ANY  0x00000000
47 #endif
48 #ifndef INADDR_NONE
49 #   define INADDR_NONE 0xFFFFFFFF
50 #endif
51
52 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
53                        int i_protocol );
54
55 /*****************************************************************************
56  * __net_ConnectUDP:
57  *****************************************************************************
58  * Open a UDP socket to send data to a defined destination, with an optional
59  * hop limit.
60  *****************************************************************************/
61 int __net_ConnectUDP( vlc_object_t *p_this, const char *psz_host, int i_port,
62                       int hlim )
63 {
64     struct addrinfo hints, *res, *ptr;
65     int             i_val, i_handle = -1;
66     vlc_bool_t      b_unreach = VLC_FALSE;
67
68     if( i_port == 0 )
69         i_port = 1234; /* historical VLC thing */
70
71     memset( &hints, 0, sizeof( hints ) );
72     hints.ai_socktype = SOCK_DGRAM;
73
74     msg_Dbg( p_this, "net: connecting to %s port %d", psz_host, i_port );
75
76     i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
77     if( i_val )
78     {
79         msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
80                  vlc_gai_strerror( i_val ) );
81         return -1;
82     }
83
84     for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
85     {
86         int fd;
87
88         fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
89                          ptr->ai_protocol );
90         if( fd == -1 )
91             continue;
92 #if !defined( SYS_BEOS )
93         else
94         {
95             int i_val;
96
97             /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
98             * packet loss caused by scheduling problems */
99             i_val = 0x80000;
100             setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *)&i_val,
101                         sizeof( i_val ) );
102             i_val = 0x80000;
103             setsockopt( i_handle, SOL_SOCKET, SO_SNDBUF, (void *)&i_val,
104                         sizeof( i_val ) );
105         }
106 #endif
107
108         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
109         {
110             /* success */
111             i_handle = fd;
112             break;
113         }
114
115 #if defined( WIN32 ) || defined( UNDER_CE )
116         if( WSAGetLastError () == WSAENETUNREACH )
117 #else
118         if( errno == ENETUNREACH )
119 #endif
120             b_unreach = VLC_TRUE;
121         else
122         {
123             msg_Warn( p_this, "%s port %d : %s", psz_host, i_port,
124                       strerror( errno ) );
125             net_Close( fd );
126             continue;
127         }
128     }
129
130     vlc_freeaddrinfo( res );
131
132     if( i_handle == -1 )
133     {
134         if( b_unreach )
135             msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
136                      i_port );
137         return -1;
138     }
139
140     return i_handle;
141 }
142
143 /*****************************************************************************
144  * __net_OpenUDP:
145  *****************************************************************************
146  * Open a UDP connection and return a handle
147  *****************************************************************************/
148 int __net_OpenUDP( vlc_object_t *p_this, const char *psz_bind, int i_bind,
149                    const char *psz_server, int i_server )
150 {
151     vlc_value_t      v4, v6;
152     void            *private;
153     network_socket_t sock;
154     module_t         *p_network = NULL;
155
156     if( ( psz_server != NULL ) && ( psz_server[0] == '\0' ) )
157         msg_Warn( p_this, "calling net_OpenUDP with an explicit destination "
158                   "is obsolete - use net_ConnectUDP instead" );
159     if( i_server != 0 )
160         msg_Warn( p_this, "calling net_OpenUDP with an explicit destination "
161                   "port is obsolete - use __net_ConnectUDP instead" );
162
163     if( psz_server == NULL ) psz_server = "";
164     if( psz_bind == NULL ) psz_bind = "";
165
166     /* Prepare the network_socket_t structure */
167     sock.psz_bind_addr   = psz_bind;
168     sock.i_bind_port     = i_bind;
169     sock.psz_server_addr = psz_server;
170     sock.i_server_port   = i_server;
171     sock.i_ttl           = 0;
172     sock.v6only          = 0;
173     sock.i_handle        = -1;
174
175     msg_Dbg( p_this, "net: connecting to '[%s]:%d@[%s]:%d'",
176              psz_server, i_server, psz_bind, i_bind );
177
178     /* Check if we have force ipv4 or ipv6 */
179     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
180     var_Get( p_this, "ipv4", &v4 );
181     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
182     var_Get( p_this, "ipv6", &v6 );
183
184     if( !v4.b_bool )
185     {
186         if( v6.b_bool )
187             sock.v6only = 1;
188
189         /* try IPv6 first (unless IPv4 forced) */
190         private = p_this->p_private;
191         p_this->p_private = (void*)&sock;
192         p_network = module_Need( p_this, "network", "ipv6", VLC_TRUE );
193
194         if( p_network != NULL )
195             module_Unneed( p_this, p_network );
196
197         p_this->p_private = private;
198
199         /*
200          * Check if the IP stack can receive IPv4 packets on IPv6 sockets.
201          * If yes, then it is better to use the IPv6 socket.
202          * Otherwise, if we also get an IPv4, we have to choose, so we use
203          * IPv4 only.
204          */
205         if( ( sock.i_handle != -1 ) && ( ( sock.v6only == 0 ) || v6.b_bool ) )
206             return sock.i_handle;
207     }
208
209     if( !v6.b_bool )
210     {
211         int fd6 = sock.i_handle;
212
213         /* also try IPv4 (unless IPv6 forced) */
214         private = p_this->p_private;
215         p_this->p_private = (void*)&sock;
216         p_network = module_Need( p_this, "network", "ipv4", VLC_TRUE );
217
218         if( p_network != NULL )
219             module_Unneed( p_this, p_network );
220
221         p_this->p_private = private;
222
223         if( fd6 != -1 )
224         {
225             if( sock.i_handle != -1 )
226             {
227                 msg_Warn( p_this, "net: lame IPv6/IPv4 dual-stack present. "
228                                   "Using only IPv4." );
229                 net_Close( fd6 );
230             }
231             else
232                 sock.i_handle = fd6;
233         }
234     }
235
236     if( sock.i_handle == -1 )
237         msg_Dbg( p_this, "net: connection to '[%s]:%d@[%s]:%d' failed",
238                 psz_server, i_server, psz_bind, i_bind );
239
240     return sock.i_handle;
241 }