]> git.sesse.net Git - vlc/blob - src/misc/net.c
deaf74abf9fba1a45226eab194b315b8ed45ee10
[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 #include <errno.h>
31
32 #ifdef HAVE_FCNTL_H
33 #   include <fcntl.h>
34 #endif
35
36 #ifdef HAVE_SYS_TIME_H
37 #    include <sys/time.h>
38 #endif
39
40 #if defined( UNDER_CE )
41 #   include <winsock.h>
42 #elif defined( WIN32 )
43 #   include <winsock2.h>
44 #   include <ws2tcpip.h>
45 #   ifndef IN_MULTICAST
46 #       define IN_MULTICAST(a) IN_CLASSD(a)
47 #   endif
48 #else
49 #   include <sys/socket.h>
50 #endif
51
52 #ifdef HAVE_UNISTD_H
53 #   include <unistd.h>
54 #elif defined( WIN32 ) && !defined( UNDER_CE )
55 #   include <io.h>
56 #endif
57
58 #include "network.h"
59
60 /*****************************************************************************
61  * __net_OpenTCP:
62  *****************************************************************************
63  * Open a TCP connection and return a handle
64  *****************************************************************************/
65 int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
66 {
67     vlc_value_t      val;
68     void            *private;
69
70     char            *psz_network = "";
71     network_socket_t sock;
72     module_t         *p_network;
73
74     /* Check if we have force ipv4 or ipv6 */
75     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
76     var_Get( p_this, "ipv4", &val );
77     if( val.b_bool )
78     {
79         psz_network = "ipv4";
80     }
81
82     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
83     var_Get( p_this, "ipv6", &val );
84     if( val.b_bool )
85     {
86         psz_network = "ipv6";
87     }
88
89     /* Prepare the network_socket_t structure */
90     sock.i_type = NETWORK_TCP;
91     sock.psz_bind_addr   = "";
92     sock.i_bind_port     = 0;
93     sock.psz_server_addr = (char *)psz_host;
94     sock.i_server_port   = i_port;
95     sock.i_ttl           = 0;
96
97     msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_host, i_port );
98     private = p_this->p_private;
99     p_this->p_private = (void*)&sock;
100     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
101     {
102         msg_Dbg( p_this, "net: connection to '%s:%d' failed",
103                  psz_host, i_port );
104         return -1;
105     }
106     module_Unneed( p_this, p_network );
107     p_this->p_private = private;
108
109     return sock.i_handle;
110 }
111
112 /*****************************************************************************
113  * __net_ListenTCP:
114  *****************************************************************************
115  * Open a TCP listening socket and return it
116  *****************************************************************************/
117 int __net_ListenTCP( vlc_object_t *p_this, char *psz_host, int i_port )
118 {
119     vlc_value_t      val;
120     void            *private;
121
122     char            *psz_network = "";
123     network_socket_t sock;
124     module_t         *p_network;
125
126     /* Check if we have force ipv4 or ipv6 */
127     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
128     var_Get( p_this, "ipv4", &val );
129     if( val.b_bool )
130     {
131         psz_network = "ipv4";
132     }
133
134     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
135     var_Get( p_this, "ipv6", &val );
136     if( val.b_bool )
137     {
138         psz_network = "ipv6";
139     }
140
141     /* Prepare the network_socket_t structure */
142     sock.i_type = NETWORK_TCP_PASSIVE;
143     sock.psz_bind_addr   = "";
144     sock.i_bind_port     = 0;
145     sock.psz_server_addr = psz_host;
146     sock.i_server_port   = i_port;
147     sock.i_ttl           = 0;
148
149     msg_Dbg( p_this, "net: listening to '%s:%d'", psz_host, i_port );
150     private = p_this->p_private;
151     p_this->p_private = (void*)&sock;
152     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
153     {
154         msg_Dbg( p_this, "net: listening to '%s:%d' failed",
155                  psz_host, i_port );
156         return -1;
157     }
158     module_Unneed( p_this, p_network );
159     p_this->p_private = private;
160
161     return sock.i_handle;
162 }
163
164 /*****************************************************************************
165  * __net_Accept:
166  *****************************************************************************
167  * Accept a connection on a listening socket and return it
168  *****************************************************************************/
169 int __net_Accept( vlc_object_t *p_this, int fd, mtime_t i_wait )
170 {
171     vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
172     struct timeval timeout;
173     fd_set fds_r, fds_e;
174     int i_ret;
175
176     while( p_this->b_die == b_die )
177     {
178         /* Initialize file descriptor set */
179         FD_ZERO( &fds_r );
180         FD_SET( fd, &fds_r );
181         FD_ZERO( &fds_e );
182         FD_SET( fd, &fds_e );
183
184         timeout.tv_sec = 0;
185         timeout.tv_usec = b_block ? 500000 : i_wait;
186
187         i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
188         if( (i_ret < 0 && errno == EINTR) || i_ret == 0 )
189         {
190             if( b_block ) continue;
191             else return -1;
192         }
193         else if( i_ret < 0 )
194         {
195 #if defined(WIN32) || defined(UNDER_CE)
196             msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
197 #else
198             msg_Err( p_this, "network select error (%s)", strerror(errno) );
199 #endif
200             return -1;
201         }
202
203         if( ( i_ret = accept( fd, 0, 0 ) ) <= 0 )
204         {
205 #if defined(WIN32) || defined(UNDER_CE)
206             msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
207 #else
208             msg_Err( p_this, "accept failed (%s)", strerror(errno) );
209 #endif
210             return -1;
211         }
212
213         return i_ret;
214     }
215
216     return -1;
217 }
218
219 /*****************************************************************************
220  * __net_OpenUDP:
221  *****************************************************************************
222  * Open a UDP connection and return a handle
223  *****************************************************************************/
224 int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
225                    char *psz_server, int i_server )
226 {
227     vlc_value_t      val;
228     void            *private;
229
230     char            *psz_network = "";
231     network_socket_t sock;
232     module_t         *p_network;
233
234
235     /* Check if we have force ipv4 or ipv6 */
236     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
237     var_Get( p_this, "ipv4", &val );
238     if( val.b_bool )
239     {
240         psz_network = "ipv4";
241     }
242
243     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
244     var_Get( p_this, "ipv6", &val );
245     if( val.b_bool )
246     {
247         psz_network = "ipv6";
248     }
249     if( psz_server == NULL ) psz_server = "";
250     if( psz_bind   == NULL ) psz_bind   = "";
251
252     /* Prepare the network_socket_t structure */
253     sock.i_type = NETWORK_UDP;
254     sock.psz_bind_addr   = psz_bind;
255     sock.i_bind_port     = i_bind;
256     sock.psz_server_addr = psz_server;
257     sock.i_server_port   = i_server;
258     sock.i_ttl           = 0;
259
260     msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
261              psz_server, i_server, psz_bind, i_bind );
262     private = p_this->p_private;
263     p_this->p_private = (void*)&sock;
264     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
265     {
266         msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
267                  psz_server, i_server, psz_bind, i_bind );
268         return -1;
269     }
270     module_Unneed( p_this, p_network );
271     p_this->p_private = private;
272
273     return sock.i_handle;
274 }
275
276 /*****************************************************************************
277  * __net_Close:
278  *****************************************************************************
279  * Close a network handle
280  *****************************************************************************/
281 void net_Close( int fd )
282 {
283 #ifdef UNDER_CE
284     CloseHandle( (HANDLE)fd );
285 #elif defined( WIN32 )
286     closesocket( fd );
287 #else
288     close( fd );
289 #endif
290 }
291
292 /*****************************************************************************
293  * __net_Read:
294  *****************************************************************************
295  * Read from a network socket
296  * If b_rety is true, then we repeat until we have read the right amount of
297  * data
298  *****************************************************************************/
299 int __net_Read( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data,
300                 vlc_bool_t b_retry )
301 {
302     struct timeval  timeout;
303     fd_set          fds_r, fds_e;
304     int             i_recv;
305     int             i_total = 0;
306     int             i_ret;
307     vlc_bool_t      b_die = p_this->b_die;
308
309     while( i_data > 0 )
310     {
311         do
312         {
313             if( p_this->b_die != b_die )
314             {
315                 return 0;
316             }
317
318             /* Initialize file descriptor set */
319             FD_ZERO( &fds_r );
320             FD_SET( fd, &fds_r );
321             FD_ZERO( &fds_e );
322             FD_SET( fd, &fds_e );
323
324             /* We'll wait 0.5 second if nothing happens */
325             timeout.tv_sec = 0;
326             timeout.tv_usec = 500000;
327
328         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
329                  || ( i_ret < 0 && errno == EINTR ) );
330
331         if( i_ret < 0 )
332         {
333 #if defined(WIN32) || defined(UNDER_CE)
334             msg_Err( p_this, "network select error" );
335 #else
336             msg_Err( p_this, "network select error (%s)", strerror(errno) );
337 #endif
338             return i_total > 0 ? i_total : -1;
339         }
340
341         if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) < 0 )
342         {
343 #if defined(WIN32) || defined(UNDER_CE)
344             /* For udp only */
345             /* On win32 recv() will fail if the datagram doesn't fit inside
346              * the passed buffer, even though the buffer will be filled with
347              * the first part of the datagram. */
348             if( WSAGetLastError() == WSAEMSGSIZE )
349             {
350                 msg_Err( p_this, "recv() failed. "
351                          "Increase the mtu size (--mtu option)" );
352                 i_total += i_data;
353             }
354             else
355                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
356 #else
357             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
358 #endif
359             return i_total > 0 ? i_total : -1;
360         }
361         else if( i_recv == 0 )
362         {
363             /* Connection closed */
364             b_retry = VLC_FALSE;
365         }
366
367         p_data += i_recv;
368         i_data -= i_recv;
369         i_total+= i_recv;
370         if( !b_retry )
371         {
372             break;
373         }
374     }
375     return i_total;
376 }
377
378 /*****************************************************************************
379  * __net_ReadNonBlock:
380  *****************************************************************************
381  * Read from a network socket, non blocking mode (with timeout)
382  *****************************************************************************/
383 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, uint8_t *p_data,
384                         int i_data, mtime_t i_wait)
385 {
386     struct timeval  timeout;
387     fd_set          fds_r, fds_e;
388     int             i_recv;
389     int             i_ret;
390
391     /* Initialize file descriptor set */
392     FD_ZERO( &fds_r );
393     FD_SET( fd, &fds_r );
394     FD_ZERO( &fds_e );
395     FD_SET( fd, &fds_e );
396
397     timeout.tv_sec = 0;
398     timeout.tv_usec = i_wait;
399
400     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
401
402     if( i_ret < 0 && errno == EINTR )
403     {
404         return 0;
405     }
406     else if( i_ret < 0 )
407     {
408 #if defined(WIN32) || defined(UNDER_CE)
409         msg_Err( p_this, "network select error" );
410 #else
411         msg_Err( p_this, "network select error (%s)", strerror(errno) );
412 #endif
413         return -1;
414     }
415     else if( i_ret == 0)
416     {
417         return 0;
418     }
419     else
420     {
421 #if !defined(UNDER_CE)
422         if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
423 #endif
424         if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) <= 0 )
425         {
426 #if defined(WIN32) || defined(UNDER_CE)
427             /* For udp only */
428             /* On win32 recv() will fail if the datagram doesn't fit inside
429              * the passed buffer, even though the buffer will be filled with
430              * the first part of the datagram. */
431             if( WSAGetLastError() == WSAEMSGSIZE )
432             {
433                 msg_Err( p_this, "recv() failed. "
434                          "Increase the mtu size (--mtu option)" );
435             }
436             else
437                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
438 #else
439             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
440 #endif
441             return -1;
442         }
443
444         return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
445     }
446
447     /* We will never be here */
448     return -1;
449 }
450
451 /*****************************************************************************
452  * __net_Select:
453  *****************************************************************************
454  * Read from several sockets (with timeout)
455  *****************************************************************************/
456 int __net_Select( vlc_object_t *p_this, int *pi_fd, int i_fd, uint8_t *p_data,
457                       int i_data, mtime_t i_wait )
458 {
459     struct timeval  timeout;
460     fd_set          fds_r, fds_e;
461     int             i_recv;
462     int             i_ret;
463     int             i;
464     int             i_max_fd = 0;
465
466     /* Initialize file descriptor set */
467     FD_ZERO( &fds_r );
468     FD_ZERO( &fds_e );
469
470     for( i = 0 ; i < i_fd ; i++)
471     {
472         if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
473         FD_SET( pi_fd[i], &fds_r );
474         FD_SET( pi_fd[i], &fds_e );
475     }
476
477     timeout.tv_sec = 0;
478     timeout.tv_usec = i_wait;
479
480     i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );
481
482     if( i_ret < 0 && errno == EINTR )
483     {
484         return 0;
485     }
486     else if( i_ret < 0 )
487     {
488         msg_Err( p_this, "network select error (%s)", strerror(errno) );
489         return -1;
490     }
491     else if( i_ret == 0 )
492     {
493         return 0;
494     }
495     else
496     {
497         for( i = 0 ; i < i_fd ; i++)
498         {
499             if( FD_ISSET( pi_fd[i], &fds_r ) )
500             {
501                 i_recv = recv( pi_fd[i], p_data, i_data, 0 );
502                 if( i_recv <= 0 )
503                 {
504 #ifdef WIN32
505                     /* For udp only */
506                     /* On win32 recv() will fail if the datagram doesn't
507                      * fit inside the passed buffer, even though the buffer
508                      *  will be filled with the first part of the datagram. */
509                     if( WSAGetLastError() == WSAEMSGSIZE )
510                     {
511                         msg_Err( p_this, "recv() failed. "
512                              "Increase the mtu size (--mtu option)" );
513                     }
514                     else
515                         msg_Err( p_this, "recv failed (%i)",
516                                         WSAGetLastError() );
517 #else
518                      msg_Err( p_this, "recv failed (%s)", strerror(errno) );
519 #endif
520                     return VLC_EGENERIC;
521                 }
522
523                 return i_recv;
524             }
525         }
526     }
527
528     /* We will never be here */
529     return -1;
530 }
531
532
533 /* Write exact amount requested */
534 int __net_Write( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data )
535 {
536     struct timeval  timeout;
537     fd_set          fds_w, fds_e;
538     int             i_send;
539     int             i_total = 0;
540     int             i_ret;
541
542     vlc_bool_t      b_die = p_this->b_die;
543
544     while( i_data > 0 )
545     {
546         do
547         {
548             if( p_this->b_die != b_die )
549             {
550                 return 0;
551             }
552
553             /* Initialize file descriptor set */
554             FD_ZERO( &fds_w );
555             FD_SET( fd, &fds_w );
556             FD_ZERO( &fds_e );
557             FD_SET( fd, &fds_e );
558
559             /* We'll wait 0.5 second if nothing happens */
560             timeout.tv_sec = 0;
561             timeout.tv_usec = 500000;
562
563         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
564                  || ( i_ret < 0 && errno == EINTR ) );
565
566         if( i_ret < 0 )
567         {
568 #if defined(WIN32) || defined(UNDER_CE)
569             msg_Err( p_this, "network select error" );
570 #else
571             msg_Err( p_this, "network select error (%s)", strerror(errno) );
572 #endif
573             return i_total > 0 ? i_total : -1;
574         }
575
576         if( ( i_send = send( fd, p_data, i_data, 0 ) ) < 0 )
577         {
578             /* XXX With udp for example, it will issue a message if the host
579              * isn't listening */
580             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
581             return i_total > 0 ? i_total : -1;
582         }
583
584         p_data += i_send;
585         i_data -= i_send;
586         i_total+= i_send;
587     }
588     return i_total;
589 }
590
591 char *__net_Gets( vlc_object_t *p_this, int fd )
592 {
593     char *psz_line = malloc( 1024 );
594     int  i_line = 0;
595     int  i_max = 1024;
596
597
598     for( ;; )
599     {
600         if( net_Read( p_this, fd, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
601         {
602             psz_line[i_line] = '\0';
603             break;
604         }
605         i_line++;
606
607         if( psz_line[i_line-1] == '\n' )
608         {
609             psz_line[i_line] = '\0';
610             break;
611         }
612
613         if( i_line >= i_max - 1 )
614         {
615             i_max += 1024;
616             psz_line = realloc( psz_line, i_max );
617         }
618     }
619
620     if( i_line <= 0 )
621     {
622         free( psz_line );
623         return NULL;
624     }
625
626     while( i_line >= 1 &&
627            ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
628     {
629         i_line--;
630         psz_line[i_line] = '\0';
631     }
632     return psz_line;
633 }
634
635 int net_Printf( vlc_object_t *p_this, int fd, const char *psz_fmt, ... )
636 {
637     int i_ret;
638     va_list args;
639     va_start( args, psz_fmt );
640     i_ret = net_vaPrintf( p_this, fd, psz_fmt, args );
641     va_end( args );
642
643     return i_ret;
644 }
645
646 int __net_vaPrintf( vlc_object_t *p_this, int fd, const char *psz_fmt,
647                     va_list args )
648 {
649     char    *psz;
650     int     i_size, i_ret;
651
652     vasprintf( &psz, psz_fmt, args );
653     i_size = strlen( psz );
654     i_ret = __net_Write( p_this, fd, psz, i_size ) < i_size ? -1 : i_size;
655     free( psz );
656
657     return i_ret;
658 }