]> git.sesse.net Git - vlc/blob - src/misc/net.c
Better ACL API (full rewrite)
[vlc] / src / misc / net.c
1 /*****************************************************************************
2  * net.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
37 #ifdef HAVE_SYS_TIME_H
38 #    include <sys/time.h>
39 #endif
40
41 #if defined( WIN32 ) || defined( UNDER_CE )
42 #   if defined(UNDER_CE) && defined(sockaddr_storage)
43 #       undef sockaddr_storage
44 #   endif
45 #   include <winsock2.h>
46 #   include <ws2tcpip.h>
47 #else
48 #   include <sys/socket.h>
49 #   include <netinet/in.h>
50 #   ifdef HAVE_ARPA_INET_H
51 #       include <arpa/inet.h>
52 #   endif
53 #   include <netdb.h>
54 #endif
55
56 #ifdef HAVE_UNISTD_H
57 #   include <unistd.h>
58 #endif
59
60 #include "network.h"
61
62 #ifndef INADDR_ANY
63 #   define INADDR_ANY  0x00000000
64 #endif
65 #ifndef INADDR_NONE
66 #   define INADDR_NONE 0xFFFFFFFF
67 #endif
68
69 static int SocksNegociate( vlc_object_t *, int fd, int i_socks_version,
70                            char *psz_socks_user, char *psz_socks_passwd );
71 static int SocksHandshakeTCP( vlc_object_t *,
72                               int fd, int i_socks_version,
73                               char *psz_socks_user, char *psz_socks_passwd,
74                               const char *psz_host, int i_port );
75
76 static int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
77                        int i_protocol )
78 {
79     int fd, i_val;
80
81     fd = socket( i_family, i_socktype, i_protocol );
82     if( fd == -1 )
83     {
84 #if defined(WIN32) || defined(UNDER_CE)
85         msg_Warn( p_this, "cannot create socket (%i)",
86                   WSAGetLastError() );
87 #else
88         msg_Warn( p_this, "cannot create socket (%s)",
89                   strerror( errno ) );
90 #endif
91         return -1;
92     }
93
94         /* Set to non-blocking */
95 #if defined( WIN32 ) || defined( UNDER_CE )
96     {
97         unsigned long i_dummy = 1;
98         if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
99             msg_Err( p_this, "cannot set socket to non-blocking mode" );
100     }
101 #else
102     if( ( ( i_val = fcntl( fd, F_GETFL, 0 ) ) < 0 ) ||
103         ( fcntl( fd, F_SETFL, i_val | O_NONBLOCK ) < 0 ) )
104         msg_Err( p_this, "cannot set socket to non-blocking mode (%s)",
105                  strerror( errno ) );
106 #endif
107
108     i_val = 1;
109     setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
110                 sizeof( i_val ) );
111
112 #ifdef IPV6_V6ONLY
113     /*
114      * Accepts only IPv6 connections on IPv6 sockets
115      * (and open an IPv4 socket later as well if needed).
116      * Only Linux and FreeBSD can map IPv4 connections on IPv6 sockets,
117      * so this allows for more uniform handling across platforms. Besides,
118      * it makes sure that IPv4 addresses will be printed as w.x.y.z rather
119      * than ::ffff:w.x.y.z
120      */
121     if( i_family == AF_INET6 )
122         setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&i_val,
123                     sizeof( i_val ) );
124 #endif
125
126 #if defined( WIN32 ) || defined( UNDER_CE )
127 # ifdef IPV6_PROTECTION_LEVEL
128     if( i_family == AF_INET6 )
129     {
130         i_val = PROTECTION_LEVEL_UNRESTRICTED;
131         setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
132                     sizeof( i_val ) );
133     }
134 # else
135 # warning You are using outdated headers for Winsock !
136 # endif
137 #endif
138     return fd;
139 }
140
141 /*****************************************************************************
142  * __net_OpenTCP:
143  *****************************************************************************
144  * Open a TCP connection and return a handle
145  *****************************************************************************/
146 int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
147 {
148     struct addrinfo hints, *res, *ptr;
149     const char      *psz_realhost;
150     char            *psz_socks;
151     int             i_realport, i_val, i_handle = -1;
152
153     if( i_port == 0 )
154         i_port = 80; /* historical VLC thing */
155
156     memset( &hints, 0, sizeof( hints ) );
157     hints.ai_socktype = SOCK_STREAM;
158
159     psz_socks = var_CreateGetString( p_this, "socks" );
160     if( *psz_socks && *psz_socks != ':' )
161     {
162         char *psz = strchr( psz_socks, ':' );
163
164         if( psz )
165             *psz++ = '\0';
166
167         psz_realhost = psz_socks;
168         i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
169
170         msg_Dbg( p_this, "net: connecting to '%s:%d' for '%s:%d'",
171                  psz_realhost, i_realport, psz_host, i_port );
172     }
173     else
174     {
175         psz_realhost = psz_host;
176         i_realport = i_port;
177
178         msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_realhost,
179                  i_realport );
180     }
181
182     i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
183     if( i_val )
184     {
185         msg_Err( p_this, "cannot resolve '%s:%d' : %s", psz_realhost,
186                  i_realport, vlc_gai_strerror( i_val ) );
187         free( psz_socks );
188         return -1;
189     }
190
191     for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
192     {
193         int fd;
194
195         fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
196                          ptr->ai_protocol );
197         if( fd == -1 )
198             continue;
199
200         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
201         {
202             socklen_t i_val_size = sizeof( i_val );
203             div_t d;
204             struct timeval tv;
205             vlc_value_t timeout;
206 #if defined( WIN32 ) || defined( UNDER_CE )
207             if( WSAGetLastError() != WSAEWOULDBLOCK )
208             {
209                 msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
210                           i_port, WSAGetLastError( ) );
211                 net_Close( fd );
212                 continue;
213             }
214 #else
215             if( errno != EINPROGRESS )
216             {
217                 msg_Warn( p_this, "connection to %s:%d : %s", psz_host,
218                           i_port, strerror( errno ) );
219                 net_Close( fd );
220                 continue;
221             }
222 #endif
223
224             var_Create( p_this, "ipv4-timeout",
225                         VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
226             var_Get( p_this, "ipv4-timeout", &timeout );
227             if( timeout.i_int < 0 )
228             {
229                 msg_Err( p_this, "invalid negative value for ipv4-timeout" );
230                 timeout.i_int = 0;
231             }
232             d = div( timeout.i_int, 100 );
233
234             msg_Dbg( p_this, "connection in progress" );
235             do
236             {
237                 fd_set fds;
238
239                 if( p_this->b_die )
240                 {
241                     msg_Dbg( p_this, "connection aborted" );
242                     net_Close( fd );
243                     vlc_freeaddrinfo( res );
244                     free( psz_socks );
245                     return -1;
246                 }
247
248                 /* Initialize file descriptor set */
249                 FD_ZERO( &fds );
250                 FD_SET( fd, &fds );
251
252                 /* We'll wait 0.1 second if nothing happens */
253                 tv.tv_sec = 0;
254                 tv.tv_usec = (d.quot > 0) ? 100000 : (1000 * d.rem);
255
256                 i_val = select( fd + 1, NULL, &fds, NULL, &tv );
257
258                 if( d.quot <= 0 )
259                 {
260                     msg_Dbg( p_this, "connection timed out" );
261                     net_Close( fd );
262                     fd = -1;
263                     break;
264                 }
265
266                 d.quot--;
267             }
268             while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
269 #if defined( WIN32 ) || defined( UNDER_CE )
270                             ( WSAGetLastError() == WSAEWOULDBLOCK )
271 #else
272                             ( errno == EINTR )
273 #endif
274                      ) );
275
276             if( fd == -1 )
277                 continue; /* timeout */
278
279             if( i_val < 0 )
280             {
281                 msg_Warn( p_this, "connection aborted (select failed)" );
282                 net_Close( fd );
283                 continue;
284             }
285
286 #if !defined( SYS_BEOS ) && !defined( UNDER_CE )
287             if( getsockopt( fd, SOL_SOCKET, SO_ERROR, (void*)&i_val,
288                             &i_val_size ) == -1 || i_val != 0 )
289             {
290 #ifdef WIN32
291                 msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
292                           i_port, WSAGetLastError( ) );
293 #else
294                 msg_Warn( p_this, "connection to %s:%d : %s", psz_host,
295                           i_port, strerror( i_val ) );
296 #endif
297                 net_Close( fd );
298                 continue;
299             }
300 #endif
301         }
302         i_handle = fd; /* success! */
303     }
304     
305     vlc_freeaddrinfo( res );
306
307     if( *psz_socks && *psz_socks != ':' )
308     {
309         char *psz_user = var_CreateGetString( p_this, "socks-user" );
310         char *psz_pwd  = var_CreateGetString( p_this, "socks-pwd" );
311
312         if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
313                                psz_host, i_port ) )
314         {
315             msg_Err( p_this, "failed to use the SOCKS server" );
316             net_Close( i_handle );
317             i_handle = -1;
318         }
319
320         free( psz_user );
321         free( psz_pwd );
322     }
323     free( psz_socks );
324
325     return i_handle;
326 }
327
328
329 /*****************************************************************************
330  * __net_ListenTCP:
331  *****************************************************************************
332  * Open TCP passive "listening" socket(s)
333  * This function returns NULL in case of error.
334  *****************************************************************************/
335 int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
336 {
337     struct addrinfo hints, *res, *ptr;
338     int             i_val, *pi_handles, i_size;
339
340     memset( &hints, 0, sizeof( hints ) );
341     hints.ai_socktype = SOCK_STREAM;
342     hints.ai_flags = AI_PASSIVE;
343
344     msg_Dbg( p_this, "net: listening to '%s:%d'", psz_host, i_port );
345
346     i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
347     if( i_val )
348     {
349         msg_Err( p_this, "cannot resolve '%s:%d' : %s", psz_host, i_port,
350                  vlc_gai_strerror( i_val ) );
351         return NULL;
352     }
353
354     pi_handles = NULL;
355     i_size = 1;
356
357     for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
358     {
359         int fd, *newpi;
360
361         fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
362                          ptr->ai_protocol );
363         if( fd == -1 )
364             continue;
365
366         /* Bind the socket */
367         if( bind( fd, ptr->ai_addr, ptr->ai_addrlen ) )
368         {
369 #if defined(WIN32) || defined(UNDER_CE)
370             msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError( ) );
371 #else
372             msg_Warn( p_this, "cannot bind socket (%s)", strerror( errno ) );
373 #endif
374             net_Close( fd );
375             continue;
376         }
377  
378         /* Listen */
379         if( listen( fd, 100 ) == -1 )
380         {
381 #if defined(WIN32) || defined(UNDER_CE)
382             msg_Err( p_this, "cannot bring socket in listening mode (%i)",
383                      WSAGetLastError());
384 #else
385             msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
386                      strerror( errno ) );
387 #endif
388             net_Close( fd );
389             continue;
390         }
391
392         newpi = (int *)realloc( pi_handles, (++i_size) * sizeof( int ) );
393         if( newpi == NULL )
394         {
395             net_Close( fd );
396             break;
397         }
398         else
399         {
400             newpi[i_size - 2] = fd;
401             pi_handles = newpi;
402         }
403     }
404     
405     vlc_freeaddrinfo( res );
406
407     if( pi_handles != NULL )
408         pi_handles[i_size - 1] = -1;
409     return pi_handles;
410 }
411
412 /*****************************************************************************
413  * __net_Accept:
414  *****************************************************************************
415  * Accept a connection on a set of listening sockets and return it
416  *****************************************************************************/
417 int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
418 {
419     vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
420
421     while( p_this->b_die == b_die )
422     {
423         int i_val = -1, *pi, *pi_end;
424         struct timeval timeout;
425         fd_set fds_r, fds_e;
426
427         pi = pi_fd;
428
429         /* Initialize file descriptor set */
430         FD_ZERO( &fds_r );
431         FD_ZERO( &fds_e );
432
433         for( pi = pi_fd; *pi != -1; pi++ )
434         {
435             int i_fd = *pi;
436
437             if( i_fd > i_val )
438                 i_val = i_fd;
439
440             FD_SET( i_fd, &fds_r );
441             FD_SET( i_fd, &fds_e );
442         }
443         pi_end = pi;
444
445         timeout.tv_sec = 0;
446         timeout.tv_usec = b_block ? 500000 : i_wait;
447
448         i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
449         if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
450         {
451             if( b_block )
452                 continue;
453             else
454                 return -1;
455         }
456         else if( i_val < 0 )
457         {
458 #if defined(WIN32) || defined(UNDER_CE)
459             msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
460 #else
461             msg_Err( p_this, "network select error (%s)", strerror( errno ) );
462 #endif
463             return -1;
464         }
465
466         for( pi = pi_fd; *pi != -1; pi++ )
467         {
468             int i_fd = *pi;
469
470             if( !FD_ISSET( i_fd, &fds_r ) && !FD_ISSET( i_fd, &fds_e ) )
471                 continue;
472
473             i_val = accept( i_fd, NULL, 0 );
474             if( i_val < 0 )
475             {
476 #if defined(WIN32) || defined(UNDER_CE)
477                 msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
478 #else
479                 msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
480 #endif
481             }
482             else
483             {
484                 /*
485                  * This round-robin trick ensures that the first sockets in
486                  * pi_fd won't prevent the last ones from getting accept'ed.
487                  */
488                 --pi_end;
489                 memmove( pi, pi + 1, pi_end - pi );
490                 *pi_end = i_fd;
491                 return i_val;
492             }
493         }
494     }
495
496     return -1;
497 }
498
499 /*****************************************************************************
500  * __net_OpenUDP:
501  *****************************************************************************
502  * Open a UDP connection and return a handle
503  *****************************************************************************/
504 int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
505                    char *psz_server, int i_server )
506 {
507     vlc_value_t      val;
508     void            *private;
509
510     char            *psz_network = "";
511     network_socket_t sock;
512     module_t         *p_network;
513
514
515     /* Check if we have force ipv4 or ipv6 */
516     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
517     var_Get( p_this, "ipv4", &val );
518     if( val.b_bool )
519     {
520         psz_network = "ipv4";
521     }
522
523     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
524     var_Get( p_this, "ipv6", &val );
525     if( val.b_bool )
526     {
527         psz_network = "ipv6";
528     }
529     if( psz_server == NULL ) psz_server = "";
530     if( psz_bind   == NULL ) psz_bind   = "";
531
532     /* Prepare the network_socket_t structure */
533     sock.psz_bind_addr   = psz_bind;
534     sock.i_bind_port     = i_bind;
535     sock.psz_server_addr = psz_server;
536     sock.i_server_port   = i_server;
537     sock.i_ttl           = 0;
538
539     msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
540              psz_server, i_server, psz_bind, i_bind );
541     private = p_this->p_private;
542     p_this->p_private = (void*)&sock;
543     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
544     {
545         msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
546                  psz_server, i_server, psz_bind, i_bind );
547         return -1;
548     }
549     module_Unneed( p_this, p_network );
550     p_this->p_private = private;
551
552     return sock.i_handle;
553 }
554
555 /*****************************************************************************
556  * __net_Close:
557  *****************************************************************************
558  * Close a network handle
559  *****************************************************************************/
560 void net_Close( int fd )
561 {
562 #ifdef UNDER_CE
563     CloseHandle( (HANDLE)fd );
564 #elif defined( WIN32 )
565     closesocket( fd );
566 #else
567     close( fd );
568 #endif
569 }
570
571 void net_ListenClose( int *pi_fd )
572 {
573     if( pi_fd != NULL )
574     {
575         int *pi;
576
577         for( pi = pi_fd; *pi != -1; pi++ )
578             net_Close( *pi );
579         free( pi_fd );
580     }
581 }
582
583 /*****************************************************************************
584  * __net_Read:
585  *****************************************************************************
586  * Read from a network socket
587  * If b_rety is true, then we repeat until we have read the right amount of
588  * data
589  *****************************************************************************/
590 int __net_Read( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
591                 uint8_t *p_data, int i_data, vlc_bool_t b_retry )
592 {
593     struct timeval  timeout;
594     fd_set          fds_r, fds_e;
595     int             i_recv;
596     int             i_total = 0;
597     int             i_ret;
598     vlc_bool_t      b_die = p_this->b_die;
599
600     while( i_data > 0 )
601     {
602         do
603         {
604             if( p_this->b_die != b_die )
605             {
606                 return 0;
607             }
608
609             /* Initialize file descriptor set */
610             FD_ZERO( &fds_r );
611             FD_SET( fd, &fds_r );
612             FD_ZERO( &fds_e );
613             FD_SET( fd, &fds_e );
614
615             /* We'll wait 0.5 second if nothing happens */
616             timeout.tv_sec = 0;
617             timeout.tv_usec = 500000;
618
619         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
620                  || ( i_ret < 0 && errno == EINTR ) );
621
622         if( i_ret < 0 )
623         {
624 #if defined(WIN32) || defined(UNDER_CE)
625             msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
626 #else
627             msg_Err( p_this, "network select error (%s)", strerror(errno) );
628 #endif
629             return i_total > 0 ? i_total : -1;
630         }
631
632         if( ( i_recv = (p_vs != NULL)
633               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
634               : recv( fd, p_data, i_data, 0 ) ) < 0 )
635         {
636 #if defined(WIN32) || defined(UNDER_CE)
637             if( WSAGetLastError() == WSAEWOULDBLOCK )
638             {
639                 /* only happens with p_vs (SSL) - not really an error */
640             }
641             else
642             /* For udp only */
643             /* On win32 recv() will fail if the datagram doesn't fit inside
644              * the passed buffer, even though the buffer will be filled with
645              * the first part of the datagram. */
646             if( WSAGetLastError() == WSAEMSGSIZE )
647             {
648                 msg_Err( p_this, "recv() failed. "
649                          "Increase the mtu size (--mtu option)" );
650                 i_total += i_data;
651             }
652             else if( WSAGetLastError() == WSAEINTR ) continue;
653             else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
654 #else
655             /* EAGAIN only happens with p_vs (SSL) and it's not an error */
656             if( errno != EAGAIN )
657                 msg_Err( p_this, "recv failed (%s)", strerror(errno) );
658 #endif
659             return i_total > 0 ? i_total : -1;
660         }
661         else if( i_recv == 0 )
662         {
663             /* Connection closed */
664             b_retry = VLC_FALSE;
665         }
666
667         p_data += i_recv;
668         i_data -= i_recv;
669         i_total+= i_recv;
670         if( !b_retry )
671         {
672             break;
673         }
674     }
675     return i_total;
676 }
677
678 /*****************************************************************************
679  * __net_ReadNonBlock:
680  *****************************************************************************
681  * Read from a network socket, non blocking mode (with timeout)
682  *****************************************************************************/
683 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
684                         uint8_t *p_data, int i_data, mtime_t i_wait)
685 {
686     struct timeval  timeout;
687     fd_set          fds_r, fds_e;
688     int             i_recv;
689     int             i_ret;
690
691     /* Initialize file descriptor set */
692     FD_ZERO( &fds_r );
693     FD_SET( fd, &fds_r );
694     FD_ZERO( &fds_e );
695     FD_SET( fd, &fds_e );
696
697     timeout.tv_sec = 0;
698     timeout.tv_usec = i_wait;
699
700     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
701
702     if( i_ret < 0 && errno == EINTR )
703     {
704         return 0;
705     }
706     else if( i_ret < 0 )
707     {
708 #if defined(WIN32) || defined(UNDER_CE)
709         msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
710 #else
711         msg_Err( p_this, "network select error (%s)", strerror(errno) );
712 #endif
713         return -1;
714     }
715     else if( i_ret == 0)
716     {
717         return 0;
718     }
719     else
720     {
721 #if !defined(UNDER_CE)
722         if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
723 #endif
724         if( ( i_recv = (p_vs != NULL)
725               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
726               : recv( fd, p_data, i_data, 0 ) ) <= 0 )
727         {
728 #if defined(WIN32) || defined(UNDER_CE)
729             /* For udp only */
730             /* On win32 recv() will fail if the datagram doesn't fit inside
731              * the passed buffer, even though the buffer will be filled with
732              * the first part of the datagram. */
733             if( WSAGetLastError() == WSAEMSGSIZE )
734             {
735                 msg_Err( p_this, "recv() failed. "
736                          "Increase the mtu size (--mtu option)" );
737             }
738             else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
739 #else
740             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
741 #endif
742             return -1;
743         }
744
745         return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
746     }
747
748     /* We will never be here */
749     return -1;
750 }
751
752 /*****************************************************************************
753  * __net_Select:
754  *****************************************************************************
755  * Read from several sockets (with timeout). Takes data from the first socket
756  * that has some.
757  *****************************************************************************/
758 int __net_Select( vlc_object_t *p_this, int *pi_fd, v_socket_t **pp_vs,
759                   int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait )
760 {
761     struct timeval  timeout;
762     fd_set          fds_r, fds_e;
763     int             i_recv;
764     int             i_ret;
765     int             i;
766     int             i_max_fd = 0;
767
768     /* Initialize file descriptor set */
769     FD_ZERO( &fds_r );
770     FD_ZERO( &fds_e );
771
772     for( i = 0 ; i < i_fd ; i++)
773     {
774         if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
775         FD_SET( pi_fd[i], &fds_r );
776         FD_SET( pi_fd[i], &fds_e );
777     }
778
779     timeout.tv_sec = 0;
780     timeout.tv_usec = i_wait;
781
782     i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );
783
784     if( i_ret < 0 && errno == EINTR )
785     {
786         return 0;
787     }
788     else if( i_ret < 0 )
789     {
790         msg_Err( p_this, "network select error (%s)", strerror(errno) );
791         return -1;
792     }
793     else if( i_ret == 0 )
794     {
795         return 0;
796     }
797     else
798     {
799         for( i = 0 ; i < i_fd ; i++)
800         {
801             if( FD_ISSET( pi_fd[i], &fds_r ) )
802             {
803                 i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
804                          ? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
805                          : recv( pi_fd[i], p_data, i_data, 0 );
806                 if( i_recv <= 0 )
807                 {
808 #ifdef WIN32
809                     /* For udp only */
810                     /* On win32 recv() will fail if the datagram doesn't
811                      * fit inside the passed buffer, even though the buffer
812                      *  will be filled with the first part of the datagram. */
813                     if( WSAGetLastError() == WSAEMSGSIZE )
814                     {
815                         msg_Err( p_this, "recv() failed. "
816                              "Increase the mtu size (--mtu option)" );
817                     }
818                     else msg_Err( p_this, "recv failed (%i)",
819                                   WSAGetLastError() );
820 #else
821                      msg_Err( p_this, "recv failed (%s)", strerror(errno) );
822 #endif
823                     return VLC_EGENERIC;
824                 }
825
826                 return i_recv;
827             }
828         }
829     }
830
831     /* We will never be here */
832     return -1;
833 }
834
835
836 /* Write exact amount requested */
837 int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
838                  uint8_t *p_data, int i_data )
839 {
840     struct timeval  timeout;
841     fd_set          fds_w, fds_e;
842     int             i_send;
843     int             i_total = 0;
844     int             i_ret;
845
846     vlc_bool_t      b_die = p_this->b_die;
847
848     while( i_data > 0 )
849     {
850         do
851         {
852             if( p_this->b_die != b_die )
853             {
854                 return 0;
855             }
856
857             /* Initialize file descriptor set */
858             FD_ZERO( &fds_w );
859             FD_SET( fd, &fds_w );
860             FD_ZERO( &fds_e );
861             FD_SET( fd, &fds_e );
862
863             /* We'll wait 0.5 second if nothing happens */
864             timeout.tv_sec = 0;
865             timeout.tv_usec = 500000;
866
867         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
868                  || ( i_ret < 0 && errno == EINTR ) );
869
870         if( i_ret < 0 )
871         {
872 #if defined(WIN32) || defined(UNDER_CE)
873             msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
874 #else
875             msg_Err( p_this, "network select error (%s)", strerror(errno) );
876 #endif
877             return i_total > 0 ? i_total : -1;
878         }
879
880         if( ( i_send = (p_vs != NULL)
881                        ? p_vs->pf_send( p_vs->p_sys, p_data, i_data )
882                        : send( fd, p_data, i_data, 0 ) ) < 0 )
883         {
884             /* XXX With udp for example, it will issue a message if the host
885              * isn't listening */
886             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
887             return i_total > 0 ? i_total : -1;
888         }
889
890         p_data += i_send;
891         i_data -= i_send;
892         i_total+= i_send;
893     }
894     return i_total;
895 }
896
897 char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
898 {
899     char *psz_line = NULL, *ptr = NULL;
900     size_t  i_line = 0, i_max = 0;
901
902
903     for( ;; )
904     {
905         if( i_line == i_max )
906         {
907             i_max += 1024;
908             psz_line = realloc( psz_line, i_max );
909             ptr = psz_line + i_line;
910         }
911
912         if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
913         {
914             if( i_line == 0 )
915             {
916                 free( psz_line );
917                 return NULL;
918             }
919             break;
920         }
921
922         if ( *ptr == '\n' )
923             break;
924
925         i_line++;
926         ptr++;
927     }
928
929     *ptr-- = '\0';
930
931     if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
932         *ptr = '\0';
933
934     return psz_line;
935 }
936
937 int net_Printf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
938                 const char *psz_fmt, ... )
939 {
940     int i_ret;
941     va_list args;
942     va_start( args, psz_fmt );
943     i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
944     va_end( args );
945
946     return i_ret;
947 }
948
949 int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
950                     const char *psz_fmt, va_list args )
951 {
952     char    *psz;
953     int     i_size, i_ret;
954
955     vasprintf( &psz, psz_fmt, args );
956     i_size = strlen( psz );
957     i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
958         ? -1 : i_size;
959     free( psz );
960
961     return i_ret;
962 }
963
964
965
966 /*****************************************************************************
967  * SocksNegociate:
968  *****************************************************************************
969  * Negociate authentication with a SOCKS server.
970  *****************************************************************************/
971 static int SocksNegociate( vlc_object_t *p_obj,
972                            int fd, int i_socks_version,
973                            char *psz_socks_user,
974                            char *psz_socks_passwd )
975 {
976     uint8_t buffer[128+2*256];
977     int i_len;
978     vlc_bool_t b_auth = VLC_FALSE;
979
980     if( i_socks_version != 5 )
981         return VLC_SUCCESS;
982
983     /* We negociate authentication */
984
985     if( psz_socks_user && psz_socks_passwd &&
986         *psz_socks_user && *psz_socks_passwd )
987         b_auth = VLC_TRUE;
988
989     buffer[0] = i_socks_version;    /* SOCKS version */
990     if( b_auth )
991     {
992         buffer[1] = 2;                  /* Number of methods */
993         buffer[2] = 0x00;               /* - No auth required */
994         buffer[3] = 0x02;               /* - USer/Password */
995         i_len = 4;
996     }
997     else
998     {
999         buffer[1] = 1;                  /* Number of methods */
1000         buffer[2] = 0x00;               /* - No auth required */
1001         i_len = 3;
1002     }
1003     
1004     if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
1005         return VLC_EGENERIC;
1006     if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
1007         return VLC_EGENERIC;
1008
1009     msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
1010
1011     if( buffer[1] == 0x00 )
1012     {
1013         msg_Dbg( p_obj, "socks: no authentication required" );
1014     }
1015     else if( buffer[1] == 0x02 )
1016     {
1017         int i_len1 = __MIN( strlen(psz_socks_user), 255 );
1018         int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
1019         msg_Dbg( p_obj, "socks: username/password authentication" );
1020
1021         /* XXX: we don't support user/pwd > 255 (truncated)*/
1022         buffer[0] = i_socks_version;        /* Version */
1023         buffer[1] = i_len1;                 /* User length */
1024         memcpy( &buffer[2], psz_socks_user, i_len1 );
1025         buffer[2+i_len1] = i_len2;          /* Password length */
1026         memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
1027
1028         i_len = 3 + i_len1 + i_len2;
1029
1030         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
1031             return VLC_EGENERIC;
1032
1033         if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
1034             return VLC_EGENERIC;
1035
1036         msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
1037         if( buffer[1] != 0x00 )
1038         {
1039             msg_Err( p_obj, "socks: authentication rejected" );
1040             return VLC_EGENERIC;
1041         }
1042     }
1043     else
1044     {
1045         if( b_auth )
1046             msg_Err( p_obj, "socks: unsupported authentication method %x",
1047                      buffer[0] );
1048         else
1049             msg_Err( p_obj, "socks: authentification needed" );
1050         return VLC_EGENERIC;
1051     }
1052
1053     return VLC_SUCCESS;
1054 }
1055
1056 /*****************************************************************************
1057  * SocksHandshakeTCP:
1058  *****************************************************************************
1059  * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
1060  *****************************************************************************/
1061 static int SocksHandshakeTCP( vlc_object_t *p_obj,
1062                               int fd,
1063                               int i_socks_version,
1064                               char *psz_socks_user, char *psz_socks_passwd,
1065                               const char *psz_host, int i_port )
1066 {
1067     uint8_t buffer[128+2*256];
1068
1069     if( i_socks_version != 4 && i_socks_version != 5 )
1070     {
1071         msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
1072         i_socks_version = 5;
1073     }
1074
1075     if( i_socks_version == 5 && 
1076         SocksNegociate( p_obj, fd, i_socks_version,
1077                         psz_socks_user, psz_socks_passwd ) )
1078         return VLC_EGENERIC;
1079
1080     if( i_socks_version == 4 )
1081     {
1082         struct addrinfo hints = { 0 }, *p_res;
1083
1084         /* v4 only support ipv4 */
1085         hints.ai_family = AF_INET;
1086         if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
1087             return VLC_EGENERIC;
1088
1089         buffer[0] = i_socks_version;
1090         buffer[1] = 0x01;               /* CONNECT */
1091         SetWBE( &buffer[2], i_port );   /* Port */
1092         memcpy( &buffer[4],             /* Address */
1093                 &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
1094         vlc_freeaddrinfo( p_res );
1095
1096         buffer[8] = 0;                  /* Empty user id */
1097
1098         if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
1099             return VLC_EGENERIC;
1100         if( net_Read( p_obj, fd, NULL, buffer, 8, VLC_TRUE ) != 8 )
1101             return VLC_EGENERIC;
1102
1103         msg_Dbg( p_obj, "socks: v=%d cd=%d",
1104                  buffer[0], buffer[1] );
1105
1106         if( buffer[1] != 90 )
1107             return VLC_EGENERIC;
1108     }
1109     else if( i_socks_version == 5 )
1110     {
1111         int i_hlen = __MIN(strlen( psz_host ), 255);
1112         int i_len;
1113
1114         buffer[0] = i_socks_version;    /* Version */
1115         buffer[1] = 0x01;               /* Cmd: connect */
1116         buffer[2] = 0x00;               /* Reserved */
1117         buffer[3] = 3;                  /* ATYP: for now domainname */
1118
1119         buffer[4] = i_hlen;
1120         memcpy( &buffer[5], psz_host, i_hlen );
1121         SetWBE( &buffer[5+i_hlen], i_port );
1122
1123         i_len = 5 + i_hlen + 2;
1124
1125
1126         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
1127             return VLC_EGENERIC;
1128
1129         /* Read the header */
1130         if( net_Read( p_obj, fd, NULL, buffer, 5, VLC_TRUE ) != 5 )
1131             return VLC_EGENERIC;
1132
1133         msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
1134                  buffer[0], buffer[1], buffer[3] );
1135
1136         if( buffer[1] != 0x00 )
1137         {
1138             msg_Err( p_obj, "socks: CONNECT request failed\n" );
1139             return VLC_EGENERIC;
1140         }
1141
1142         /* Read the remaining bytes */
1143         if( buffer[3] == 0x01 )
1144             i_len = 4-1 + 2;
1145         else if( buffer[3] == 0x03 )
1146             i_len = buffer[4] + 2;
1147         else if( buffer[3] == 0x04 )
1148             i_len = 16-1+2;
1149         else 
1150             return VLC_EGENERIC;
1151
1152         if( net_Read( p_obj, fd, NULL, buffer, i_len, VLC_TRUE ) != i_len )
1153             return VLC_EGENERIC;
1154     }
1155
1156     return VLC_SUCCESS;
1157 }
1158