*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
- * Rémi Denis-Courmont <courmisch # via.ecp.fr>
+ * 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
*****************************************************************************/
struct network_socket_t
{
- unsigned int i_type;
-
char * psz_bind_addr;
int i_bind_port;
size_t i_mtu;
};
-/* Socket types */
-#define NETWORK_UDP 1
-#define NETWORK_TCP 2
-#define NETWORK_TCP_PASSIVE 3
-
-
typedef struct
{
char *psz_protocol;
return ret;
}
-VLC_EXPORT( int, net_ConvertIPv4, ( uint32_t *p_addr, const char * psz_address ) );
-
/* Portable networking layer communication */
#define net_OpenTCP(a, b, c) __net_OpenTCP(VLC_OBJECT(a), b, c)
VLC_EXPORT( int, __net_OpenTCP, ( vlc_object_t *p_this, const char *psz_host, int i_port ) );
#define net_ListenTCP(a, b, c) __net_ListenTCP(VLC_OBJECT(a), b, c)
-VLC_EXPORT( int, __net_ListenTCP, ( vlc_object_t *p_this, char *psz_localaddr, int i_port ) );
+VLC_EXPORT( int *, __net_ListenTCP, ( vlc_object_t *, const char *, int ) );
#define net_Accept(a, b, c) __net_Accept(VLC_OBJECT(a), b, c)
-VLC_EXPORT( int, __net_Accept, ( vlc_object_t *p_this, int fd_listen, mtime_t i_wait ) );
+VLC_EXPORT( int, __net_Accept, ( vlc_object_t *, int *, mtime_t ) );
#define net_OpenUDP(a, b, c, d, e ) __net_OpenUDP(VLC_OBJECT(a), b, c, d, e)
VLC_EXPORT( int, __net_OpenUDP, ( vlc_object_t *p_this, char *psz_bind, int i_bind, char *psz_server, int i_server ) );
VLC_EXPORT( void, net_Close, ( int fd ) );
+VLC_EXPORT( void, net_ListenClose, ( int *fd ) );
/* Functions to read from or write to the networking layer */
#define net_vaPrintf(a,b,c,d,e) __net_vaPrintf(VLC_OBJECT(a),b,c,d,e)
VLC_EXPORT( int, __net_vaPrintf, ( vlc_object_t *p_this, int fd, v_socket_t *, const char *psz_fmt, va_list args ) );
+/* Portable network names/addresses resolution layer */
+
+/* GAI error codes */
+# ifndef EAI_BADFLAGS
+# define EAI_BADFLAGS -1
+# endif
+# ifndef EAI_NONAME
+# define EAI_NONAME -2
+# endif
+# ifndef EAI_AGAIN
+# define EAI_AGAIN -3
+# endif
+# ifndef EAI_FAIL
+# define EAI_FAIL -4
+# endif
+# ifndef EAI_NODATA
+# define EAI_NODATA -5
+# endif
+# ifndef EAI_FAMILY
+# define EAI_FAMILY -6
+# endif
+# ifndef EAI_SOCKTYPE
+# define EAI_SOCKTYPE -7
+# endif
+# ifndef EAI_SERVICE
+# define EAI_SERVICE -8
+# endif
+# ifndef EAI_ADDRFAMILY
+# define EAI_ADDRFAMILY -9
+# endif
+# ifndef EAI_MEMORY
+# define EAI_MEMORY -10
+# endif
+# ifndef EAI_SYSTEM
+# define EAI_SYSTEM -11
+# endif
+
+
+# ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+# define NI_MAXSERV 32
+# endif
+
+# ifndef NI_NUMERICHOST
+# define NI_NUMERICHOST 0x01
+# define NI_NUMERICSERV 0x02
+# define NI_NOFQDN 0x04
+# define NI_NAMEREQD 0x08
+# define NI_DGRAM 0x10
+# endif
+
+# ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+};
+# define AI_PASSIVE 1
+# define AI_CANONNAME 2
+# define AI_NUMERICHOST 4
+# endif /* if !HAVE_STRUCT_ADDRINFO */
+
+/*** libidn support ***/
+# ifndef AI_IDN
+# define AI_IDN 0
+# define AI_CANONIDN 0
+# endif
+
+VLC_EXPORT( const char *, vlc_gai_strerror, ( int ) );
+VLC_EXPORT( int, vlc_getnameinfo, ( vlc_object_t *, const struct sockaddr *, int, char *, int, char *, int, int ) );
+VLC_EXPORT( int, vlc_getaddrinfo, ( vlc_object_t *, const char *, const char *, const struct addrinfo *, struct addrinfo ** ) );
+VLC_EXPORT( void, vlc_freeaddrinfo, ( struct addrinfo * ) );
+
#endif
struct intf_sys_t
{
- int i_socket_listen;
+ int *pi_socket_listen;
int i_socket;
char *psz_unix_path;
intf_thread_t *p_intf = (intf_thread_t*)p_this;
playlist_t *p_playlist;
char *psz_host, *psz_unix_path;
- int i_socket = -1;
+ int *pi_socket = NULL;
#if defined(HAVE_ISATTY) && !defined(WIN32)
/* Check that stdin is a TTY */
psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
if( psz_unix_path )
{
+ int i_socket;
+
#ifndef PF_LOCAL
msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
free( psz_unix_path );
net_Close( i_socket );
return VLC_EGENERIC;
}
+
+ /* FIXME: we need a core function to merge listening sockets sets */
+ pi_socket = calloc( 2, sizeof( int ) );
+ if( pi_socket == NULL )
+ {
+ free( psz_unix_path );
+ net_Close( i_socket );
+ return VLC_ENOMEM;
+ }
+ pi_socket[0] = i_socket;
+ pi_socket[1] = -1;
#endif
}
- if( ( i_socket == -1) &&
+ if( ( pi_socket == NULL ) &&
( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
{
vlc_url_t url;
msg_Dbg( p_intf, "base %s port %d", url.psz_host, url.i_port );
- if( (i_socket = net_ListenTCP(p_this, url.psz_host, url.i_port)) == -1)
+ pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
+ if( pi_socket == NULL )
{
msg_Warn( p_intf, "can't listen to %s port %i",
url.psz_host, url.i_port );
return VLC_ENOMEM;
}
- p_intf->p_sys->i_socket_listen = i_socket;
+ p_intf->p_sys->pi_socket_listen = pi_socket;
p_intf->p_sys->i_socket = -1;
p_intf->p_sys->psz_unix_path = psz_unix_path;
{
intf_thread_t *p_intf = (intf_thread_t*)p_this;
- if( p_intf->p_sys->i_socket_listen != -1 )
- net_Close( p_intf->p_sys->i_socket_listen );
+ net_ListenClose( p_intf->p_sys->pi_socket_listen );
if( p_intf->p_sys->i_socket != -1 )
net_Close( p_intf->p_sys->i_socket );
if( p_intf->p_sys->psz_unix_path != NULL )
char *psz_cmd, *psz_arg;
vlc_bool_t b_complete;
- if( p_intf->p_sys->i_socket_listen != - 1 &&
+ if( p_intf->p_sys->pi_socket_listen != NULL &&
p_intf->p_sys->i_socket == -1 )
{
p_intf->p_sys->i_socket =
- net_Accept( p_intf, p_intf->p_sys->i_socket_listen, 0 );
+ net_Accept( p_intf, p_intf->p_sys->pi_socket_listen, 0 );
}
b_complete = ReadCommand( p_intf, p_buffer, &i_size );
/*****************************************************************************
* telnet.c: VLM interface plugin
*****************************************************************************
- * Copyright (C) 2000, 2001 VideoLAN
+ * Copyright (C) 2000-2005 VideoLAN
* $Id$
*
* Authors: Simon Latapie <garf@videolan.org>
{
telnet_client_t **clients;
int i_clients;
- int fd;
+ int *pi_fd;
vlm_t *mediatheque;
};
i_telnetport = config_GetInt( p_intf, "telnet-port" );
p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
- if( ( p_intf->p_sys->fd = net_ListenTCP( p_intf , "", i_telnetport ) ) < 0 )
+ if( ( p_intf->p_sys->pi_fd = net_ListenTCP( p_intf , "", i_telnetport ) )
+ == NULL )
{
msg_Err( p_intf, "cannot listen for telnet" );
free( p_intf->p_sys );
}
if( p_sys->clients != NULL ) free( p_sys->clients );
- net_Close( p_sys->fd );
+ net_ListenClose( p_sys->pi_fd );
vlm_Delete( p_sys->mediatheque );
int i_ret, i_len, fd, i;
/* if a new client wants to communicate */
- fd = net_Accept( p_intf, p_sys->fd, p_sys->i_clients > 0 ? 0 : -1 );
+ fd = net_Accept( p_intf, p_sys->pi_fd, p_sys->i_clients > 0 ? 0 : -1 );
if( fd > 0 )
{
telnet_client_t *cl;
/*****************************************************************************
* ipv4.c: IPv4 network abstraction layer
*****************************************************************************
- * Copyright (C) 2001, 2002 VideoLAN
+ * Copyright (C) 2001-2005 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
-#define TIMEOUT_TEXT N_("TCP connection timeout in ms")
-#define TIMEOUT_LONGTEXT N_( \
- "Allows you to modify the default TCP connection timeout. This " \
- "value should be set in millisecond units." )
-
#define MIFACE_TEXT N_("Multicast output interface")
#define MIFACE_LONGTEXT N_( \
"Indicate here the multicast output interface. " \
set_description( _("IPv4 network abstraction layer") );
set_capability( "network", 50 );
set_callbacks( NetOpen, NULL );
-
- add_integer( "ipv4-timeout", 5 * 1000, NULL, TIMEOUT_TEXT,
- TIMEOUT_LONGTEXT, VLC_TRUE );
add_string( "miface-addr", NULL, NULL, MIFACE_TEXT, MIFACE_LONGTEXT, VLC_TRUE );
vlc_module_end();
return( 0 );
}
-/*****************************************************************************
- * SocketTCP: create a TCP socket
- *****************************************************************************
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int SocketTCP( vlc_object_t * p_this )
-{
- int i_handle;
-
- /* Open a SOCK_STREAM (TCP) socket, in the PF_INET domain, automatic (0)
- * protocol */
- if( (i_handle = socket( PF_INET, SOCK_STREAM, 0 )) == -1 )
- {
-#if defined(WIN32) || defined(UNDER_CE)
- msg_Warn( p_this, "cannot create socket (%i)", WSAGetLastError() );
-#else
- msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
-#endif
- return -1;
- }
-
- /* Set to non-blocking */
-#if defined( WIN32 ) || defined( UNDER_CE )
- {
- unsigned long i_dummy = 1;
- if( ioctlsocket( i_handle, FIONBIO, &i_dummy ) != 0 )
- {
- msg_Err( p_this, "cannot set socket to non-blocking mode" );
- }
- }
-#else
- {
- int i_flags;
- if( ( i_flags = fcntl( i_handle, F_GETFL, 0 ) ) < 0 ||
- fcntl( i_handle, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
- {
- msg_Err( p_this, "cannot set socket to non-blocking mode" );
- }
- }
-#endif
-
- return i_handle;
-}
-
-/*****************************************************************************
- * OpenTCP: open a TCP socket
- *****************************************************************************
- * psz_server_addr, i_server_port : address and port used for the connect()
- * system call. If i_server_port == 0, 80 is used.
- * Other parameters are ignored.
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
-{
- char * psz_server_addr = p_socket->psz_server_addr;
- int i_server_port = p_socket->i_server_port;
-
- int i_handle;
- struct sockaddr_in sock;
-
- if( i_server_port == 0 )
- {
- i_server_port = 80;
- }
-
- if( (i_handle = SocketTCP( p_this )) == -1 )
- return VLC_EGENERIC;
-
- /* Build remote address */
- if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
- {
- msg_Dbg( p_this, "could not build local address" );
- goto error;
- }
-
- /* Connect the socket */
- if( connect( i_handle, (struct sockaddr *) &sock, sizeof( sock ) ) == -1 )
- {
-#if defined( WIN32 ) || defined( UNDER_CE )
- if( WSAGetLastError() == WSAEWOULDBLOCK )
-#else
- if( errno == EINPROGRESS )
-#endif
- {
- int i_ret, i_opt, i_opt_size = sizeof( i_opt ), i_max_count;
- struct timeval timeout;
- vlc_value_t val;
- fd_set fds;
-
- if( var_Get( p_this, "ipv4-timeout", &val ) != VLC_SUCCESS )
- {
- var_Create( p_this, "ipv4-timeout",
- VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv4-timeout", &val );
- }
- i_max_count = val.i_int * 1000 / 100000 /* timeout.tv_usec */;
-
- msg_Dbg( p_this, "connection in progress" );
- do
- {
- if( p_this->b_die || i_max_count <= 0 )
- {
- msg_Dbg( p_this, "connection aborted" );
- goto error;
- }
-
- i_max_count--;
-
- /* Initialize file descriptor set */
- FD_ZERO( &fds );
- FD_SET( i_handle, &fds );
-
- /* We'll wait 0.1 second if nothing happens */
- timeout.tv_sec = 0;
- timeout.tv_usec = 100000;
-
- } while( ( i_ret = select( i_handle + 1, NULL, &fds, NULL,
- &timeout ) ) == 0 ||
-#if defined( WIN32 ) || defined( UNDER_CE )
- ( i_ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK ) );
-#else
- ( i_ret < 0 && errno == EINTR ) );
-#endif
-
- if( i_ret < 0 )
- {
- msg_Warn( p_this, "cannot connect socket (select failed)" );
- goto error;
- }
-
-#if !defined( SYS_BEOS ) && !defined( UNDER_CE )
- if( getsockopt( i_handle, SOL_SOCKET, SO_ERROR, (void*)&i_opt,
- &i_opt_size ) == -1 || i_opt != 0 )
- {
- msg_Warn( p_this, "cannot connect socket (SO_ERROR)" );
- goto error;
- }
-#endif
- }
- else
- {
-#if defined(WIN32) || defined(UNDER_CE)
- msg_Warn( p_this, "cannot connect socket (%i)", WSAGetLastError());
-#else
- msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
-#endif
- goto error;
- }
- }
-
- p_socket->i_handle = i_handle;
- p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
- return VLC_SUCCESS;
-
-error:
- close( i_handle );
- return VLC_EGENERIC;
-}
-
-/*****************************************************************************
- * ListenTCP: open a TCP passive socket (server-side)
- *****************************************************************************
- * psz_server_addr, i_server_port : address and port used for the bind()
- * system call. If i_server_port == 0, 80 is used.
- * Other parameters are ignored.
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int ListenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
-{
- char * psz_server_addr = p_socket->psz_server_addr;
- int i_server_port = p_socket->i_server_port;
-
- int i_handle, i_dummy = 1;
- struct sockaddr_in sock;
-
- if( (i_handle = SocketTCP( p_this )) == -1 )
- return VLC_EGENERIC;
-
- if ( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
- (void *)&i_dummy, sizeof( i_dummy ) ) == -1 )
- {
- msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" );
- }
-
- /* Build remote address */
- if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
- {
- msg_Dbg( p_this, "could not build local address" );
- return VLC_EGENERIC;
- }
-
- /* Bind the socket */
- if( bind( i_handle, (struct sockaddr *) &sock, sizeof( sock )) == -1 )
- {
-#if defined(WIN32) || defined(UNDER_CE)
- msg_Err( p_this, "cannot bind socket (%i)", WSAGetLastError());
-#else
- msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
-#endif
- goto error;
- }
-
- /* Listen */
- if( listen( i_handle, 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
- goto error;
- }
-
- p_socket->i_handle = i_handle;
- p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
- return VLC_SUCCESS;
-
-error:
- close( i_handle );
- return VLC_EGENERIC;
-}
-
/*****************************************************************************
* NetOpen: wrapper around OpenUDP, ListenTCP and OpenTCP
*****************************************************************************/
{
network_socket_t * p_socket = p_this->p_private;
- if( p_socket->i_type == NETWORK_UDP )
- {
- return OpenUDP( p_this, p_socket );
- }
- else if( p_socket->i_type == NETWORK_TCP_PASSIVE )
- {
- return ListenTCP( p_this, p_socket );
- }
- else
- {
- return OpenTCP( p_this, p_socket );
- }
+ return OpenUDP( p_this, p_socket );
}
/*****************************************************************************
* ipv6.c: IPv6 network abstraction layer
*****************************************************************************
- * Copyright (C) 2002 VideoLAN
+ * Copyright (C) 2002-2005 VideoLAN
* $Id$
*
* Authors: Alexis Guillard <alexis.guillard@bt.com>
return( -1 );
}
+#ifdef WIN32
+# ifdef IPV6_PROTECTION_LEVEL
+ if( ptr->ai_family == PF_INET6 )
+ {
+ i_val = PROTECTION_LEVEL_UNRESTRICTED;
+ setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
+ sizeof( i_val ) );
+ }
+# else
+# warning You are using outdated headers for Winsock !
+# endif
+#endif
+
/* We may want to reuse an already used socket */
i_opt = 1;
if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
(void *)&ttl, sizeof( ttl ) ) < 0 )
{
-#ifdef HAVE_ERRNO_H
msg_Err( p_this, "failed to set multicast ttl (%s)",
strerror(errno) );
-#else
- msg_Err( p_this, "failed to set multicast ttl" );
-#endif
}
}
else
if( setsockopt( i_handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
(void *)&ttl, sizeof( ttl ) ) < 0 )
{
-#ifdef HAVE_ERRNO_H
msg_Err( p_this, "failed to set unicast ttl (%s)",
strerror(errno) );
-#else
- msg_Err( p_this, "failed to set unicast ttl" );
-#endif
}
}
}
return( 0 );
}
-/*****************************************************************************
- * SocketTCP: create a TCP socket
- *****************************************************************************
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int SocketTCP( vlc_object_t * p_this )
-{
- int i_handle;
-
- /* Open a SOCK_STREAM (TCP) socket, in the PF_INET6 domain, automatic (0)
- * protocol */
- if( (i_handle = socket( PF_INET6, SOCK_STREAM, 0 )) == -1 )
- {
-#ifdef HAVE_ERRNO_H
- msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) );
-#else
- msg_Warn( p_this, "cannot create socket" );
-#endif
- return -1;
- }
-
- /* Set to non-blocking */
-#if defined( WIN32 ) || defined( UNDER_CE )
- {
- unsigned long i_dummy = 1;
- if( ioctlsocket( i_handle, FIONBIO, &i_dummy ) != 0 )
- {
- msg_Err( p_this, "cannot set socket to non-blocking mode" );
- }
- }
-#elif defined( HAVE_ERRNO_H )
- {
- int i_flags;
- if( ( i_flags = fcntl( i_handle, F_GETFL, 0 ) ) < 0 ||
- fcntl( i_handle, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
- {
- msg_Err( p_this, "cannot set socket to non-blocking mode" );
- }
- }
-#endif
-
- return i_handle;
-}
-
-/*****************************************************************************
- * OpenTCP: open a TCP socket
- *****************************************************************************
- * psz_server_addr, i_server_port : address and port used for the connect()
- * system call. If i_server_port == 0, 80 is used.
- * Other parameters are ignored.
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
-{
- char * psz_server_addr = p_socket->psz_server_addr;
- int i_server_port = p_socket->i_server_port;
-
- int i_handle;
- struct sockaddr_in6 sock;
-
- if( i_server_port == 0 )
- {
- i_server_port = 80;
- }
-
- if( (i_handle = SocketTCP( p_this )) == -1 )
- return VLC_EGENERIC;
-
- /* Build remote address */
- if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
- goto error;
-
- /* Connect the socket */
- if( connect( i_handle, (struct sockaddr *) &sock, sizeof( sock ) ) == -1 )
- {
-#if defined( WIN32 ) || defined( UNDER_CE )
- if( WSAGetLastError() == WSAEWOULDBLOCK )
-#elif defined( HAVE_ERRNO_H )
- if( errno == EINPROGRESS )
-#else
- if( 0 )
-#endif
- {
- int i_ret, i_opt, i_opt_size = sizeof( i_opt ), i_max_count;
- struct timeval timeout;
- vlc_value_t val;
- fd_set fds;
-
- /* FIXME: There is no ipv6-timeout option, so we use ipv4-timeout
- * instead */
- if( !var_Type( p_this, "ipv4-timeout" ) )
- {
- var_Create( p_this, "ipv4-timeout",
- VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
- }
- var_Get( p_this, "ipv4-timeout", &val );
- i_max_count = val.i_int * 1000 / 100000 /* timeout.tv_usec */;
-
- msg_Dbg( p_this, "connection in progress" );
- do
- {
- if( p_this->b_die || i_max_count <= 0 )
- {
- msg_Dbg( p_this, "connection aborted" );
- goto error;
- }
-
- i_max_count--;
-
- /* Initialize file descriptor set */
- FD_ZERO( &fds );
- FD_SET( i_handle, &fds );
-
- /* We'll wait 0.1 second if nothing happens */
- timeout.tv_sec = 0;
- timeout.tv_usec = 100000;
-
- } while( ( i_ret = select( i_handle + 1, NULL, &fds, NULL,
- &timeout ) ) == 0 ||
-#if defined( WIN32 ) || defined( UNDER_CE )
- ( i_ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK ) );
-#elif defined( HAVE_ERRNO_H )
- ( i_ret < 0 && errno == EINTR ) );
-#else
- ( i_ret < 0 ) );
-#endif
-
- if( i_ret < 0 )
- {
- msg_Warn( p_this, "cannot connect socket (select failed)" );
- goto error;
- }
-
-#if !defined( SYS_BEOS )
- if( getsockopt( i_handle, SOL_SOCKET, SO_ERROR, (void*)&i_opt,
- &i_opt_size ) == -1 || i_opt != 0 )
- {
- msg_Warn( p_this, "cannot connect socket (SO_ERROR)" );
- goto error;
- }
-#endif
- }
- else
- {
-#if defined( HAVE_ERRNO_H )
- msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) );
-#else
- msg_Warn( p_this, "cannot connect socket" );
-#endif
- goto error;
- }
- }
-
- p_socket->i_handle = i_handle;
- p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
- return VLC_SUCCESS;
-
-error:
- close( i_handle );
- return VLC_EGENERIC;
-}
-
-/*****************************************************************************
- * ListenTCP: open a TCP passive socket (server-side)
- *****************************************************************************
- * psz_server_addr, i_server_port : address and port used for the bind()
- * system call. If i_server_port == 0, 80 is used.
- * Other parameters are ignored.
- * This function returns -1 in case of error.
- *****************************************************************************/
-static int ListenTCP( vlc_object_t * p_this, network_socket_t * p_socket )
-{
- char * psz_server_addr = p_socket->psz_server_addr;
- int i_server_port = p_socket->i_server_port;
-
- int i_handle, i_dummy = 1;
- struct sockaddr_in6 sock;
-
- if( (i_handle = SocketTCP( p_this )) == -1 )
- return VLC_EGENERIC;
-
- if ( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
- (void *)&i_dummy, sizeof( i_dummy ) ) == -1 )
- {
- msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" );
- }
-
- /* Build remote address */
- if ( BuildAddr( p_this, &sock, psz_server_addr, i_server_port ) == -1 )
- {
- msg_Dbg( p_this, "could not build local address" );
- return VLC_EGENERIC;
- }
-
- /* Bind the socket */
- if( bind( i_handle, (struct sockaddr *) &sock, sizeof( sock )) == -1 )
- {
-#ifdef HAVE_ERRNO_H
- msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) );
-#else
- msg_Err( p_this, "cannot bind socket" );
-#endif
- goto error;
- }
-
- /* Listen */
- if( listen( i_handle, 100 ) == -1 )
- {
-#ifdef HAVE_ERRNO_H
- msg_Err( p_this, "cannot bring the socket in listening mode (%s)",
- strerror(errno) );
-#else
- msg_Err( p_this, "cannot bring the socket in listening mode" );
-#endif
- goto error;
- }
-
- p_socket->i_handle = i_handle;
- p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
- return VLC_SUCCESS;
-
-error:
- close( i_handle );
- return VLC_EGENERIC;
-}
-
/*****************************************************************************
* NetOpen: wrapper around OpenUDP, ListenTCP and OpenTCP
*****************************************************************************/
{
network_socket_t * p_socket = p_this->p_private;
- if( p_socket->i_type == NETWORK_UDP )
- {
- return OpenUDP( p_this, p_socket );
- }
- else if( p_socket->i_type == NETWORK_TCP_PASSIVE )
- {
- return ListenTCP( p_this, p_socket );
- }
- else
- {
- return OpenTCP( p_this, p_socket );
- }
+ return OpenUDP( p_this, p_socket );
}
"If you check this box, IPv4 will be used by default for all UDP and " \
"HTTP connections.")
+#define TIMEOUT_TEXT N_("TCP connection timeout in ms")
+#define TIMEOUT_LONGTEXT N_( \
+ "Allows you to modify the default TCP connection timeout. This " \
+ "value should be set in millisecond units." )
+
#define SOCKS_SERVER_TEXT N_("SOCKS server")
#define SOCKS_SERVER_LONGTEXT N_( \
"Allow you to specify a SOCKS server to use. It must be of the form " \
change_short('6');
add_bool( "ipv4", 0, NULL, IPV4_TEXT, IPV4_LONGTEXT, VLC_FALSE );
change_short('4');
+ add_integer( "ipv4-timeout", 5 * 1000, NULL, TIMEOUT_TEXT,
+ TIMEOUT_LONGTEXT, VLC_TRUE );
set_section( N_( "Socks proxy") , NULL )
add_string( "socks", NULL, NULL,
/*****************************************************************************
* net.c:
*****************************************************************************
- * Copyright (C) 2004 VideoLAN
+ * Copyright (C) 2004-2005 VideoLAN
* $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
*****************************************************************************/
int __net_OpenTCP( vlc_object_t *p_this, const char *psz_host, int i_port )
{
- vlc_value_t val;
- void *private;
-
- char *psz_network = "";
- network_socket_t sock;
- module_t *p_network;
-
- /* 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";
- }
+ struct addrinfo hints, *res, *ptr;
+ const char *psz_realhost;
+ char *psz_realport, *psz_socks;
+ int i_val, i_handle = -1;
- var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv6", &val );
- if( val.b_bool )
- {
- psz_network = "ipv6";
- }
+ if( ( i_port < 0 ) || ( i_port > 65535 ) )
+ return -1; /* I don't expect the next TCP version shortly */
+ if( i_port == 0 )
+ i_port = 80; /* historical VLC thing */
- /* Prepare the network_socket_t structure */
- sock.i_type = NETWORK_TCP;
- sock.psz_bind_addr = "";
- sock.i_bind_port = 0;
- sock.i_ttl = 0;
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_socktype = SOCK_STREAM;
- var_Create( p_this, "socks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
- var_Get( p_this, "socks", &val );
- if( *val.psz_string && *val.psz_string != ':' )
+ psz_socks = var_CreateGetString( p_this, "socks" );
+ if( *psz_socks && *psz_socks != ':' )
{
- 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;
+ psz_realport = strdup( ( psz != NULL ) ? 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:%s' for '%s:%d'",
+ psz_realhost, psz_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;
+ psz_realport = malloc( 6 );
+ if( psz_realport == NULL )
+ {
+ free( psz_socks );
+ return -1;
+ }
+ sprintf( psz_realport, "%d", i_port );
+ msg_Dbg( p_this, "net: connecting to '%s:%s'", psz_realhost,
+ psz_realport );
+ }
- private = p_this->p_private;
- p_this->p_private = (void*)&sock;
- if( !( p_network = module_Need( p_this, "network", psz_network, 0 ) ) )
+ i_val = vlc_getaddrinfo( p_this, psz_realhost, psz_realport, &hints,
+ &res );
+ free( psz_realport );
+ if( i_val )
{
- msg_Dbg( p_this, "net: connection to '%s:%d' failed",
- psz_host, i_port );
+ msg_Err( p_this, "cannot resolve '%s' : %s", psz_realhost,
+ vlc_gai_strerror( i_val ) );
+ free( psz_socks );
return -1;
}
- module_Unneed( p_this, p_network );
- p_this->p_private = private;
- if( *val.psz_string && *val.psz_string != ':' )
+ for( ptr = res; (ptr != NULL) && (i_handle == -1); ptr = ptr->ai_next )
+ {
+ int fd;
+
+ fd = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
+ if( fd == -1 )
+ {
+#if defined(WIN32) || defined(UNDER_CE)
+ msg_Warn( p_this, "cannot create socket (%i)",
+ WSAGetLastError() );
+#else
+ msg_Warn( p_this, "cannot create socket (%s)",
+ strerror( errno ) );
+#endif
+ continue;
+ }
+
+
+ /* 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" );
+ }
+
+# ifdef IPV6_PROTECTION_LEVEL
+ if( ptr->ai_family == PF_INET6 )
+ {
+ i_val = PROTECTION_LEVEL_UNRESTRICTED;
+ setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, &i_val,
+ sizeof( i_val ) );
+ }
+# else
+# warning You are using outdated headers for Winsock !
+# endif
+#else
+ 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
+
+ i_val = 1;
+ setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
+ sizeof( i_val ) );
+
+ if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
+ {
+ int i_val_size = sizeof( i_val ), i_max_count;
+ struct timeval tv;
+ vlc_value_t timeout;
+ fd_set fds;
+
+#if defined( WIN32 ) || defined( UNDER_CE )
+ if( WSAGetLastError() != WSAEWOULDBLOCK )
+ {
+ msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
+ i_port, WSAGetLastError( ) );
+ net_Close( fd );
+ continue;
+ }
+#else
+ if( errno != EINPROGRESS )
+ {
+ msg_Warn( p_this, "connection to %s:%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 );
+ i_max_count = timeout.i_int / 100;
+
+ msg_Dbg( p_this, "connection in progress" );
+ do
+ {
+ if( p_this->b_die )
+ {
+ msg_Dbg( p_this, "connection aborted" );
+ net_Close( fd );
+ vlc_freeaddrinfo( res );
+ free( psz_socks );
+ return -1;
+ }
+ if( i_max_count <= 0 )
+ {
+ msg_Dbg( p_this, "connection timed out" );
+ net_Close( fd );
+ continue;
+ }
+
+ i_max_count--;
+
+ /* 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 = 100000;
+
+ i_val = select( fd + 1, NULL, &fds, NULL, &tv );
+ }
+ while( ( i_val == 0 ) || ( ( i_val < 0 ) &&
+#if defined( WIN32 ) || defined( UNDER_CE )
+ ( WSAGetLastError() == WSAEWOULDBLOCK )
+#else
+ ( errno == EINTR )
+#endif
+ ) );
+
+ 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 )
+ {
+#ifdef WIN32
+ msg_Warn( p_this, "connection to %s:%d failed (%d)", psz_host,
+ i_port, WSAGetLastError( ) );
+#else
+ msg_Warn( p_this, "connection to %s:%d : %s", psz_host,
+ i_port, strerror( i_val ) );
+#endif
+ net_Close( fd );
+ continue;
+ }
+#endif
+ }
+ i_handle = fd; /* success! */
+ }
+
+ vlc_freeaddrinfo( res );
+
+ 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_port;
- char *psz_network = "";
- network_socket_t sock;
- module_t *p_network;
+ if( ( i_port < 0 ) || ( i_port > 65535 ) )
+ return NULL; /* I don't expect the next TCP version shortly */
+ 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;
+ hints.ai_flags = AI_PASSIVE;
- var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_this, "ipv6", &val );
- if( val.b_bool )
+ psz_port = malloc( 6 );
+ if( psz_port == NULL )
+ return NULL;
+
+ sprintf( psz_port, "%d", i_port );
+ msg_Dbg( p_this, "net: listening to '%s:%s'", psz_host, psz_port );
+
+ i_val = vlc_getaddrinfo( p_this, psz_host, psz_port, &hints, &res );
+ free( psz_port );
+ if( i_val )
{
- psz_network = "ipv6";
+ msg_Err( p_this, "cannot resolve '%s' : %s", psz_host,
+ 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 = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
+ if( fd == -1 )
+ {
+#if defined(WIN32) || defined(UNDER_CE)
+ msg_Warn( p_this, "cannot create socket (%i)",
+ WSAGetLastError() );
+#else
+ msg_Warn( p_this, "cannot create socket (%s)",
+ strerror( errno ) );
+#endif
+ continue;
+ }
+
+ /* 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
+ 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
+
+ i_val = 1;
+ setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
+ sizeof( i_val ) );
+
+ /* 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;
+ if( pi_handles == NULL )
+ newpi[i_size - 1] = -1;
+ pi_handles = newpi;
+ }
}
- module_Unneed( p_this, p_network );
- p_this->p_private = private;
+
+ freeaddrinfo( res );
- return sock.i_handle;
+ 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;
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;
#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:
*****************************************************************************