1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2005 the VideoLAN team
7 * Authors: Laurent Aimar <fenrir@videolan.org>
8 * RĂ©mi Denis-Courmont <rem # videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
36 #ifdef HAVE_SYS_TIME_H
37 # include <sys/time.h>
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,
54 /*****************************************************************************
56 *****************************************************************************
57 * Open a TCP connection and return a handle
58 *****************************************************************************/
59 int __net_ConnectTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
61 struct addrinfo hints, *res, *ptr;
62 const char *psz_realhost;
64 int i_realport, i_val, i_handle = -1;
65 vlc_bool_t b_unreach = VLC_FALSE;
68 i_port = 80; /* historical VLC thing */
70 memset( &hints, 0, sizeof( hints ) );
71 hints.ai_socktype = SOCK_STREAM;
73 psz_socks = var_CreateGetString( p_this, "socks" );
74 if( *psz_socks && *psz_socks != ':' )
76 char *psz = strchr( psz_socks, ':' );
81 psz_realhost = psz_socks;
82 i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
84 msg_Dbg( p_this, "net: connecting to %s port %d for %s port %d",
85 psz_realhost, i_realport, psz_host, i_port );
89 psz_realhost = psz_host;
92 msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
96 i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
99 msg_Err( p_this, "cannot resolve %s port %d : %s", psz_realhost,
100 i_realport, vlc_gai_strerror( i_val ) );
105 for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
109 fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
114 if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
116 socklen_t i_val_size = sizeof( i_val );
121 #if defined( WIN32 ) || defined( UNDER_CE )
122 if( WSAGetLastError() != WSAEWOULDBLOCK )
124 if( WSAGetLastError () == WSAENETUNREACH )
125 b_unreach = VLC_TRUE;
127 msg_Warn( p_this, "connection to %s port %d failed (%d)",
128 psz_host, i_port, WSAGetLastError( ) );
133 if( errno != EINPROGRESS )
135 if( errno == ENETUNREACH )
136 b_unreach = VLC_TRUE;
138 msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
139 i_port, strerror( errno ) );
145 var_Create( p_this, "ipv4-timeout",
146 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
147 var_Get( p_this, "ipv4-timeout", &timeout );
148 if( timeout.i_int < 0 )
150 msg_Err( p_this, "invalid negative value for ipv4-timeout" );
153 d = div( timeout.i_int, 100 );
155 msg_Dbg( p_this, "connection in progress" );
162 msg_Dbg( p_this, "connection aborted" );
164 vlc_freeaddrinfo( res );
169 /* Initialize file descriptor set */
173 /* We'll wait 0.1 second if nothing happens */
175 tv.tv_usec = (d.quot > 0) ? 100000 : (1000 * d.rem);
177 i_val = select( fd + 1, NULL, &fds, NULL, &tv );
181 msg_Dbg( p_this, "connection timed out" );
189 while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
190 #if defined( WIN32 ) || defined( UNDER_CE )
191 ( WSAGetLastError() == WSAEWOULDBLOCK )
198 continue; /* timeout */
202 msg_Warn( p_this, "connection aborted (select failed)" );
207 #if !defined( SYS_BEOS ) && !defined( UNDER_CE )
208 if( getsockopt( fd, SOL_SOCKET, SO_ERROR, (void*)&i_val,
209 &i_val_size ) == -1 || i_val != 0 )
211 if( i_val == ENETUNREACH )
212 b_unreach = VLC_TRUE;
216 msg_Warn( p_this, "connection to %s port %d failed (%d)",
217 psz_host, i_port, WSAGetLastError( ) );
219 msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
220 i_port, strerror( i_val ) );
228 i_handle = fd; /* success! */
231 vlc_freeaddrinfo( res );
236 msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
241 if( *psz_socks && *psz_socks != ':' )
243 char *psz_user = var_CreateGetString( p_this, "socks-user" );
244 char *psz_pwd = var_CreateGetString( p_this, "socks-pwd" );
246 if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
249 msg_Err( p_this, "failed to use the SOCKS server" );
250 net_Close( i_handle );
263 /*****************************************************************************
265 *****************************************************************************
266 * Open TCP passive "listening" socket(s)
267 * This function returns NULL in case of error.
268 *****************************************************************************/
269 int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
271 struct addrinfo hints, *res, *ptr;
272 int i_val, *pi_handles, i_size;
274 memset( &hints, 0, sizeof( hints ) );
275 hints.ai_socktype = SOCK_STREAM;
276 hints.ai_flags = AI_PASSIVE;
278 msg_Dbg( p_this, "net: listening to %s port %d", psz_host, i_port );
280 i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
283 msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
284 vlc_gai_strerror( i_val ) );
291 for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
295 fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
300 /* Bind the socket */
301 if( bind( fd, ptr->ai_addr, ptr->ai_addrlen ) )
303 #if defined(WIN32) || defined(UNDER_CE)
304 msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError( ) );
306 msg_Warn( p_this, "cannot bind socket (%s)", strerror( errno ) );
313 if( listen( fd, 100 ) == -1 )
315 #if defined(WIN32) || defined(UNDER_CE)
316 msg_Err( p_this, "cannot bring socket in listening mode (%i)",
319 msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
326 newpi = (int *)realloc( pi_handles, (++i_size) * sizeof( int ) );
334 newpi[i_size - 2] = fd;
339 vlc_freeaddrinfo( res );
341 if( pi_handles != NULL )
342 pi_handles[i_size - 1] = -1;
346 /*****************************************************************************
348 *****************************************************************************
349 * Accept a connection on a set of listening sockets and return it
350 *****************************************************************************/
351 int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
353 vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
355 while( p_this->b_die == b_die )
357 int i_val = -1, *pi, *pi_end;
358 struct timeval timeout;
363 /* Initialize file descriptor set */
367 for( pi = pi_fd; *pi != -1; pi++ )
374 FD_SET( i_fd, &fds_r );
375 FD_SET( i_fd, &fds_e );
380 timeout.tv_usec = b_block ? 500000 : i_wait;
382 i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
383 if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
392 #if defined(WIN32) || defined(UNDER_CE)
393 msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
395 msg_Err( p_this, "network select error (%s)", strerror( errno ) );
400 for( pi = pi_fd; *pi != -1; pi++ )
404 if( !FD_ISSET( i_fd, &fds_r ) && !FD_ISSET( i_fd, &fds_e ) )
407 i_val = accept( i_fd, NULL, 0 );
410 #if defined(WIN32) || defined(UNDER_CE)
411 msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
413 msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
419 * This round-robin trick ensures that the first sockets in
420 * pi_fd won't prevent the last ones from getting accept'ed.
423 memmove( pi, pi + 1, pi_end - pi );
434 /*****************************************************************************
436 *****************************************************************************
437 * Negociate authentication with a SOCKS server.
438 *****************************************************************************/
439 static int SocksNegociate( vlc_object_t *p_obj,
440 int fd, int i_socks_version,
441 char *psz_socks_user,
442 char *psz_socks_passwd )
444 uint8_t buffer[128+2*256];
446 vlc_bool_t b_auth = VLC_FALSE;
448 if( i_socks_version != 5 )
451 /* We negociate authentication */
453 if( psz_socks_user && psz_socks_passwd &&
454 *psz_socks_user && *psz_socks_passwd )
457 buffer[0] = i_socks_version; /* SOCKS version */
460 buffer[1] = 2; /* Number of methods */
461 buffer[2] = 0x00; /* - No auth required */
462 buffer[3] = 0x02; /* - USer/Password */
467 buffer[1] = 1; /* Number of methods */
468 buffer[2] = 0x00; /* - No auth required */
472 if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
474 if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
477 msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
479 if( buffer[1] == 0x00 )
481 msg_Dbg( p_obj, "socks: no authentication required" );
483 else if( buffer[1] == 0x02 )
485 int i_len1 = __MIN( strlen(psz_socks_user), 255 );
486 int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
487 msg_Dbg( p_obj, "socks: username/password authentication" );
489 /* XXX: we don't support user/pwd > 255 (truncated)*/
490 buffer[0] = i_socks_version; /* Version */
491 buffer[1] = i_len1; /* User length */
492 memcpy( &buffer[2], psz_socks_user, i_len1 );
493 buffer[2+i_len1] = i_len2; /* Password length */
494 memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
496 i_len = 3 + i_len1 + i_len2;
498 if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
501 if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
504 msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
505 if( buffer[1] != 0x00 )
507 msg_Err( p_obj, "socks: authentication rejected" );
514 msg_Err( p_obj, "socks: unsupported authentication method %x",
517 msg_Err( p_obj, "socks: authentification needed" );
524 /*****************************************************************************
526 *****************************************************************************
527 * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
528 *****************************************************************************/
529 static int SocksHandshakeTCP( vlc_object_t *p_obj,
532 char *psz_socks_user, char *psz_socks_passwd,
533 const char *psz_host, int i_port )
535 uint8_t buffer[128+2*256];
537 if( i_socks_version != 4 && i_socks_version != 5 )
539 msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
543 if( i_socks_version == 5 &&
544 SocksNegociate( p_obj, fd, i_socks_version,
545 psz_socks_user, psz_socks_passwd ) )
548 if( i_socks_version == 4 )
550 struct addrinfo hints = { 0 }, *p_res;
552 /* v4 only support ipv4 */
553 hints.ai_family = AF_INET;
554 if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
557 buffer[0] = i_socks_version;
558 buffer[1] = 0x01; /* CONNECT */
559 SetWBE( &buffer[2], i_port ); /* Port */
560 memcpy( &buffer[4], /* Address */
561 &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
562 vlc_freeaddrinfo( p_res );
564 buffer[8] = 0; /* Empty user id */
566 if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
568 if( net_Read( p_obj, fd, NULL, buffer, 8, VLC_TRUE ) != 8 )
571 msg_Dbg( p_obj, "socks: v=%d cd=%d",
572 buffer[0], buffer[1] );
574 if( buffer[1] != 90 )
577 else if( i_socks_version == 5 )
579 int i_hlen = __MIN(strlen( psz_host ), 255);
582 buffer[0] = i_socks_version; /* Version */
583 buffer[1] = 0x01; /* Cmd: connect */
584 buffer[2] = 0x00; /* Reserved */
585 buffer[3] = 3; /* ATYP: for now domainname */
588 memcpy( &buffer[5], psz_host, i_hlen );
589 SetWBE( &buffer[5+i_hlen], i_port );
591 i_len = 5 + i_hlen + 2;
594 if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
597 /* Read the header */
598 if( net_Read( p_obj, fd, NULL, buffer, 5, VLC_TRUE ) != 5 )
601 msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
602 buffer[0], buffer[1], buffer[3] );
604 if( buffer[1] != 0x00 )
606 msg_Err( p_obj, "socks: CONNECT request failed\n" );
610 /* Read the remaining bytes */
611 if( buffer[3] == 0x01 )
613 else if( buffer[3] == 0x03 )
614 i_len = buffer[4] + 2;
615 else if( buffer[3] == 0x04 )
620 if( net_Read( p_obj, fd, NULL, buffer, i_len, VLC_TRUE ) != i_len )
627 void net_ListenClose( int *pi_fd )
633 for( pi = pi_fd; *pi != -1; pi++ )