You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
11 KiB
341 lines
11 KiB
/*****************************************************************************
|
|
* ipv6.c: IPv6 network abstraction layer
|
|
*****************************************************************************
|
|
* Copyright (C) 2002 VideoLAN
|
|
* $Id: ipv6.c,v 1.5 2002/04/19 13:56:11 sam Exp $
|
|
*
|
|
* Authors: Alexis Guillard <alexis.guillard@bt.com>
|
|
* Christophe Massiot <massiot@via.ecp.fr>
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Preamble
|
|
*****************************************************************************/
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <videolan/vlc.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#elif defined( _MSC_VER ) && defined( _WIN32 )
|
|
# include <io.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
# include <winsock2.h>
|
|
# include <ws2tcpip.h>
|
|
#elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
|
|
# include <netdb.h> /* hostent ... */
|
|
# include <sys/socket.h>
|
|
# include <netinet/in.h>
|
|
# ifdef HAVE_ARPA_INET_H
|
|
# include <arpa/inet.h> /* inet_ntoa(), inet_aton() */
|
|
# endif
|
|
#endif
|
|
|
|
#include "network.h"
|
|
|
|
/* Default MTU used for UDP socket. FIXME: we should issue some ioctl()
|
|
* call to get that value from the interface driver. */
|
|
#define DEFAULT_MTU 1500
|
|
|
|
/*****************************************************************************
|
|
* Local prototypes
|
|
*****************************************************************************/
|
|
static void getfunctions( function_list_t * );
|
|
static int NetworkOpen( struct network_socket_s * );
|
|
|
|
/*****************************************************************************
|
|
* Build configuration tree.
|
|
*****************************************************************************/
|
|
MODULE_CONFIG_START
|
|
MODULE_CONFIG_STOP
|
|
|
|
MODULE_INIT_START
|
|
SET_DESCRIPTION( _("IPv6 network abstraction layer") )
|
|
ADD_CAPABILITY( NETWORK, 40 )
|
|
ADD_SHORTCUT( "ipv6" )
|
|
MODULE_INIT_STOP
|
|
|
|
MODULE_ACTIVATE_START
|
|
getfunctions( &p_module->p_functions->network );
|
|
MODULE_ACTIVATE_STOP
|
|
|
|
MODULE_DEACTIVATE_START
|
|
MODULE_DEACTIVATE_STOP
|
|
|
|
/*****************************************************************************
|
|
* Functions exported as capabilities. They are declared as static so that
|
|
* we don't pollute the namespace too much.
|
|
*****************************************************************************/
|
|
static void getfunctions( function_list_t * p_function_list )
|
|
{
|
|
#define f p_function_list->functions.network
|
|
f.pf_open = NetworkOpen;
|
|
#undef f
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* BuildAddr: utility function to build a struct sockaddr_in6
|
|
*****************************************************************************/
|
|
static int BuildAddr( struct sockaddr_in6 * p_socket,
|
|
char * psz_address, int i_port )
|
|
{
|
|
/* Reset struct */
|
|
memset( p_socket, 0, sizeof( struct sockaddr_in6 ) );
|
|
p_socket->sin6_family = AF_INET6; /* family */
|
|
p_socket->sin6_port = htons( i_port );
|
|
if( !*psz_address )
|
|
{
|
|
p_socket->sin6_addr = in6addr_any;
|
|
}
|
|
else if( psz_address[0] == '['
|
|
&& psz_address[strlen(psz_address) - 1] == ']' )
|
|
{
|
|
psz_address++;
|
|
psz_address[strlen(psz_address) - 1] = '\0' ;
|
|
inet_pton(AF_INET6, psz_address, &p_socket->sin6_addr.s6_addr);
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_GETHOSTBYNAME2
|
|
struct hostent * p_hostent;
|
|
|
|
/* We have a fqdn, try to find its address */
|
|
if ( (p_hostent = gethostbyname2( psz_address, AF_INET6 )) == NULL )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: unknown host %s", psz_address );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Copy the first address of the host in the socket address */
|
|
memcpy( &p_socket->sin6_addr, p_hostent->h_addr_list[0],
|
|
p_hostent->h_length );
|
|
#else
|
|
intf_ErrMsg( "ipv6 error: IPv6 address %s is invalid", psz_address );
|
|
return( -1 );
|
|
#endif
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* OpenUDP: open a UDP socket
|
|
*****************************************************************************
|
|
* psz_bind_addr, i_bind_port : address and port used for the bind()
|
|
* system call. If psz_bind_addr == NULL, the socket is bound to
|
|
* in6addr_any and broadcast reception is enabled. If i_bind_port == 0,
|
|
* 1234 is used. If psz_bind_addr is a multicast (class D) address,
|
|
* join the multicast group.
|
|
* psz_server_addr, i_server_port : address and port used for the connect()
|
|
* system call. It can avoid receiving packets from unauthorized IPs.
|
|
* Its use leads to great confusion and is currently discouraged.
|
|
* This function returns -1 in case of error.
|
|
*****************************************************************************/
|
|
static int OpenUDP( network_socket_t * p_socket )
|
|
{
|
|
char * psz_bind_addr = p_socket->psz_bind_addr;
|
|
int i_bind_port = p_socket->i_bind_port;
|
|
char * psz_server_addr = p_socket->psz_server_addr;
|
|
int i_server_port = p_socket->i_server_port;
|
|
|
|
int i_handle, i_opt, i_opt_size;
|
|
struct sockaddr_in6 sock;
|
|
|
|
if( i_bind_port == 0 )
|
|
{
|
|
i_bind_port = config_GetIntVariable( "server_port" );
|
|
}
|
|
|
|
/* Open a SOCK_DGRAM (UDP) socket, in the AF_INET6 domain, automatic (0)
|
|
* protocol */
|
|
if( (i_handle = socket( AF_INET6, SOCK_DGRAM, 0 )) == -1 )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot create socket (%s)", strerror(errno) );
|
|
return( -1 );
|
|
}
|
|
|
|
/* We may want to reuse an already used socket */
|
|
i_opt = 1;
|
|
if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR,
|
|
(void *) &i_opt, sizeof( i_opt ) ) == -1 )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot configure socket (SO_REUSEADDR: %s)",
|
|
strerror(errno));
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
|
|
* packet loss caused by scheduling problems */
|
|
i_opt = 0x80000;
|
|
if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
|
|
(void *) &i_opt, sizeof( i_opt ) ) == -1 )
|
|
{
|
|
intf_WarnMsg( 1,
|
|
"ipv6 warning: cannot configure socket (SO_RCVBUF: %s)",
|
|
strerror(errno));
|
|
}
|
|
|
|
/* Check if we really got what we have asked for, because Linux, etc.
|
|
* will silently limit the max buffer size to net.core.rmem_max which
|
|
* is typically only 65535 bytes */
|
|
i_opt = 0;
|
|
i_opt_size = sizeof( i_opt );
|
|
if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF,
|
|
(void*) &i_opt, &i_opt_size ) == -1 )
|
|
{
|
|
intf_WarnMsg( 1, "ipv6 warning: cannot query socket (SO_RCVBUF: %s)",
|
|
strerror(errno));
|
|
}
|
|
else if( i_opt < 0x80000 )
|
|
{
|
|
intf_WarnMsg( 1, "ipv6 warning: socket buffer size is 0x%x"
|
|
" instead of 0x%x", i_opt, 0x80000 );
|
|
}
|
|
|
|
/* Build the local socket */
|
|
if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 )
|
|
{
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Bind it */
|
|
if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot bind socket (%s)", strerror(errno) );
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Allow broadcast reception if we bound on in6addr_any */
|
|
if( !*psz_bind_addr )
|
|
{
|
|
i_opt = 1;
|
|
if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST,
|
|
(void*) &i_opt, sizeof( i_opt ) ) == -1 )
|
|
{
|
|
intf_WarnMsg( 1,
|
|
"ipv6 warning: cannot configure socket (SO_BROADCAST: %s)",
|
|
strerror(errno));
|
|
}
|
|
}
|
|
|
|
/* Join the multicast group if the socket is a multicast address */
|
|
/* FIXME: To be written */
|
|
|
|
if( *psz_server_addr )
|
|
{
|
|
/* Build socket for remote connection */
|
|
if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot build remote address" );
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Connect the socket */
|
|
if( connect( i_handle, (struct sockaddr *) &sock,
|
|
sizeof( sock ) ) == (-1) )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot connect socket (%s)",
|
|
strerror(errno) );
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
}
|
|
|
|
p_socket->i_handle = i_handle;
|
|
p_socket->i_mtu = DEFAULT_MTU;
|
|
return( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* 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( 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;
|
|
}
|
|
|
|
/* Open a SOCK_STREAM (TCP) socket, in the AF_INET6 domain, automatic (0)
|
|
* protocol */
|
|
if( (i_handle = socket( AF_INET6, SOCK_STREAM, 0 )) == -1 )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot create socket (%s)", strerror(errno) );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Build remote address */
|
|
if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 )
|
|
{
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Connect the socket */
|
|
if( connect( i_handle, (struct sockaddr *) &sock,
|
|
sizeof( sock ) ) == (-1) )
|
|
{
|
|
intf_ErrMsg( "ipv6 error: cannot connect socket (%s)",
|
|
strerror(errno) );
|
|
close( i_handle );
|
|
return( -1 );
|
|
}
|
|
|
|
p_socket->i_handle = i_handle;
|
|
p_socket->i_mtu = 0; /* There is no MTU notion in TCP */
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NetworkOpen: wrapper around OpenUDP and OpenTCP
|
|
*****************************************************************************/
|
|
static int NetworkOpen( network_socket_t * p_socket )
|
|
{
|
|
if( p_socket->i_type == NETWORK_UDP )
|
|
{
|
|
return OpenUDP( p_socket );
|
|
}
|
|
else
|
|
{
|
|
return OpenTCP( p_socket );
|
|
}
|
|
}
|
|
|
|
|
|
|