]> git.sesse.net Git - vlc/blob - src/network/tcp.c
Remove erroneous debug message
[vlc] / src / network / tcp.c
1 /*****************************************************************************
2  * tcp.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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 #ifdef HAVE_SYS_TIME_H
37 #    include <sys/time.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #   include <unistd.h>
41 #endif
42
43 #include "network.h"
44
45 static int SocksNegociate( vlc_object_t *, int fd, int i_socks_version,
46                            char *psz_socks_user, char *psz_socks_passwd );
47 static int SocksHandshakeTCP( vlc_object_t *,
48                               int fd, int i_socks_version,
49                               char *psz_socks_user, char *psz_socks_passwd,
50                               const char *psz_host, int i_port );
51 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
52                        int i_protocol );
53 extern int rootwrap_bind (int family, int socktype, int protocol,
54                           const struct sockaddr *addr, size_t alen);
55
56 /*****************************************************************************
57  * __net_ConnectTCP:
58  *****************************************************************************
59  * Open a TCP connection and return a handle
60  *****************************************************************************/
61 int __net_ConnectTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
62 {
63     struct addrinfo hints, *res, *ptr;
64     const char      *psz_realhost;
65     char            *psz_socks;
66     int             i_realport, i_val, i_handle = -1;
67     vlc_bool_t      b_unreach = VLC_FALSE;
68
69     if( i_port == 0 )
70         i_port = 80; /* historical VLC thing */
71
72     memset( &hints, 0, sizeof( hints ) );
73     hints.ai_socktype = SOCK_STREAM;
74
75     psz_socks = var_CreateGetString( p_this, "socks" );
76     if( *psz_socks && *psz_socks != ':' )
77     {
78         char *psz = strchr( psz_socks, ':' );
79
80         if( psz )
81             *psz++ = '\0';
82
83         psz_realhost = psz_socks;
84         i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
85
86         msg_Dbg( p_this, "net: connecting to %s port %d for %s port %d",
87                  psz_realhost, i_realport, psz_host, i_port );
88     }
89     else
90     {
91         psz_realhost = psz_host;
92         i_realport = i_port;
93
94         msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
95                  i_realport );
96     }
97
98     i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
99     if( i_val )
100     {
101         msg_Err( p_this, "cannot resolve %s port %d : %s", psz_realhost,
102                  i_realport, vlc_gai_strerror( i_val ) );
103         free( psz_socks );
104         return -1;
105     }
106
107     for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
108     {
109         int fd;
110
111         fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
112                          ptr->ai_protocol );
113         if( fd == -1 )
114             continue;
115
116         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
117         {
118             socklen_t i_val_size = sizeof( i_val );
119             div_t d;
120             struct timeval tv;
121             vlc_value_t timeout;
122
123 #if defined( WIN32 ) || defined( UNDER_CE )
124             if( WSAGetLastError() != WSAEWOULDBLOCK )
125             {
126                 if( WSAGetLastError () == WSAENETUNREACH )
127                     b_unreach = VLC_TRUE;
128                 else
129                     msg_Warn( p_this, "connection to %s port %d failed (%d)",
130                               psz_host, i_port, WSAGetLastError( ) );
131                 net_Close( fd );
132                 continue;
133             }
134 #else
135             if( errno != EINPROGRESS )
136             {
137                 if( errno == ENETUNREACH )
138                     b_unreach = VLC_TRUE;
139                 else
140                     msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
141                               i_port, strerror( errno ) );
142                 net_Close( fd );
143                 continue;
144             }
145 #endif
146
147             var_Create( p_this, "ipv4-timeout",
148                         VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
149             var_Get( p_this, "ipv4-timeout", &timeout );
150             if( timeout.i_int < 0 )
151             {
152                 msg_Err( p_this, "invalid negative value for ipv4-timeout" );
153                 timeout.i_int = 0;
154             }
155             d = div( timeout.i_int, 100 );
156
157             msg_Dbg( p_this, "connection in progress" );
158             do
159             {
160                 fd_set fds;
161
162                 if( p_this->b_die )
163                 {
164                     msg_Dbg( p_this, "connection aborted" );
165                     net_Close( fd );
166                     vlc_freeaddrinfo( res );
167                     free( psz_socks );
168                     return -1;
169                 }
170
171                 /* Initialize file descriptor set */
172                 FD_ZERO( &fds );
173                 FD_SET( fd, &fds );
174
175                 /* We'll wait 0.1 second if nothing happens */
176                 tv.tv_sec = 0;
177                 tv.tv_usec = (d.quot > 0) ? 100000 : (1000 * d.rem);
178
179                 i_val = select( fd + 1, NULL, &fds, NULL, &tv );
180
181                 if( d.quot <= 0 )
182                 {
183                     msg_Dbg( p_this, "connection timed out" );
184                     net_Close( fd );
185                     fd = -1;
186                     break;
187                 }
188
189                 d.quot--;
190             }
191             while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
192 #if defined( WIN32 ) || defined( UNDER_CE )
193                             ( WSAGetLastError() == WSAEWOULDBLOCK )
194 #else
195                             ( errno == EINTR )
196 #endif
197                      ) );
198
199             if( fd == -1 )
200                 continue; /* timeout */
201
202             if( i_val < 0 )
203             {
204                 msg_Warn( p_this, "connection aborted (select failed)" );
205                 net_Close( fd );
206                 continue;
207             }
208
209 #if !defined( SYS_BEOS ) && !defined( UNDER_CE )
210             if( getsockopt( fd, SOL_SOCKET, SO_ERROR, (void*)&i_val,
211                             &i_val_size ) == -1 || i_val != 0 )
212             {
213                 if( i_val == ENETUNREACH )
214                     b_unreach = VLC_TRUE;
215                 else
216                 {
217 #ifdef WIN32
218                     msg_Warn( p_this, "connection to %s port %d failed (%d)",
219                               psz_host, i_port, WSAGetLastError( ) );
220 #else
221                     msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
222                               i_port, strerror( i_val ) );
223 #endif
224                 }
225                 net_Close( fd );
226                 continue;
227             }
228 #endif
229         }
230         i_handle = fd; /* success! */
231     }
232
233     vlc_freeaddrinfo( res );
234
235     if( i_handle == -1 )
236     {
237         if( b_unreach )
238             msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
239                      i_port );
240         return -1;
241     }
242
243     if( *psz_socks && *psz_socks != ':' )
244     {
245         char *psz_user = var_CreateGetString( p_this, "socks-user" );
246         char *psz_pwd  = var_CreateGetString( p_this, "socks-pwd" );
247
248         if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
249                                psz_host, i_port ) )
250         {
251             msg_Err( p_this, "failed to use the SOCKS server" );
252             net_Close( i_handle );
253             i_handle = -1;
254         }
255
256         free( psz_user );
257         free( psz_pwd );
258     }
259     free( psz_socks );
260
261     return i_handle;
262 }
263
264
265 /*****************************************************************************
266  * __net_ListenTCP:
267  *****************************************************************************
268  * Open TCP passive "listening" socket(s)
269  * This function returns NULL in case of error.
270  *****************************************************************************/
271 int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
272 {
273     struct addrinfo hints, *res, *ptr;
274     int             i_val, *pi_handles, i_size;
275
276     memset( &hints, 0, sizeof( hints ) );
277     hints.ai_socktype = SOCK_STREAM;
278     hints.ai_flags = AI_PASSIVE;
279
280     msg_Dbg( p_this, "net: listening to %s port %d", psz_host, i_port );
281
282     i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
283     if( i_val )
284     {
285         msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
286                  vlc_gai_strerror( i_val ) );
287         return NULL;
288     }
289
290     pi_handles = NULL;
291     i_size = 1;
292
293     for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
294     {
295         int fd, *newpi;
296
297         fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
298                          ptr->ai_protocol );
299         if( fd == -1 )
300             continue;
301
302         /* Bind the socket */
303         if( bind( fd, ptr->ai_addr, ptr->ai_addrlen ) )
304         {
305 #if defined(WIN32) || defined(UNDER_CE)
306             msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError( ) );
307             net_Close( fd );
308             continue;
309 #else
310             int saved_errno;
311
312             saved_errno = errno;
313             net_Close( fd );
314             fd = rootwrap_bind( ptr->ai_family, ptr->ai_socktype,
315                                 ptr->ai_protocol, ptr->ai_addr,
316                                 ptr->ai_addrlen );
317             if( fd != -1 )
318             {
319                 msg_Dbg( p_this, "got socket %d from rootwrap", fd );
320             }
321             else
322             {
323                 msg_Warn( p_this, "cannot bind socket (%s)",
324                           strerror( saved_errno ) );
325                 continue;
326             }
327 #endif
328         }
329
330         /* Listen */
331         if( listen( fd, 100 ) == -1 )
332         {
333 #if defined(WIN32) || defined(UNDER_CE)
334             msg_Err( p_this, "cannot bring socket in listening mode (%i)",
335                      WSAGetLastError());
336 #else
337             msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
338                      strerror( errno ) );
339 #endif
340             net_Close( fd );
341             continue;
342         }
343
344         newpi = (int *)realloc( pi_handles, (++i_size) * sizeof( int ) );
345         if( newpi == NULL )
346         {
347             net_Close( fd );
348             break;
349         }
350         else
351         {
352             newpi[i_size - 2] = fd;
353             pi_handles = newpi;
354         }
355     }
356
357     vlc_freeaddrinfo( res );
358
359     if( pi_handles != NULL )
360         pi_handles[i_size - 1] = -1;
361     return pi_handles;
362 }
363
364 /*****************************************************************************
365  * __net_Accept:
366  *****************************************************************************
367  * Accept a connection on a set of listening sockets and return it
368  *****************************************************************************/
369 int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
370 {
371     vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
372
373     while( p_this->b_die == b_die )
374     {
375         int i_val = -1, *pi, *pi_end;
376         struct timeval timeout;
377         fd_set fds_r, fds_e;
378
379         pi = pi_fd;
380
381         /* Initialize file descriptor set */
382         FD_ZERO( &fds_r );
383         FD_ZERO( &fds_e );
384
385         for( pi = pi_fd; *pi != -1; pi++ )
386         {
387             int i_fd = *pi;
388
389             if( i_fd > i_val )
390                 i_val = i_fd;
391
392             FD_SET( i_fd, &fds_r );
393             FD_SET( i_fd, &fds_e );
394         }
395         pi_end = pi;
396
397         timeout.tv_sec = 0;
398         timeout.tv_usec = b_block ? 500000 : i_wait;
399
400         i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
401         if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
402         {
403             if( b_block )
404                 continue;
405             else
406                 return -1;
407         }
408         else if( i_val < 0 )
409         {
410 #if defined(WIN32) || defined(UNDER_CE)
411             msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
412 #else
413             msg_Err( p_this, "network select error (%s)", strerror( errno ) );
414 #endif
415             return -1;
416         }
417
418         for( pi = pi_fd; *pi != -1; pi++ )
419         {
420             int i_fd = *pi;
421
422             if( !FD_ISSET( i_fd, &fds_r ) && !FD_ISSET( i_fd, &fds_e ) )
423                 continue;
424
425             i_val = accept( i_fd, NULL, 0 );
426             if( i_val < 0 )
427             {
428 #if defined(WIN32) || defined(UNDER_CE)
429                 msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
430 #else
431                 msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
432 #endif
433             }
434             else
435             {
436                 /*
437                  * This round-robin trick ensures that the first sockets in
438                  * pi_fd won't prevent the last ones from getting accept'ed.
439                  */
440                 --pi_end;
441                 memmove( pi, pi + 1, pi_end - pi );
442                 *pi_end = i_fd;
443                 return i_val;
444             }
445         }
446     }
447
448     return -1;
449 }
450
451
452 /*****************************************************************************
453  * SocksNegociate:
454  *****************************************************************************
455  * Negociate authentication with a SOCKS server.
456  *****************************************************************************/
457 static int SocksNegociate( vlc_object_t *p_obj,
458                            int fd, int i_socks_version,
459                            char *psz_socks_user,
460                            char *psz_socks_passwd )
461 {
462     uint8_t buffer[128+2*256];
463     int i_len;
464     vlc_bool_t b_auth = VLC_FALSE;
465
466     if( i_socks_version != 5 )
467         return VLC_SUCCESS;
468
469     /* We negociate authentication */
470
471     if( psz_socks_user && psz_socks_passwd &&
472         *psz_socks_user && *psz_socks_passwd )
473         b_auth = VLC_TRUE;
474
475     buffer[0] = i_socks_version;    /* SOCKS version */
476     if( b_auth )
477     {
478         buffer[1] = 2;                  /* Number of methods */
479         buffer[2] = 0x00;               /* - No auth required */
480         buffer[3] = 0x02;               /* - USer/Password */
481         i_len = 4;
482     }
483     else
484     {
485         buffer[1] = 1;                  /* Number of methods */
486         buffer[2] = 0x00;               /* - No auth required */
487         i_len = 3;
488     }
489
490     if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
491         return VLC_EGENERIC;
492     if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
493         return VLC_EGENERIC;
494
495     msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
496
497     if( buffer[1] == 0x00 )
498     {
499         msg_Dbg( p_obj, "socks: no authentication required" );
500     }
501     else if( buffer[1] == 0x02 )
502     {
503         int i_len1 = __MIN( strlen(psz_socks_user), 255 );
504         int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
505         msg_Dbg( p_obj, "socks: username/password authentication" );
506
507         /* XXX: we don't support user/pwd > 255 (truncated)*/
508         buffer[0] = i_socks_version;        /* Version */
509         buffer[1] = i_len1;                 /* User length */
510         memcpy( &buffer[2], psz_socks_user, i_len1 );
511         buffer[2+i_len1] = i_len2;          /* Password length */
512         memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
513
514         i_len = 3 + i_len1 + i_len2;
515
516         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
517             return VLC_EGENERIC;
518
519         if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
520             return VLC_EGENERIC;
521
522         msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
523         if( buffer[1] != 0x00 )
524         {
525             msg_Err( p_obj, "socks: authentication rejected" );
526             return VLC_EGENERIC;
527         }
528     }
529     else
530     {
531         if( b_auth )
532             msg_Err( p_obj, "socks: unsupported authentication method %x",
533                      buffer[0] );
534         else
535             msg_Err( p_obj, "socks: authentification needed" );
536         return VLC_EGENERIC;
537     }
538
539     return VLC_SUCCESS;
540 }
541
542 /*****************************************************************************
543  * SocksHandshakeTCP:
544  *****************************************************************************
545  * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
546  *****************************************************************************/
547 static int SocksHandshakeTCP( vlc_object_t *p_obj,
548                               int fd,
549                               int i_socks_version,
550                               char *psz_socks_user, char *psz_socks_passwd,
551                               const char *psz_host, int i_port )
552 {
553     uint8_t buffer[128+2*256];
554
555     if( i_socks_version != 4 && i_socks_version != 5 )
556     {
557         msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
558         i_socks_version = 5;
559     }
560
561     if( i_socks_version == 5 && 
562         SocksNegociate( p_obj, fd, i_socks_version,
563                         psz_socks_user, psz_socks_passwd ) )
564         return VLC_EGENERIC;
565
566     if( i_socks_version == 4 )
567     {
568         struct addrinfo hints = { 0 }, *p_res;
569
570         /* v4 only support ipv4 */
571         hints.ai_family = AF_INET;
572         if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
573             return VLC_EGENERIC;
574
575         buffer[0] = i_socks_version;
576         buffer[1] = 0x01;               /* CONNECT */
577         SetWBE( &buffer[2], i_port );   /* Port */
578         memcpy( &buffer[4],             /* Address */
579                 &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
580         vlc_freeaddrinfo( p_res );
581
582         buffer[8] = 0;                  /* Empty user id */
583
584         if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
585             return VLC_EGENERIC;
586         if( net_Read( p_obj, fd, NULL, buffer, 8, VLC_TRUE ) != 8 )
587             return VLC_EGENERIC;
588
589         msg_Dbg( p_obj, "socks: v=%d cd=%d",
590                  buffer[0], buffer[1] );
591
592         if( buffer[1] != 90 )
593             return VLC_EGENERIC;
594     }
595     else if( i_socks_version == 5 )
596     {
597         int i_hlen = __MIN(strlen( psz_host ), 255);
598         int i_len;
599
600         buffer[0] = i_socks_version;    /* Version */
601         buffer[1] = 0x01;               /* Cmd: connect */
602         buffer[2] = 0x00;               /* Reserved */
603         buffer[3] = 3;                  /* ATYP: for now domainname */
604
605         buffer[4] = i_hlen;
606         memcpy( &buffer[5], psz_host, i_hlen );
607         SetWBE( &buffer[5+i_hlen], i_port );
608
609         i_len = 5 + i_hlen + 2;
610
611
612         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
613             return VLC_EGENERIC;
614
615         /* Read the header */
616         if( net_Read( p_obj, fd, NULL, buffer, 5, VLC_TRUE ) != 5 )
617             return VLC_EGENERIC;
618
619         msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
620                  buffer[0], buffer[1], buffer[3] );
621
622         if( buffer[1] != 0x00 )
623         {
624             msg_Err( p_obj, "socks: CONNECT request failed\n" );
625             return VLC_EGENERIC;
626         }
627
628         /* Read the remaining bytes */
629         if( buffer[3] == 0x01 )
630             i_len = 4-1 + 2;
631         else if( buffer[3] == 0x03 )
632             i_len = buffer[4] + 2;
633         else if( buffer[3] == 0x04 )
634             i_len = 16-1+2;
635         else 
636             return VLC_EGENERIC;
637
638         if( net_Read( p_obj, fd, NULL, buffer, i_len, VLC_TRUE ) != i_len )
639             return VLC_EGENERIC;
640     }
641
642     return VLC_SUCCESS;
643 }
644
645 void net_ListenClose( int *pi_fd )
646 {
647     if( pi_fd != NULL )
648     {
649         int *pi;
650
651         for( pi = pi_fd; *pi != -1; pi++ )
652             net_Close( *pi );
653         free( pi_fd );
654     }
655 }