]> git.sesse.net Git - vlc/blob - src/misc/net.c
361fc80e71cd807a3f3518f102ea6e65e2ae9e88
[vlc] / src / misc / net.c
1 /*****************************************************************************
2  * net.c:
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
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 <vlc/vlc.h>
29
30 #ifdef HAVE_ERRNO_H
31 #   include <errno.h>
32 #endif
33
34 #ifdef HAVE_FCNTL_H
35 #   include <fcntl.h>
36 #endif
37
38 #ifdef HAVE_SYS_TIME_H
39 #    include <sys/time.h>
40 #endif
41
42 #if defined( UNDER_CE )
43 #   include <winsock.h>
44 #elif defined( WIN32 )
45 #   include <winsock2.h>
46 #   include <ws2tcpip.h>
47 #   ifndef IN_MULTICAST
48 #       define IN_MULTICAST(a) IN_CLASSD(a)
49 #   endif
50 #else
51 #   include <sys/socket.h>
52 #endif
53
54 #ifdef HAVE_UNISTD_H
55 #   include <unistd.h>
56 #endif
57
58 #include "network.h"
59
60
61 /*****************************************************************************
62  * __net_OpenTCP:
63  *****************************************************************************
64  * Open a TCP connection and return a handle
65  *****************************************************************************/
66 int __net_OpenTCP( vlc_object_t *p_this, char *psz_host, int i_port )
67 {
68     vlc_value_t      val;
69     void            *private;
70
71     char            *psz_network = "";
72     network_socket_t sock;
73     module_t         *p_network;
74
75     /* Check if we have force ipv4 or ipv6 */
76     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
77     var_Get( p_this, "ipv4", &val );
78     if( val.b_bool )
79     {
80         psz_network = "ipv4";
81     }
82
83     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
84     var_Get( p_this, "ipv6", &val );
85     if( val.b_bool )
86     {
87         psz_network = "ipv6";
88     }
89
90     /* Prepare the network_socket_t structure */
91     sock.i_type = NETWORK_TCP;
92     sock.psz_bind_addr   = "";
93     sock.i_bind_port     = 0;
94     sock.psz_server_addr = psz_host;
95     sock.i_server_port   = i_port;
96     sock.i_ttl           = 0;
97
98     msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_host, i_port );
99     private = p_this->p_private;
100     p_this->p_private = (void*)&sock;
101     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
102     {
103         msg_Dbg( p_this, "net: connection to '%s:%d' failed",
104                  psz_host, i_port );
105         return -1;
106     }
107     module_Unneed( p_this, p_network );
108     p_this->p_private = private;
109
110     return sock.i_handle;
111 }
112
113 /*****************************************************************************
114  * __net_OpenUDP:
115  *****************************************************************************
116  * Open a UDP connection and return a handle
117  *****************************************************************************/
118 int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
119                    char *psz_server, int i_server )
120 {
121     vlc_value_t      val;
122     void            *private;
123
124     char            *psz_network = "";
125     network_socket_t sock;
126     module_t         *p_network;
127
128
129     /* Check if we have force ipv4 or ipv6 */
130     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
131     var_Get( p_this, "ipv4", &val );
132     if( val.b_bool )
133     {
134         psz_network = "ipv4";
135     }
136
137     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
138     var_Get( p_this, "ipv6", &val );
139     if( val.b_bool )
140     {
141         psz_network = "ipv6";
142     }
143     if( psz_server == NULL ) psz_server = "";
144     if( psz_bind   == NULL ) psz_bind   = "";
145
146     /* Prepare the network_socket_t structure */
147     sock.i_type = NETWORK_UDP;
148     sock.psz_bind_addr   = psz_bind;
149     sock.i_bind_port     = i_bind;
150     sock.psz_server_addr = psz_server;
151     sock.i_server_port   = i_server;
152     sock.i_ttl           = 0;
153
154     msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
155              psz_server, i_server, psz_bind, i_bind );
156     private = p_this->p_private;
157     p_this->p_private = (void*)&sock;
158     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
159     {
160         msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
161                  psz_server, i_server, psz_bind, i_bind );
162         return -1;
163     }
164     module_Unneed( p_this, p_network );
165     p_this->p_private = private;
166
167     return sock.i_handle;
168 }
169
170 /*****************************************************************************
171  * __net_Close:
172  *****************************************************************************
173  * Close a network handle
174  *****************************************************************************/
175 void net_Close( int fd )
176 {
177 #ifdef UNDER_CE
178     CloseHandle( (HANDLE)fd );
179 #elif defined( WIN32 )
180     closesocket( fd );
181 #else
182     close( fd );
183 #endif
184 }
185
186 /*****************************************************************************
187  * __net_Read:
188  *****************************************************************************
189  * Read from a network socket
190  * If b_rety is true, then we repeat until we have read the right amount of
191  * data
192  *****************************************************************************/
193 int __net_Read( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data,
194                 vlc_bool_t b_retry )
195 {
196     struct timeval  timeout;
197     fd_set          fds_r, fds_e;
198     int             i_recv;
199     int             i_total = 0;
200     int             i_ret;
201     vlc_bool_t      b_die = p_this->b_die;
202
203     while( i_data > 0 )
204     {
205         do
206         {
207             if( p_this->b_die != b_die )
208             {
209                 return 0;
210             }
211
212             /* Initialize file descriptor set */
213             FD_ZERO( &fds_r );
214             FD_SET( fd, &fds_r );
215             FD_ZERO( &fds_e );
216             FD_SET( fd, &fds_e );
217
218             /* We'll wait 0.5 second if nothing happens */
219             timeout.tv_sec = 0;
220             timeout.tv_usec = 500000;
221
222         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
223                  || ( i_ret < 0 && errno == EINTR ) );
224
225         if( i_ret < 0 )
226         {
227             msg_Err( p_this, "network select error (%s)", strerror(errno) );
228             return i_total > 0 ? i_total : -1;
229         }
230
231         if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) < 0 )
232         {
233 #ifdef WIN32
234             /* For udp only */
235             /* On win32 recv() will fail if the datagram doesn't fit inside
236              * the passed buffer, even though the buffer will be filled with
237              * the first part of the datagram. */
238             if( WSAGetLastError() == WSAEMSGSIZE )
239             {
240                 msg_Err( p_this, "recv() failed. "
241                          "Increase the mtu size (--mtu option)" );
242                 i_recv = i_data;
243             }
244             else
245                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
246 #else
247             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
248 #endif
249             return i_total > 0 ? i_total : -1;
250         }
251         else if( i_recv == 0 )
252         {
253             /* Connection closed */
254             b_retry = VLC_FALSE;
255         }
256
257         p_data += i_recv;
258         i_data -= i_recv;
259         i_total+= i_recv;
260         if( !b_retry )
261         {
262             break;
263         }
264     }
265     return i_total;
266 }
267
268 /*****************************************************************************
269  * __net_ReadNonBlock:
270  *****************************************************************************
271  * Read from a network socket, non blocking mode (with timeout)
272  *****************************************************************************/
273 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, uint8_t *p_data,
274                         int i_data, mtime_t i_wait)
275 {
276     struct timeval  timeout;
277     fd_set          fds_r, fds_e;
278     int             i_recv;
279     int             i_ret;
280
281     /* Initialize file descriptor set */
282     FD_ZERO( &fds_r );
283     FD_SET( fd, &fds_r );
284     FD_ZERO( &fds_e );
285     FD_SET( fd, &fds_e );
286
287     timeout.tv_sec = 0;
288     timeout.tv_usec = i_wait;
289
290     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
291
292     if( i_ret < 0 && errno == EINTR )
293     {
294         return 0;
295     }
296     else if( i_ret < 0 )
297     {
298         msg_Err( p_this, "network select error (%s)", strerror(errno) );
299         return -1;
300     }
301     else if( i_ret == 0)
302     {
303         return 0;
304     }
305     else
306     {
307         if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) < 0 )
308         {
309 #ifdef WIN32
310             /* For udp only */
311             /* On win32 recv() will fail if the datagram doesn't fit inside
312              * the passed buffer, even though the buffer will be filled with
313              * the first part of the datagram. */
314             if( WSAGetLastError() == WSAEMSGSIZE )
315             {
316                 msg_Err( p_this, "recv() failed. "
317                          "Increase the mtu size (--mtu option)" );
318             }
319             else
320                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
321 #else
322             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
323 #endif
324             return -1;
325         }
326         return i_recv;
327     }
328     /* We will never be here */
329     return -1;
330 }
331
332 /* Write exact amount requested */
333 int __net_Write( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data )
334 {
335     struct timeval  timeout;
336     fd_set          fds_w, fds_e;
337     int             i_send;
338     int             i_total = 0;
339     int             i_ret;
340
341     vlc_bool_t      b_die = p_this->b_die;
342
343     while( i_data > 0 )
344     {
345         do
346         {
347             if( p_this->b_die != b_die )
348             {
349                 return 0;
350             }
351
352             /* Initialize file descriptor set */
353             FD_ZERO( &fds_w );
354             FD_SET( fd, &fds_w );
355             FD_ZERO( &fds_e );
356             FD_SET( fd, &fds_e );
357
358             /* We'll wait 0.5 second if nothing happens */
359             timeout.tv_sec = 0;
360             timeout.tv_usec = 500000;
361
362         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
363                  || ( i_ret < 0 && errno == EINTR ) );
364
365         if( i_ret < 0 )
366         {
367             msg_Err( p_this, "network select error (%s)", strerror(errno) );
368             return i_total > 0 ? i_total : -1;
369         }
370
371         if( ( i_send = send( fd, p_data, i_data, 0 ) ) < 0 )
372         {
373             /* XXX With udp for example, it will issue a message if the host
374              * isn't listening */
375             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
376             return i_total > 0 ? i_total : -1;
377         }
378
379         p_data += i_send;
380         i_data -= i_send;
381         i_total+= i_send;
382     }
383     return i_total;
384 }
385
386 char *__net_Gets( vlc_object_t *p_this, int fd )
387 {
388     char *psz_line = malloc( 1024 );
389     int  i_line = 0;
390     int  i_max = 1024;
391
392
393     for( ;; )
394     {
395         if( net_Read( p_this, fd, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
396         {
397             psz_line[i_line] = '\0';
398             break;
399         }
400         i_line++;
401
402         if( psz_line[i_line-1] == '\n' )
403         {
404             psz_line[i_line] = '\0';
405             break;
406         }
407
408         if( i_line >= i_max - 1 )
409         {
410             i_max += 1024;
411             psz_line = realloc( psz_line, i_max );
412         }
413     }
414
415     if( i_line <= 0 )
416     {
417         free( psz_line );
418         return NULL;
419     }
420
421     while( i_line >= 1 &&
422            ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
423     {
424         i_line--;
425         psz_line[i_line] = '\0';
426     }
427     return psz_line;
428 }
429
430 int net_Printf( vlc_object_t *p_this, int fd, char *psz_fmt, ... )
431 {
432     va_list args;
433     char    *psz;
434     int     i_size, i_ret;
435
436     va_start( args, psz_fmt );
437     vasprintf( &psz, psz_fmt, args );
438     va_end( args );
439
440     i_size = strlen( psz );
441
442     i_ret = __net_Write( p_this, fd, psz, i_size ) < i_size ? -1 : i_size;
443     free( psz );
444
445     return i_ret;
446 }