]> git.sesse.net Git - vlc/blob - src/misc/net.c
70cd554104c232653da533d200205435d326f8ff
[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 #ifndef INADDR_ANY
61 #   define INADDR_ANY  0x00000000
62 #endif
63 #ifndef INADDR_NONE
64 #   define INADDR_NONE 0xFFFFFFFF
65 #endif
66
67 static int SocksNegociate( vlc_object_t *, int fd, int i_socks_version,
68                            char *psz_socks_user, char *psz_socks_passwd );
69 static int SocksHandshakeTCP( vlc_object_t *,
70                               int fd, int i_socks_version,
71                               char *psz_socks_user, char *psz_socks_passwd,
72                               const char *psz_host, int i_port );
73
74 /*****************************************************************************
75  * net_ConvertIPv4:
76  *****************************************************************************
77  * Open a TCP connection and return a handle
78  *****************************************************************************/
79 int net_ConvertIPv4( uint32_t *p_addr, const char * psz_address )
80 {
81     /* Reset struct */
82     if( !*psz_address )
83     {
84         *p_addr = INADDR_ANY;
85     }
86     else
87     {
88         struct hostent *p_hostent;
89
90         /* Try to convert address directly from in_addr - this will work if
91          * psz_address is dotted decimal. */
92 #ifdef HAVE_ARPA_INET_H
93         if( !inet_aton( psz_address, &p_addr ) )
94 #else
95         *p_addr = inet_addr( psz_address );
96         if( *p_addr == INADDR_NONE )
97 #endif
98         {
99             /* We have a fqdn, try to find its address */
100             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
101             {
102                 return VLC_EGENERIC;
103             }
104
105             /* Copy the first address of the host in the socket address */
106             memcpy( p_addr, p_hostent->h_addr_list[0],
107                     p_hostent->h_length );
108         }
109     }
110     return VLC_SUCCESS;
111 }
112
113 /*****************************************************************************
114  * __net_OpenTCP:
115  *****************************************************************************
116  * Open a TCP connection and return a handle
117  *****************************************************************************/
118 int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
119 {
120     vlc_value_t      val;
121     void            *private;
122
123     char            *psz_network = "";
124     network_socket_t sock;
125     module_t         *p_network;
126
127     /* Check if we have force ipv4 or ipv6 */
128     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
129     var_Get( p_this, "ipv4", &val );
130     if( val.b_bool )
131     {
132         psz_network = "ipv4";
133     }
134
135     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
136     var_Get( p_this, "ipv6", &val );
137     if( val.b_bool )
138     {
139         psz_network = "ipv6";
140     }
141
142     /* Prepare the network_socket_t structure */
143     sock.i_type = NETWORK_TCP;
144     sock.psz_bind_addr   = "";
145     sock.i_bind_port     = 0;
146     sock.i_ttl           = 0;
147
148     var_Create( p_this, "socks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
149     var_Get( p_this, "socks", &val );
150     if( *val.psz_string && *val.psz_string != ':' )
151     {
152         char *psz = strchr( val.psz_string, ':' );
153
154         if( psz )
155             *psz++ = '\0';
156
157         sock.psz_server_addr = (char*)val.psz_string;
158         sock.i_server_port   = psz ? atoi( psz ) : 1080;
159
160         msg_Dbg( p_this, "net: connecting to '%s:%d' for '%s:%d'",
161                  sock.psz_server_addr, sock.i_server_port,
162                  psz_host, i_port );
163     }
164     else
165     {
166         sock.psz_server_addr = (char*)psz_host;
167         sock.i_server_port   = i_port;
168         msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_host, i_port );
169     }
170
171
172     private = p_this->p_private;
173     p_this->p_private = (void*)&sock;
174     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
175     {
176         msg_Dbg( p_this, "net: connection to '%s:%d' failed",
177                  psz_host, i_port );
178         return -1;
179     }
180     module_Unneed( p_this, p_network );
181     p_this->p_private = private;
182
183     if( *val.psz_string && *val.psz_string != ':' )
184     {
185         char *psz_user = var_CreateGetString( p_this, "socks-user" );
186         char *psz_pwd  = var_CreateGetString( p_this, "socks-pwd" );
187
188         if( SocksHandshakeTCP( p_this, sock.i_handle, 5,
189                                psz_user, psz_pwd,
190                                psz_host, i_port ) )
191         {
192             msg_Err( p_this, "failed to use the SOCKS server" );
193             net_Close( sock.i_handle );
194             return -1;
195         }
196
197         free( psz_user );
198         free( psz_pwd );
199     }
200     free( val.psz_string );
201
202     return sock.i_handle;
203 }
204
205 /*****************************************************************************
206  * __net_ListenTCP:
207  *****************************************************************************
208  * Open a TCP listening socket and return it
209  *****************************************************************************/
210 int __net_ListenTCP( vlc_object_t *p_this, char *psz_host, int i_port )
211 {
212     vlc_value_t      val;
213     void            *private;
214
215     char            *psz_network = "";
216     network_socket_t sock;
217     module_t         *p_network;
218
219     /* Check if we have force ipv4 or ipv6 */
220     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
221     var_Get( p_this, "ipv4", &val );
222     if( val.b_bool )
223     {
224         psz_network = "ipv4";
225     }
226
227     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
228     var_Get( p_this, "ipv6", &val );
229     if( val.b_bool )
230     {
231         psz_network = "ipv6";
232     }
233
234     /* Prepare the network_socket_t structure */
235     sock.i_type = NETWORK_TCP_PASSIVE;
236     sock.psz_bind_addr   = "";
237     sock.i_bind_port     = 0;
238     sock.psz_server_addr = psz_host;
239     sock.i_server_port   = i_port;
240     sock.i_ttl           = 0;
241
242     msg_Dbg( p_this, "net: listening to '%s:%d'", psz_host, i_port );
243     private = p_this->p_private;
244     p_this->p_private = (void*)&sock;
245     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
246     {
247         msg_Dbg( p_this, "net: listening to '%s:%d' failed",
248                  psz_host, i_port );
249         return -1;
250     }
251     module_Unneed( p_this, p_network );
252     p_this->p_private = private;
253
254     return sock.i_handle;
255 }
256
257 /*****************************************************************************
258  * __net_Accept:
259  *****************************************************************************
260  * Accept a connection on a listening socket and return it
261  *****************************************************************************/
262 int __net_Accept( vlc_object_t *p_this, int fd, mtime_t i_wait )
263 {
264     vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
265     struct timeval timeout;
266     fd_set fds_r, fds_e;
267     int i_ret;
268
269     while( p_this->b_die == b_die )
270     {
271         /* Initialize file descriptor set */
272         FD_ZERO( &fds_r );
273         FD_SET( fd, &fds_r );
274         FD_ZERO( &fds_e );
275         FD_SET( fd, &fds_e );
276
277         timeout.tv_sec = 0;
278         timeout.tv_usec = b_block ? 500000 : i_wait;
279
280         i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
281         if( (i_ret < 0 && errno == EINTR) || i_ret == 0 )
282         {
283             if( b_block ) continue;
284             else return -1;
285         }
286         else if( i_ret < 0 )
287         {
288 #if defined(WIN32) || defined(UNDER_CE)
289             msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
290 #else
291             msg_Err( p_this, "network select error (%s)", strerror(errno) );
292 #endif
293             return -1;
294         }
295
296         if( ( i_ret = accept( fd, 0, 0 ) ) <= 0 )
297         {
298 #if defined(WIN32) || defined(UNDER_CE)
299             msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
300 #else
301             msg_Err( p_this, "accept failed (%s)", strerror(errno) );
302 #endif
303             return -1;
304         }
305
306         return i_ret;
307     }
308
309     return -1;
310 }
311
312 /*****************************************************************************
313  * __net_OpenUDP:
314  *****************************************************************************
315  * Open a UDP connection and return a handle
316  *****************************************************************************/
317 int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
318                    char *psz_server, int i_server )
319 {
320     vlc_value_t      val;
321     void            *private;
322
323     char            *psz_network = "";
324     network_socket_t sock;
325     module_t         *p_network;
326
327
328     /* Check if we have force ipv4 or ipv6 */
329     var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
330     var_Get( p_this, "ipv4", &val );
331     if( val.b_bool )
332     {
333         psz_network = "ipv4";
334     }
335
336     var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
337     var_Get( p_this, "ipv6", &val );
338     if( val.b_bool )
339     {
340         psz_network = "ipv6";
341     }
342     if( psz_server == NULL ) psz_server = "";
343     if( psz_bind   == NULL ) psz_bind   = "";
344
345     /* Prepare the network_socket_t structure */
346     sock.i_type = NETWORK_UDP;
347     sock.psz_bind_addr   = psz_bind;
348     sock.i_bind_port     = i_bind;
349     sock.psz_server_addr = psz_server;
350     sock.i_server_port   = i_server;
351     sock.i_ttl           = 0;
352
353     msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
354              psz_server, i_server, psz_bind, i_bind );
355     private = p_this->p_private;
356     p_this->p_private = (void*)&sock;
357     if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
358     {
359         msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
360                  psz_server, i_server, psz_bind, i_bind );
361         return -1;
362     }
363     module_Unneed( p_this, p_network );
364     p_this->p_private = private;
365
366     return sock.i_handle;
367 }
368
369 /*****************************************************************************
370  * __net_Close:
371  *****************************************************************************
372  * Close a network handle
373  *****************************************************************************/
374 void net_Close( int fd )
375 {
376 #ifdef UNDER_CE
377     CloseHandle( (HANDLE)fd );
378 #elif defined( WIN32 )
379     closesocket( fd );
380 #else
381     close( fd );
382 #endif
383 }
384
385 /*****************************************************************************
386  * __net_Read:
387  *****************************************************************************
388  * Read from a network socket
389  * If b_rety is true, then we repeat until we have read the right amount of
390  * data
391  *****************************************************************************/
392 int __net_Read( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
393                 uint8_t *p_data, int i_data, vlc_bool_t b_retry )
394 {
395     struct timeval  timeout;
396     fd_set          fds_r, fds_e;
397     int             i_recv;
398     int             i_total = 0;
399     int             i_ret;
400     vlc_bool_t      b_die = p_this->b_die;
401
402     while( i_data > 0 )
403     {
404         do
405         {
406             if( p_this->b_die != b_die )
407             {
408                 return 0;
409             }
410
411             /* Initialize file descriptor set */
412             FD_ZERO( &fds_r );
413             FD_SET( fd, &fds_r );
414             FD_ZERO( &fds_e );
415             FD_SET( fd, &fds_e );
416
417             /* We'll wait 0.5 second if nothing happens */
418             timeout.tv_sec = 0;
419             timeout.tv_usec = 500000;
420
421         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
422                  || ( i_ret < 0 && errno == EINTR ) );
423
424         if( i_ret < 0 )
425         {
426 #if defined(WIN32) || defined(UNDER_CE)
427             msg_Err( p_this, "network select error" );
428 #else
429             msg_Err( p_this, "network select error (%s)", strerror(errno) );
430 #endif
431             return i_total > 0 ? i_total : -1;
432         }
433
434         if( ( i_recv = (p_vs != NULL)
435               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
436               : recv( fd, p_data, i_data, 0 ) ) < 0 )
437         {
438 #if defined(WIN32) || defined(UNDER_CE)
439             /* For udp only */
440             /* On win32 recv() will fail if the datagram doesn't fit inside
441              * the passed buffer, even though the buffer will be filled with
442              * the first part of the datagram. */
443             if( WSAGetLastError() == WSAEMSGSIZE )
444             {
445                 msg_Err( p_this, "recv() failed. "
446                          "Increase the mtu size (--mtu option)" );
447                 i_total += i_data;
448             }
449             else
450                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
451 #else
452             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
453 #endif
454             return i_total > 0 ? i_total : -1;
455         }
456         else if( i_recv == 0 )
457         {
458             /* Connection closed */
459             b_retry = VLC_FALSE;
460         }
461
462         p_data += i_recv;
463         i_data -= i_recv;
464         i_total+= i_recv;
465         if( !b_retry )
466         {
467             break;
468         }
469     }
470     return i_total;
471 }
472
473 /*****************************************************************************
474  * __net_ReadNonBlock:
475  *****************************************************************************
476  * Read from a network socket, non blocking mode (with timeout)
477  *****************************************************************************/
478 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
479                         uint8_t *p_data, int i_data, mtime_t i_wait)
480 {
481     struct timeval  timeout;
482     fd_set          fds_r, fds_e;
483     int             i_recv;
484     int             i_ret;
485
486     /* Initialize file descriptor set */
487     FD_ZERO( &fds_r );
488     FD_SET( fd, &fds_r );
489     FD_ZERO( &fds_e );
490     FD_SET( fd, &fds_e );
491
492     timeout.tv_sec = 0;
493     timeout.tv_usec = i_wait;
494
495     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
496
497     if( i_ret < 0 && errno == EINTR )
498     {
499         return 0;
500     }
501     else if( i_ret < 0 )
502     {
503 #if defined(WIN32) || defined(UNDER_CE)
504         msg_Err( p_this, "network select error" );
505 #else
506         msg_Err( p_this, "network select error (%s)", strerror(errno) );
507 #endif
508         return -1;
509     }
510     else if( i_ret == 0)
511     {
512         return 0;
513     }
514     else
515     {
516 #if !defined(UNDER_CE)
517         if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
518 #endif
519         if( ( i_recv = (p_vs != NULL)
520               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
521               : recv( fd, p_data, i_data, 0 ) ) <= 0 )
522         {
523 #if defined(WIN32) || defined(UNDER_CE)
524             /* For udp only */
525             /* On win32 recv() will fail if the datagram doesn't fit inside
526              * the passed buffer, even though the buffer will be filled with
527              * the first part of the datagram. */
528             if( WSAGetLastError() == WSAEMSGSIZE )
529             {
530                 msg_Err( p_this, "recv() failed. "
531                          "Increase the mtu size (--mtu option)" );
532             }
533             else
534                 msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
535 #else
536             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
537 #endif
538             return -1;
539         }
540
541         return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
542     }
543
544     /* We will never be here */
545     return -1;
546 }
547
548 /*****************************************************************************
549  * __net_Select:
550  *****************************************************************************
551  * Read from several sockets (with timeout). Takes data from the first socket
552  * that has some.
553  *****************************************************************************/
554 int __net_Select( vlc_object_t *p_this, int *pi_fd, v_socket_t **pp_vs,
555                   int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait )
556 {
557     struct timeval  timeout;
558     fd_set          fds_r, fds_e;
559     int             i_recv;
560     int             i_ret;
561     int             i;
562     int             i_max_fd = 0;
563
564     /* Initialize file descriptor set */
565     FD_ZERO( &fds_r );
566     FD_ZERO( &fds_e );
567
568     for( i = 0 ; i < i_fd ; i++)
569     {
570         if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
571         FD_SET( pi_fd[i], &fds_r );
572         FD_SET( pi_fd[i], &fds_e );
573     }
574
575     timeout.tv_sec = 0;
576     timeout.tv_usec = i_wait;
577
578     i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );
579
580     if( i_ret < 0 && errno == EINTR )
581     {
582         return 0;
583     }
584     else if( i_ret < 0 )
585     {
586         msg_Err( p_this, "network select error (%s)", strerror(errno) );
587         return -1;
588     }
589     else if( i_ret == 0 )
590     {
591         return 0;
592     }
593     else
594     {
595         for( i = 0 ; i < i_fd ; i++)
596         {
597             if( FD_ISSET( pi_fd[i], &fds_r ) )
598             {
599                 i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
600                          ? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
601                          : recv( pi_fd[i], p_data, i_data, 0 );
602                 if( i_recv <= 0 )
603                 {
604 #ifdef WIN32
605                     /* For udp only */
606                     /* On win32 recv() will fail if the datagram doesn't
607                      * fit inside the passed buffer, even though the buffer
608                      *  will be filled with the first part of the datagram. */
609                     if( WSAGetLastError() == WSAEMSGSIZE )
610                     {
611                         msg_Err( p_this, "recv() failed. "
612                              "Increase the mtu size (--mtu option)" );
613                     }
614                     else
615                         msg_Err( p_this, "recv failed (%i)",
616                                         WSAGetLastError() );
617 #else
618                      msg_Err( p_this, "recv failed (%s)", strerror(errno) );
619 #endif
620                     return VLC_EGENERIC;
621                 }
622
623                 return i_recv;
624             }
625         }
626     }
627
628     /* We will never be here */
629     return -1;
630 }
631
632
633 /* Write exact amount requested */
634 int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
635                  uint8_t *p_data, int i_data )
636 {
637     struct timeval  timeout;
638     fd_set          fds_w, fds_e;
639     int             i_send;
640     int             i_total = 0;
641     int             i_ret;
642
643     vlc_bool_t      b_die = p_this->b_die;
644
645     while( i_data > 0 )
646     {
647         do
648         {
649             if( p_this->b_die != b_die )
650             {
651                 return 0;
652             }
653
654             /* Initialize file descriptor set */
655             FD_ZERO( &fds_w );
656             FD_SET( fd, &fds_w );
657             FD_ZERO( &fds_e );
658             FD_SET( fd, &fds_e );
659
660             /* We'll wait 0.5 second if nothing happens */
661             timeout.tv_sec = 0;
662             timeout.tv_usec = 500000;
663
664         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
665                  || ( i_ret < 0 && errno == EINTR ) );
666
667         if( i_ret < 0 )
668         {
669 #if defined(WIN32) || defined(UNDER_CE)
670             msg_Err( p_this, "network select error" );
671 #else
672             msg_Err( p_this, "network select error (%s)", strerror(errno) );
673 #endif
674             return i_total > 0 ? i_total : -1;
675         }
676
677         if( ( i_send = (p_vs != NULL)
678                        ? p_vs->pf_send( p_vs->p_sys, p_data, i_data )
679                        : send( fd, p_data, i_data, 0 ) ) < 0 )
680         {
681             /* XXX With udp for example, it will issue a message if the host
682              * isn't listening */
683             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
684             return i_total > 0 ? i_total : -1;
685         }
686
687         p_data += i_send;
688         i_data -= i_send;
689         i_total+= i_send;
690     }
691     return i_total;
692 }
693
694 char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
695 {
696     char *psz_line = malloc( 1024 );
697     int  i_line = 0;
698     int  i_max = 1024;
699
700
701     for( ;; )
702     {
703         if( net_Read( p_this, fd, p_vs, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
704         {
705             psz_line[i_line] = '\0';
706             break;
707         }
708         i_line++;
709
710         if( psz_line[i_line-1] == '\n' )
711         {
712             psz_line[i_line] = '\0';
713             break;
714         }
715
716         if( i_line >= i_max - 1 )
717         {
718             i_max += 1024;
719             psz_line = realloc( psz_line, i_max );
720         }
721     }
722
723     if( i_line <= 0 )
724     {
725         free( psz_line );
726         return NULL;
727     }
728
729     while( i_line >= 1 &&
730            ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
731     {
732         i_line--;
733         psz_line[i_line] = '\0';
734     }
735     return psz_line;
736 }
737
738 int net_Printf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
739                 const char *psz_fmt, ... )
740 {
741     int i_ret;
742     va_list args;
743     va_start( args, psz_fmt );
744     i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
745     va_end( args );
746
747     return i_ret;
748 }
749
750 int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
751                     const char *psz_fmt, va_list args )
752 {
753     char    *psz;
754     int     i_size, i_ret;
755
756     vasprintf( &psz, psz_fmt, args );
757     i_size = strlen( psz );
758     i_ret = __net_Write( p_this, fd, p_vs, psz, i_size ) < i_size ? -1 : i_size;
759     free( psz );
760
761     return i_ret;
762 }
763
764
765
766 /*****************************************************************************
767  * SocksNegociate:
768  *****************************************************************************
769  * Negociate authentication with a SOCKS server.
770  *****************************************************************************/
771 static int SocksNegociate( vlc_object_t *p_obj,
772                            int fd, int i_socks_version,
773                            char *psz_socks_user,
774                            char *psz_socks_passwd )
775 {
776     uint8_t buffer[128+2*256];
777     int i_len;
778     vlc_bool_t b_auth = VLC_FALSE;
779
780     if( i_socks_version != 5 )
781         return VLC_SUCCESS;
782
783     /* We negociate authentication */
784
785     if( psz_socks_user && psz_socks_passwd &&
786         *psz_socks_user && *psz_socks_passwd )
787         b_auth = VLC_TRUE;
788
789     buffer[0] = i_socks_version;    /* SOCKS version */
790     if( b_auth )
791     {
792         buffer[1] = 2;                  /* Number of methods */
793         buffer[2] = 0x00;               /* - No auth required */
794         buffer[3] = 0x02;               /* - USer/Password */
795         i_len = 4;
796     }
797     else
798     {
799         buffer[1] = 1;                  /* Number of methods */
800         buffer[2] = 0x00;               /* - No auth required */
801         i_len = 3;
802     }
803     
804     if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
805         return VLC_EGENERIC;
806     if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
807         return VLC_EGENERIC;
808
809     msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
810
811     if( buffer[1] == 0x00 )
812     {
813         msg_Dbg( p_obj, "socks: no authentication required" );
814     }
815     else if( buffer[1] == 0x02 )
816     {
817         int i_len1 = __MIN( strlen(psz_socks_user), 255 );
818         int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
819         msg_Dbg( p_obj, "socks: username/password authentication" );
820
821         /* XXX: we don't support user/pwd > 255 (truncated)*/
822         buffer[0] = i_socks_version;        /* Version */
823         buffer[1] = i_len1;                 /* User length */
824         memcpy( &buffer[2], psz_socks_user, i_len1 );
825         buffer[2+i_len1] = i_len2;          /* Password length */
826         memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
827
828         i_len = 3 + i_len1 + i_len2;
829
830         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
831             return VLC_EGENERIC;
832
833         if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
834             return VLC_EGENERIC;
835
836         msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
837         if( buffer[1] != 0x00 )
838         {
839             msg_Err( p_obj, "socks: authentication rejected" );
840             return VLC_EGENERIC;
841         }
842     }
843     else
844     {
845         if( b_auth )
846             msg_Err( p_obj, "socks: unsupported authentication method %x",
847                      buffer[0] );
848         else
849             msg_Err( p_obj, "socks: authentification needed" );
850         return VLC_EGENERIC;
851     }
852
853     return VLC_SUCCESS;
854 }
855
856 /*****************************************************************************
857  * SocksHandshakeTCP:
858  *****************************************************************************
859  * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
860  *****************************************************************************/
861 static int SocksHandshakeTCP( vlc_object_t *p_obj,
862                               int fd,
863                               int i_socks_version,
864                               char *psz_socks_user, char *psz_socks_passwd,
865                               const char *psz_host, int i_port )
866 {
867     uint8_t buffer[128+2*256];
868
869     if( i_socks_version != 4 && i_socks_version != 5 )
870     {
871         msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
872         i_socks_version = 5;
873     }
874
875     if( i_socks_version == 5 && 
876         SocksNegociate( p_obj, fd, i_socks_version,
877                         psz_socks_user, psz_socks_passwd ) )
878         return VLC_EGENERIC;
879
880     if( i_socks_version == 4 )
881     {
882         uint32_t addr;
883
884         /* v4 only support ipv4 */
885         if( net_ConvertIPv4( &addr, psz_host ) )
886             return VLC_EGENERIC;
887
888         buffer[0] = i_socks_version;
889         buffer[1] = 0x01;               /* CONNECT */
890         SetWBE( &buffer[2], i_port );   /* Port */
891         memcpy( &buffer[4], &addr, 4 ); /* Addresse */
892         buffer[8] = 0;                  /* Empty user id */
893
894         if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
895             return VLC_EGENERIC;
896         if( net_Read( p_obj, fd, NULL, buffer, 8, VLC_TRUE ) != 8 )
897             return VLC_EGENERIC;
898
899         msg_Dbg( p_obj, "socks: v=%d cd=%d",
900                  buffer[0], buffer[1] );
901
902         if( buffer[1] != 90 )
903             return VLC_EGENERIC;
904     }
905     else if( i_socks_version == 5 )
906     {
907         int i_hlen = __MIN(strlen( psz_host ), 255);
908         int i_len;
909
910         buffer[0] = i_socks_version;    /* Version */
911         buffer[1] = 0x01;               /* Cmd: connect */
912         buffer[2] = 0x00;               /* Reserved */
913         buffer[3] = 3;                  /* ATYP: for now domainname */
914
915         buffer[4] = i_hlen;
916         memcpy( &buffer[5], psz_host, i_hlen );
917         SetWBE( &buffer[5+i_hlen], i_port );
918
919         i_len = 5 + i_hlen + 2;
920
921
922         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
923             return VLC_EGENERIC;
924
925         /* Read the header */
926         if( net_Read( p_obj, fd, NULL, buffer, 5, VLC_TRUE ) != 5 )
927             return VLC_EGENERIC;
928
929         msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
930                  buffer[0], buffer[1], buffer[3] );
931
932         if( buffer[1] != 0x00 )
933         {
934             msg_Err( p_obj, "socks: CONNECT request failed\n" );
935             return VLC_EGENERIC;
936         }
937
938         /* Read the remaining bytes */
939         if( buffer[3] == 0x01 )
940             i_len = 4-1 + 2;
941         else if( buffer[3] == 0x03 )
942             i_len = buffer[4] + 2;
943         else if( buffer[3] == 0x04 )
944             i_len = 16-1+2;
945         else 
946             return VLC_EGENERIC;
947
948         if( net_Read( p_obj, fd, NULL, buffer, i_len, VLC_TRUE ) != i_len )
949             return VLC_EGENERIC;
950     }
951
952     return VLC_SUCCESS;
953 }
954
955