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