/*****************************************************************************
* net.c:
*****************************************************************************
- * Copyright (C) 2004 VideoLAN
+ * Copyright (C) 2004-2005 the VideoLAN team
* $Id$
*
* Authors: Laurent Aimar <fenrir@videolan.org>
+ * RĂ©mi Denis-Courmont <rem # videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
-
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
-
-#if defined( UNDER_CE )
-# include <winsock.h>
-#elif defined( WIN32 )
-# include <winsock2.h>
-# include <ws2tcpip.h>
-# ifndef IN_MULTICAST
-# define IN_MULTICAST(a) IN_CLASSD(a)
-# endif
-#else
-# include <sys/socket.h>
-#endif
-
#ifdef HAVE_UNISTD_H
# include <unistd.h>
-#elif defined( WIN32 ) && !defined( UNDER_CE )
-# include <io.h>
#endif
#include "network.h"
char *psz_socks_user, char *psz_socks_passwd,
const char *psz_host, int i_port );
-/*****************************************************************************
- * net_ConvertIPv4:
- *****************************************************************************
- * Open a TCP connection and return a handle
- *****************************************************************************/
-int net_ConvertIPv4( uint32_t *p_addr, const char * psz_address )
+static int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
+ int i_protocol )
{
- /* Reset struct */
- if( !*psz_address )
+ int fd, i_val;
+
+ fd = socket( i_family, i_socktype, i_protocol );
+ if( fd == -1 )
{
- *p_addr = INADDR_ANY;
+#if defined(WIN32) || defined(UNDER_CE)
+ if( WSAGetLastError ( ) != WSAEAFNOSUPPORT )
+ msg_Warn( p_this, "cannot create socket (%i)",
+ WSAGetLastError() );
+#else
+ if( errno != EAFNOSUPPORT )
+ msg_Warn( p_this, "cannot create socket (%s)",
+ strerror( errno ) );
+#endif
+ return -1;
}
- else
- {
- struct hostent *p_hostent;
- /* Try to convert address directly from in_addr - this will work if
- * psz_address is dotted decimal. */
-#ifdef HAVE_ARPA_INET_H
- if( !inet_aton( psz_address, &p_addr ) )
+ /* Set to non-blocking */
+#if defined( WIN32 ) || defined( UNDER_CE )
+ {
+ unsigned long i_dummy = 1;
+ if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
+ msg_Err( p_this, "cannot set socket to non-blocking mode" );
+ }
#else
- *p_addr = inet_addr( psz_address );
- if( *p_addr == INADDR_NONE )
+ if( ( ( i_val = fcntl( fd, F_GETFL, 0 ) ) < 0 ) ||
+ ( fcntl( fd, F_SETFL, i_val | O_NONBLOCK ) < 0 ) )
+ msg_Err( p_this, "cannot set socket to non-blocking mode (%s)",
+ strerror( errno ) );
#endif
- {
- /* We have a fqdn, try to find its address */
- if ( (p_hostent = gethostbyname( psz_address )) == NULL )
- {
- return VLC_EGENERIC;
- }
- /* Copy the first address of the host in the socket address */
- memcpy( p_addr, p_hostent->h_addr_list[0],
- p_hostent->h_length );
- }
+ i_val = 1;
+ setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
+ sizeof( i_val ) );
+
+#ifdef IPV6_V6ONLY
+ /*
+ * Accepts only IPv6 connections on IPv6 sockets
+ * (and open an IPv4 socket later as well if needed).
+ * Only Linux and FreeBSD can map IPv4 connections on IPv6 sockets,
+ * so this allows for more uniform handling across platforms. Besides,
+ * it makes sure that IPv4 addresses will be printed as w.x.y.z rather
+ * than ::ffff:w.x.y.z
+ */
+ if( i_family == AF_INET6 )
+ setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&i_val,
+ sizeof( i_val ) );
+#endif
+
+#if defined( WIN32 ) || defined( UNDER_CE )
+# ifndef IPV6_PROTECTION_LEVEL
+# define IPV6_PROTECTION_LEVEL 23
+# endif
+ if( i_family == AF_INET6 )
+ {
+ i_val = 30 /*PROTECTION_LEVEL_UNRESTRICTED*/;
+ setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL,
+ (const char*)&i_val, sizeof( i_val ) );
}
- return VLC_SUCCESS;
+#endif
+ return fd;
}
/*****************************************************************************
- * __net_OpenTCP:
+ * __net_ConnectTCP:
*****************************************************************************
* Open a TCP connection and return a handle
*****************************************************************************/
-int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
+int __net_ConnectTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
{
- vlc_value_t val;
- void *private;
+ struct addrinfo hints, *res, *ptr;
+ const char *psz_realhost;
+ char *psz_socks;
+ int i_realport, i_val, i_handle = -1;
+ vlc_bool_t b_unreach = VLC_FALSE;
- char *psz_network = "";
- network_socket_t sock;
- module_t *p_network;
+ if( i_port == 0 )
+ i_port = 80; /* historical VLC thing */
- /* Check if we have force ipv4 or ipv6 */
- var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv4", &val );
- if( val.b_bool )
- {
- psz_network = "ipv4";
- }
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_socktype = SOCK_STREAM;
- var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv6", &val );
- if( val.b_bool )
+ psz_socks = var_CreateGetString( p_this, "socks" );
+ if( *psz_socks && *psz_socks != ':' )
{
- psz_network = "ipv6";
- }
-
- /* Prepare the network_socket_t structure */
- sock.i_type = NETWORK_TCP;
- sock.psz_bind_addr = "";
- sock.i_bind_port = 0;
- sock.i_ttl = 0;
-
- var_Create( p_this, "socks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
- var_Get( p_this, "socks", &val );
- if( *val.psz_string && *val.psz_string != ':' )
- {
- char *psz = strchr( val.psz_string, ':' );
+ char *psz = strchr( psz_socks, ':' );
if( psz )
*psz++ = '\0';
- sock.psz_server_addr = (char*)val.psz_string;
- sock.i_server_port = psz ? atoi( psz ) : 1080;
+ psz_realhost = psz_socks;
+ i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
- msg_Dbg( p_this, "net: connecting to '%s:%d' for '%s:%d'",
- sock.psz_server_addr, sock.i_server_port,
- psz_host, i_port );
+ msg_Dbg( p_this, "net: connecting to %s port %d for %s port %d",
+ psz_realhost, i_realport, psz_host, i_port );
}
else
{
- sock.psz_server_addr = (char*)psz_host;
- sock.i_server_port = i_port;
- msg_Dbg( p_this, "net: connecting to '%s:%d'", psz_host, i_port );
+ psz_realhost = psz_host;
+ i_realport = i_port;
+
+ msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
+ i_realport );
}
+ i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res );
+ if( i_val )
+ {
+ msg_Err( p_this, "cannot resolve %s port %d : %s", psz_realhost,
+ i_realport, vlc_gai_strerror( i_val ) );
+ free( psz_socks );
+ return -1;
+ }
- private = p_this->p_private;
- p_this->p_private = (void*)&sock;
- if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
+ for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
{
- msg_Dbg( p_this, "net: connection to '%s:%d' failed",
- psz_host, i_port );
+ int fd;
+
+ fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
+ ptr->ai_protocol );
+ if( fd == -1 )
+ continue;
+
+ if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
+ {
+ socklen_t i_val_size = sizeof( i_val );
+ div_t d;
+ struct timeval tv;
+ vlc_value_t timeout;
+
+#if defined( WIN32 ) || defined( UNDER_CE )
+ if( WSAGetLastError() != WSAEWOULDBLOCK )
+ {
+ if( WSAGetLastError () == WSAENETUNREACH )
+ b_unreach = VLC_TRUE;
+ else
+ msg_Warn( p_this, "connection to %s port %d failed (%d)",
+ psz_host, i_port, WSAGetLastError( ) );
+ net_Close( fd );
+ continue;
+ }
+#else
+ if( errno != EINPROGRESS )
+ {
+ if( errno == ENETUNREACH )
+ b_unreach = VLC_TRUE;
+ else
+ msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
+ i_port, strerror( errno ) );
+ net_Close( fd );
+ continue;
+ }
+#endif
+
+ var_Create( p_this, "ipv4-timeout",
+ VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+ var_Get( p_this, "ipv4-timeout", &timeout );
+ if( timeout.i_int < 0 )
+ {
+ msg_Err( p_this, "invalid negative value for ipv4-timeout" );
+ timeout.i_int = 0;
+ }
+ d = div( timeout.i_int, 100 );
+
+ msg_Dbg( p_this, "connection in progress" );
+ do
+ {
+ fd_set fds;
+
+ if( p_this->b_die )
+ {
+ msg_Dbg( p_this, "connection aborted" );
+ net_Close( fd );
+ vlc_freeaddrinfo( res );
+ free( psz_socks );
+ return -1;
+ }
+
+ /* Initialize file descriptor set */
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+
+ /* We'll wait 0.1 second if nothing happens */
+ tv.tv_sec = 0;
+ tv.tv_usec = (d.quot > 0) ? 100000 : (1000 * d.rem);
+
+ i_val = select( fd + 1, NULL, &fds, NULL, &tv );
+
+ if( d.quot <= 0 )
+ {
+ msg_Dbg( p_this, "connection timed out" );
+ net_Close( fd );
+ fd = -1;
+ break;
+ }
+
+ d.quot--;
+ }
+ while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
+#if defined( WIN32 ) || defined( UNDER_CE )
+ ( WSAGetLastError() == WSAEWOULDBLOCK )
+#else
+ ( errno == EINTR )
+#endif
+ ) );
+
+ if( fd == -1 )
+ continue; /* timeout */
+
+ if( i_val < 0 )
+ {
+ msg_Warn( p_this, "connection aborted (select failed)" );
+ net_Close( fd );
+ continue;
+ }
+
+#if !defined( SYS_BEOS ) && !defined( UNDER_CE )
+ if( getsockopt( fd, SOL_SOCKET, SO_ERROR, (void*)&i_val,
+ &i_val_size ) == -1 || i_val != 0 )
+ {
+ if( i_val == ENETUNREACH )
+ b_unreach = VLC_TRUE;
+ else
+ {
+#ifdef WIN32
+ msg_Warn( p_this, "connection to %s port %d failed (%d)",
+ psz_host, i_port, WSAGetLastError( ) );
+#else
+ msg_Warn( p_this, "connection to %s port %d : %s", psz_host,
+ i_port, strerror( i_val ) );
+#endif
+ }
+ net_Close( fd );
+ continue;
+ }
+#endif
+ }
+ i_handle = fd; /* success! */
+ }
+
+ vlc_freeaddrinfo( res );
+
+ if( i_handle == -1 )
+ {
+ if( b_unreach )
+ msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
+ i_port );
return -1;
}
- module_Unneed( p_this, p_network );
- p_this->p_private = private;
- if( *val.psz_string && *val.psz_string != ':' )
+ if( *psz_socks && *psz_socks != ':' )
{
char *psz_user = var_CreateGetString( p_this, "socks-user" );
char *psz_pwd = var_CreateGetString( p_this, "socks-pwd" );
- if( SocksHandshakeTCP( p_this, sock.i_handle, 5,
- psz_user, psz_pwd,
+ if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
psz_host, i_port ) )
{
msg_Err( p_this, "failed to use the SOCKS server" );
- net_Close( sock.i_handle );
- return -1;
+ net_Close( i_handle );
+ i_handle = -1;
}
free( psz_user );
free( psz_pwd );
}
- free( val.psz_string );
+ free( psz_socks );
- return sock.i_handle;
+ return i_handle;
}
+
/*****************************************************************************
* __net_ListenTCP:
*****************************************************************************
- * Open a TCP listening socket and return it
+ * Open TCP passive "listening" socket(s)
+ * This function returns NULL in case of error.
*****************************************************************************/
-int __net_ListenTCP( vlc_object_t *p_this, char *psz_host, int i_port )
+int *__net_ListenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
{
- vlc_value_t val;
- void *private;
+ struct addrinfo hints, *res, *ptr;
+ int i_val, *pi_handles, i_size;
- char *psz_network = "";
- network_socket_t sock;
- module_t *p_network;
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
- /* Check if we have force ipv4 or ipv6 */
- var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv4", &val );
- if( val.b_bool )
- {
- psz_network = "ipv4";
- }
+ msg_Dbg( p_this, "net: listening to %s port %d", psz_host, i_port );
- var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv6", &val );
- if( val.b_bool )
+ i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
+ if( i_val )
{
- psz_network = "ipv6";
+ msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
+ vlc_gai_strerror( i_val ) );
+ return NULL;
}
- /* Prepare the network_socket_t structure */
- sock.i_type = NETWORK_TCP_PASSIVE;
- sock.psz_bind_addr = "";
- sock.i_bind_port = 0;
- sock.psz_server_addr = psz_host;
- sock.i_server_port = i_port;
- sock.i_ttl = 0;
+ pi_handles = NULL;
+ i_size = 1;
- msg_Dbg( p_this, "net: listening to '%s:%d'", psz_host, i_port );
- private = p_this->p_private;
- p_this->p_private = (void*)&sock;
- if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
+ for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
{
- msg_Dbg( p_this, "net: listening to '%s:%d' failed",
- psz_host, i_port );
- return -1;
+ int fd, *newpi;
+
+ fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
+ ptr->ai_protocol );
+ if( fd == -1 )
+ continue;
+
+ /* Bind the socket */
+ if( bind( fd, ptr->ai_addr, ptr->ai_addrlen ) )
+ {
+#if defined(WIN32) || defined(UNDER_CE)
+ msg_Warn( p_this, "cannot bind socket (%i)", WSAGetLastError( ) );
+#else
+ msg_Warn( p_this, "cannot bind socket (%s)", strerror( errno ) );
+#endif
+ net_Close( fd );
+ continue;
+ }
+
+ /* Listen */
+ if( listen( fd, 100 ) == -1 )
+ {
+#if defined(WIN32) || defined(UNDER_CE)
+ msg_Err( p_this, "cannot bring socket in listening mode (%i)",
+ WSAGetLastError());
+#else
+ msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
+ strerror( errno ) );
+#endif
+ net_Close( fd );
+ continue;
+ }
+
+ newpi = (int *)realloc( pi_handles, (++i_size) * sizeof( int ) );
+ if( newpi == NULL )
+ {
+ net_Close( fd );
+ break;
+ }
+ else
+ {
+ newpi[i_size - 2] = fd;
+ pi_handles = newpi;
+ }
}
- module_Unneed( p_this, p_network );
- p_this->p_private = private;
- return sock.i_handle;
+ vlc_freeaddrinfo( res );
+
+ if( pi_handles != NULL )
+ pi_handles[i_size - 1] = -1;
+ return pi_handles;
}
/*****************************************************************************
* __net_Accept:
*****************************************************************************
- * Accept a connection on a listening socket and return it
+ * Accept a connection on a set of listening sockets and return it
*****************************************************************************/
-int __net_Accept( vlc_object_t *p_this, int fd, mtime_t i_wait )
+int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait )
{
vlc_bool_t b_die = p_this->b_die, b_block = (i_wait < 0);
- struct timeval timeout;
- fd_set fds_r, fds_e;
- int i_ret;
while( p_this->b_die == b_die )
{
+ int i_val = -1, *pi, *pi_end;
+ struct timeval timeout;
+ fd_set fds_r, fds_e;
+
+ pi = pi_fd;
+
/* Initialize file descriptor set */
FD_ZERO( &fds_r );
- FD_SET( fd, &fds_r );
FD_ZERO( &fds_e );
- FD_SET( fd, &fds_e );
+
+ for( pi = pi_fd; *pi != -1; pi++ )
+ {
+ int i_fd = *pi;
+
+ if( i_fd > i_val )
+ i_val = i_fd;
+
+ FD_SET( i_fd, &fds_r );
+ FD_SET( i_fd, &fds_e );
+ }
+ pi_end = pi;
timeout.tv_sec = 0;
timeout.tv_usec = b_block ? 500000 : i_wait;
- i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
- if( (i_ret < 0 && errno == EINTR) || i_ret == 0 )
+ i_val = select( i_val + 1, &fds_r, NULL, &fds_e, &timeout );
+ if( ( ( i_val < 0 ) && ( errno == EINTR ) ) || i_val == 0 )
{
- if( b_block ) continue;
- else return -1;
+ if( b_block )
+ continue;
+ else
+ return -1;
}
- else if( i_ret < 0 )
+ else if( i_val < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
msg_Err( p_this, "network select error (%i)", WSAGetLastError() );
#else
- msg_Err( p_this, "network select error (%s)", strerror(errno) );
+ msg_Err( p_this, "network select error (%s)", strerror( errno ) );
#endif
return -1;
}
- if( ( i_ret = accept( fd, 0, 0 ) ) <= 0 )
+ for( pi = pi_fd; *pi != -1; pi++ )
{
+ int i_fd = *pi;
+
+ if( !FD_ISSET( i_fd, &fds_r ) && !FD_ISSET( i_fd, &fds_e ) )
+ continue;
+
+ i_val = accept( i_fd, NULL, 0 );
+ if( i_val < 0 )
+ {
#if defined(WIN32) || defined(UNDER_CE)
- msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
+ msg_Err( p_this, "accept failed (%i)", WSAGetLastError() );
#else
- msg_Err( p_this, "accept failed (%s)", strerror(errno) );
+ msg_Err( p_this, "accept failed (%s)", strerror( errno ) );
#endif
- return -1;
+ }
+ else
+ {
+ /*
+ * This round-robin trick ensures that the first sockets in
+ * pi_fd won't prevent the last ones from getting accept'ed.
+ */
+ --pi_end;
+ memmove( pi, pi + 1, pi_end - pi );
+ *pi_end = i_fd;
+ return i_val;
+ }
}
-
- return i_ret;
}
return -1;
}
+
/*****************************************************************************
- * __net_OpenUDP:
+ * __net_ConnectUDP:
*****************************************************************************
- * Open a UDP connection and return a handle
+ * Open a UDP socket to send data to a defined destination, with an optional
+ * hop limit.
*****************************************************************************/
-int __net_OpenUDP( vlc_object_t *p_this, char *psz_bind, int i_bind,
- char *psz_server, int i_server )
+int __net_ConnectUDP( vlc_object_t *p_this, const char *psz_host, int i_port,
+ int hlim )
{
- vlc_value_t val;
- void *private;
+ struct addrinfo hints, *res, *ptr;
+ int i_val, i_handle = -1;
+ vlc_bool_t b_unreach = VLC_FALSE;
- char *psz_network = "";
- network_socket_t sock;
- module_t *p_network;
+ if( i_port == 0 )
+ i_port = 1234; /* historical VLC thing */
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_socktype = SOCK_DGRAM;
- /* Check if we have force ipv4 or ipv6 */
- var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv4", &val );
- if( val.b_bool )
+ msg_Dbg( p_this, "net: connecting to %s port %d", psz_host, i_port );
+
+ i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
+ if( i_val )
{
- psz_network = "ipv4";
+ msg_Err( p_this, "cannot resolve %s port %d : %s", psz_host, i_port,
+ vlc_gai_strerror( i_val ) );
+ return -1;
}
- var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv6", &val );
- if( val.b_bool )
+ for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
{
- psz_network = "ipv6";
+ int fd;
+
+ fd = net_Socket( p_this, ptr->ai_family, ptr->ai_socktype,
+ ptr->ai_protocol );
+ if( fd == -1 )
+ continue;
+#if !defined( SYS_BEOS )
+ else
+ {
+ int i_val;
+
+ /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
+ * packet loss caused by scheduling problems */
+ i_val = 0x80000;
+ setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *)&i_val,
+ sizeof( i_val ) );
+ i_val = 0x80000;
+ setsockopt( i_handle, SOL_SOCKET, SO_SNDBUF, (void *)&i_val,
+ sizeof( i_val ) );
+ }
+#endif
+
+ if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
+ {
+ /* success */
+ i_handle = fd;
+ break;
+ }
+
+#if defined( WIN32 ) || defined( UNDER_CE )
+ if( WSAGetLastError () == WSAENETUNREACH )
+#else
+ if( errno == ENETUNREACH )
+#endif
+ b_unreach = VLC_TRUE;
+ else
+ {
+ msg_Warn( p_this, "%s port %d : %s", psz_host, i_port,
+ strerror( errno ) );
+ net_Close( fd );
+ continue;
+ }
+ }
+
+ vlc_freeaddrinfo( res );
+
+ if( i_handle == -1 )
+ {
+ if( b_unreach )
+ msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
+ i_port );
+ return -1;
}
+
+ return i_handle;
+}
+
+/*****************************************************************************
+ * __net_OpenUDP:
+ *****************************************************************************
+ * Open a UDP connection and return a handle
+ *****************************************************************************/
+int __net_OpenUDP( vlc_object_t *p_this, const char *psz_bind, int i_bind,
+ const char *psz_server, int i_server )
+{
+ vlc_value_t v4, v6;
+ void *private;
+ network_socket_t sock;
+ module_t *p_network = NULL;
+
+ if( ( psz_server != NULL ) && ( psz_server[0] == '\0' ) )
+ msg_Warn( p_this, "calling net_OpenUDP with an explicit destination "
+ "is obsolete - use net_ConnectUDP instead" );
+ if( i_server != 0 )
+ msg_Warn( p_this, "calling net_OpenUDP with an explicit destination "
+ "port is obsolete - use __net_ConnectUDP instead" );
+
if( psz_server == NULL ) psz_server = "";
- if( psz_bind == NULL ) psz_bind = "";
+ if( psz_bind == NULL ) psz_bind = "";
/* Prepare the network_socket_t structure */
- sock.i_type = NETWORK_UDP;
sock.psz_bind_addr = psz_bind;
sock.i_bind_port = i_bind;
sock.psz_server_addr = psz_server;
sock.i_server_port = i_server;
sock.i_ttl = 0;
+ sock.v6only = 0;
+ sock.i_handle = -1;
- msg_Dbg( p_this, "net: connecting to '%s:%d@%s:%d'",
+ msg_Dbg( p_this, "net: connecting to '[%s]:%d@[%s]:%d'",
psz_server, i_server, psz_bind, i_bind );
- private = p_this->p_private;
- p_this->p_private = (void*)&sock;
- if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
+
+ /* Check if we have force ipv4 or ipv6 */
+ var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+ var_Get( p_this, "ipv4", &v4 );
+ var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+ var_Get( p_this, "ipv6", &v6 );
+
+ if( !v4.b_bool )
{
- msg_Dbg( p_this, "net: connection to '%s:%d@%s:%d' failed",
- psz_server, i_server, psz_bind, i_bind );
- return -1;
+ if( v6.b_bool )
+ sock.v6only = 1;
+
+ /* try IPv6 first (unless IPv4 forced) */
+ private = p_this->p_private;
+ p_this->p_private = (void*)&sock;
+ p_network = module_Need( p_this, "network", "ipv6", VLC_TRUE );
+
+ if( p_network != NULL )
+ module_Unneed( p_this, p_network );
+
+ p_this->p_private = private;
+
+ /*
+ * Check if the IP stack can receive IPv4 packets on IPv6 sockets.
+ * If yes, then it is better to use the IPv6 socket.
+ * Otherwise, if we also get an IPv4, we have to choose, so we use
+ * IPv4 only.
+ */
+ if( ( sock.i_handle != -1 ) && ( ( sock.v6only == 0 ) || v6.b_bool ) )
+ return sock.i_handle;
}
- module_Unneed( p_this, p_network );
- p_this->p_private = private;
+
+ if( !v6.b_bool )
+ {
+ int fd6 = sock.i_handle;
+
+ /* also try IPv4 (unless IPv6 forced) */
+ private = p_this->p_private;
+ p_this->p_private = (void*)&sock;
+ p_network = module_Need( p_this, "network", "ipv4", VLC_TRUE );
+
+ if( p_network != NULL )
+ module_Unneed( p_this, p_network );
+
+ p_this->p_private = private;
+
+ if( fd6 != -1 )
+ {
+ if( sock.i_handle != -1 )
+ {
+ msg_Warn( p_this, "net: lame IPv6/IPv4 dual-stack present. "
+ "Using only IPv4." );
+ net_Close( fd6 );
+ }
+ else
+ sock.i_handle = fd6;
+ }
+ }
+
+ if( sock.i_handle == -1 )
+ msg_Dbg( p_this, "net: connection to '[%s]:%d@[%s]:%d' failed",
+ psz_server, i_server, psz_bind, i_bind );
return sock.i_handle;
}
#endif
}
+void net_ListenClose( int *pi_fd )
+{
+ if( pi_fd != NULL )
+ {
+ int *pi;
+
+ for( pi = pi_fd; *pi != -1; pi++ )
+ net_Close( *pi );
+ free( pi_fd );
+ }
+}
+
/*****************************************************************************
* __net_Read:
*****************************************************************************
* Read from a network socket
- * If b_rety is true, then we repeat until we have read the right amount of
+ * If b_retry is true, then we repeat until we have read the right amount of
* data
*****************************************************************************/
int __net_Read( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
if( i_ret < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
- msg_Err( p_this, "network select error" );
+ msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
#else
msg_Err( p_this, "network select error (%s)", strerror(errno) );
#endif
: recv( fd, p_data, i_data, 0 ) ) < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
+ if( WSAGetLastError() == WSAEWOULDBLOCK )
+ {
+ /* only happens with p_vs (SSL) - not really an error */
+ }
+ else
/* For udp only */
/* On win32 recv() will fail if the datagram doesn't fit inside
* the passed buffer, even though the buffer will be filled with
"Increase the mtu size (--mtu option)" );
i_total += i_data;
}
- else
- msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
+ else if( WSAGetLastError() == WSAEINTR ) continue;
+ else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
#else
- msg_Err( p_this, "recv failed (%s)", strerror(errno) );
+ /* EAGAIN only happens with p_vs (TLS) and it's not an error */
+ if( errno != EAGAIN )
+ msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
return i_total > 0 ? i_total : -1;
}
else if( i_ret < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
- msg_Err( p_this, "network select error" );
+ msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
#else
msg_Err( p_this, "network select error (%s)", strerror(errno) );
#endif
#endif
if( ( i_recv = (p_vs != NULL)
? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
- : recv( fd, p_data, i_data, 0 ) ) <= 0 )
+ : recv( fd, p_data, i_data, 0 ) ) < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
/* For udp only */
msg_Err( p_this, "recv() failed. "
"Increase the mtu size (--mtu option)" );
}
- else
- msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
+ else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
#else
msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
: recv( pi_fd[i], p_data, i_data, 0 );
- if( i_recv <= 0 )
+ if( i_recv < 0 )
{
#ifdef WIN32
/* For udp only */
msg_Err( p_this, "recv() failed. "
"Increase the mtu size (--mtu option)" );
}
- else
- msg_Err( p_this, "recv failed (%i)",
- WSAGetLastError() );
+ else msg_Err( p_this, "recv failed (%i)",
+ WSAGetLastError() );
#else
- msg_Err( p_this, "recv failed (%s)", strerror(errno) );
+ msg_Err( p_this, "recv failed (%s)", strerror(errno) );
#endif
return VLC_EGENERIC;
}
/* Write exact amount requested */
int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
- uint8_t *p_data, int i_data )
+ const uint8_t *p_data, int i_data )
{
struct timeval timeout;
fd_set fds_w, fds_e;
if( i_ret < 0 )
{
#if defined(WIN32) || defined(UNDER_CE)
- msg_Err( p_this, "network select error" );
+ msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
#else
msg_Err( p_this, "network select error (%s)", strerror(errno) );
#endif
char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
{
- char *psz_line = malloc( 1024 );
- int i_line = 0;
- int i_max = 1024;
+ char *psz_line = NULL, *ptr = NULL;
+ size_t i_line = 0, i_max = 0;
for( ;; )
{
- if( net_Read( p_this, fd, p_vs, &psz_line[i_line], 1, VLC_TRUE ) != 1 )
+ if( i_line == i_max )
{
- psz_line[i_line] = '\0';
- break;
+ i_max += 1024;
+ psz_line = realloc( psz_line, i_max );
+ ptr = psz_line + i_line;
}
- i_line++;
- if( psz_line[i_line-1] == '\n' )
+ if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
{
- psz_line[i_line] = '\0';
+ if( i_line == 0 )
+ {
+ free( psz_line );
+ return NULL;
+ }
break;
}
- if( i_line >= i_max - 1 )
- {
- i_max += 1024;
- psz_line = realloc( psz_line, i_max );
- }
- }
+ if ( *ptr == '\n' )
+ break;
- if( i_line <= 0 )
- {
- free( psz_line );
- return NULL;
+ i_line++;
+ ptr++;
}
- while( i_line >= 1 &&
- ( psz_line[i_line-1] == '\n' || psz_line[i_line-1] == '\r' ) )
- {
- i_line--;
- psz_line[i_line] = '\0';
- }
+ *ptr-- = '\0';
+
+ if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
+ *ptr = '\0';
+
return psz_line;
}
char *psz;
int i_size, i_ret;
- vasprintf( &psz, psz_fmt, args );
- i_size = strlen( psz );
- i_ret = __net_Write( p_this, fd, p_vs, psz, i_size ) < i_size ? -1 : i_size;
+ i_size = vasprintf( &psz, psz_fmt, args );
+ i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
+ ? -1 : i_size;
free( psz );
return i_ret;
buffer[2] = 0x00; /* - No auth required */
i_len = 3;
}
-
+
if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
return VLC_EGENERIC;
if( net_Read( p_obj, fd, NULL, buffer, 2, VLC_TRUE ) != 2 )
if( i_socks_version == 4 )
{
- uint32_t addr;
+ struct addrinfo hints = { 0 }, *p_res;
/* v4 only support ipv4 */
- if( net_ConvertIPv4( &addr, psz_host ) )
+ hints.ai_family = AF_INET;
+ if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) )
return VLC_EGENERIC;
buffer[0] = i_socks_version;
buffer[1] = 0x01; /* CONNECT */
SetWBE( &buffer[2], i_port ); /* Port */
- memcpy( &buffer[4], &addr, 4 ); /* Addresse */
+ memcpy( &buffer[4], /* Address */
+ &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 );
+ vlc_freeaddrinfo( p_res );
+
buffer[8] = 0; /* Empty user id */
if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
return VLC_SUCCESS;
}
+/*****************************************************************************
+ * inet_pton replacement for obsolete and/or crap operating systems
+ *****************************************************************************/
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char *src, void *dst)
+{
+# ifdef WIN32
+ /* As we already know, Microsoft always go its own way, so even if they do
+ * provide IPv6, they don't provide the API. */
+ struct sockaddr_storage addr;
+ int len = sizeof( addr );
+
+ /* Damn it, they didn't even put LPCSTR for the firs parameter!!! */
+#ifdef UNICODE
+ wchar_t *workaround_for_ill_designed_api =
+ malloc( MAX_PATH * sizeof(wchar_t) );
+ mbstowcs( workaround_for_ill_designed_api, src, MAX_PATH );
+ workaround_for_ill_designed_api[MAX_PATH-1] = 0;
+#else
+ char *workaround_for_ill_designed_api = strdup( src );
+#endif
+ if( !WSAStringToAddress( workaround_for_ill_designed_api, af, NULL,
+ (LPSOCKADDR)&addr, &len ) )
+ {
+ free( workaround_for_ill_designed_api );
+ return -1;
+ }
+ free( workaround_for_ill_designed_api );
+
+ switch( af )
+ {
+ case AF_INET6:
+ memcpy( dst, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16 );
+ break;
+
+ case AF_INET:
+ memcpy( dst, &((struct sockaddr_in *)&addr)->sin_addr, 4 );
+ break;
+
+ default:
+ WSASetLastError( WSAEAFNOSUPPORT );
+ return -1;
+ }
+# else
+ /* Assume IPv6 is not supported. */
+ /* Would be safer and more simpler to use inet_aton() but it is most
+ * likely not provided either. */
+ uint32_t ipv4;
+
+ if( af != AF_INET )
+ {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ ipv4 = inet_addr( src );
+ if( ipv4 == INADDR_NONE )
+ return -1;
+
+ memcpy( dst, &ipv4, 4 );
+# endif /* WIN32 */
+ return 0;
+}
+#endif /* HAVE_INET_PTON */