X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fnetwork%2Fipv4.c;h=fdfaf8057912aa76e03851ce6c7b6e10fa0d3be5;hb=c1b4f4834670dbe158b6035d5b429ed2d772f17e;hp=1c72b973b81325aa01f26de2a0fc0f1db14a9a40;hpb=2aac17a64a9ea7437f9940c7e7c3c626f45c7968;p=vlc diff --git a/modules/misc/network/ipv4.c b/modules/misc/network/ipv4.c index 1c72b973b8..fdfaf80579 100644 --- a/modules/misc/network/ipv4.c +++ b/modules/misc/network/ipv4.c @@ -2,16 +2,17 @@ * ipv4.c: IPv4 network abstraction layer ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN - * $Id: ipv4.c,v 1.4 2002/10/01 22:29:08 massiot Exp $ + * $Id: ipv4.c,v 1.24 2004/02/01 14:43:08 alexis Exp $ * * Authors: Christophe Massiot * Mathias Kretschmer + * Alexis de Lattre * * 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 @@ -26,26 +27,32 @@ * Preamble *****************************************************************************/ #include -#include -#include #include -#include -#include - #include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif + #ifdef HAVE_UNISTD_H # include -#elif defined( _MSC_VER ) && defined( _WIN32 ) -# include #endif -#ifdef WIN32 +#if defined( UNDER_CE ) +# include +#elif defined( WIN32 ) # include # include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif +# define close closesocket #else # include /* hostent ... */ # include @@ -57,6 +64,17 @@ #include "network.h" +#ifndef INADDR_ANY +# define INADDR_ANY 0x00000000 +#endif +#ifndef INADDR_NONE +# define INADDR_NONE 0xFFFFFFFF +#endif +#ifndef IN_MULTICAST +# define IN_MULTICAST(a) IN_CLASSD(a) +#endif + + /***************************************************************************** * Local prototypes *****************************************************************************/ @@ -80,7 +98,7 @@ static int BuildAddr( struct sockaddr_in * p_socket, /* Reset struct */ memset( p_socket, 0, sizeof( struct sockaddr_in ) ); p_socket->sin_family = AF_INET; /* family */ - p_socket->sin_port = htons( i_port ); + p_socket->sin_port = htons( (uint16_t)i_port ); if( !*psz_address ) { p_socket->sin_addr.s_addr = INADDR_ANY; @@ -88,13 +106,14 @@ static int BuildAddr( struct sockaddr_in * p_socket, 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_socket->sin_addr ) ) #else - if( (p_socket->sin_addr.s_addr = inet_addr( psz_address )) == -1 ) + p_socket->sin_addr.s_addr = inet_addr( psz_address ); + if( p_socket->sin_addr.s_addr == INADDR_NONE ) #endif { /* We have a fqdn, try to find its address */ @@ -130,24 +149,32 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) 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; -#ifdef WIN32 - char * psz_bind_win32; /* WIN32 multicast kludge */ -#endif int i_handle, i_opt; - unsigned int i_opt_size; + socklen_t i_opt_size; struct sockaddr_in sock; - if( i_bind_port == 0 ) - { - i_bind_port = config_GetInt( p_this, "server-port" ); - } + /* If IP_ADD_SOURCE_MEMBERSHIP is not defined in the headers + (because it's not in glibc for example), we have to define the + headers required for IGMPv3 here */ +#ifndef IP_ADD_SOURCE_MEMBERSHIP + #define IP_ADD_SOURCE_MEMBERSHIP 39 + struct ip_mreq_source { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; + struct in_addr imr_sourceaddr; + }; +#endif /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0) * protocol */ if( (i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 ) { - msg_Err( p_this, "cannot create socket (%s)", strerror(errno) ); +#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 ); } @@ -156,8 +183,12 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) if( setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR, (void *) &i_opt, sizeof( i_opt ) ) == -1 ) { - msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)", +#ifdef HAVE_ERRNO_H + msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)", strerror(errno)); +#else + msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" ); +#endif close( i_handle ); return( -1 ); } @@ -165,128 +196,233 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) /* 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 ) +#if defined( SYS_BEOS ) + if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void *) &i_opt, sizeof( i_opt ) ) == -1 ) +#else + if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i_opt, sizeof( i_opt ) ) == -1 ) +#endif { - msg_Warn( p_this, "cannot configure socket (SO_RCVBUF: %s)", +#ifdef HAVE_ERRNO_H + msg_Dbg( p_this, "cannot configure socket (SO_RCVBUF: %s)", strerror(errno)); +#else + msg_Warn( p_this, "cannot configure socket (SO_RCVBUF)" ); +#endif } - + /* 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 ) +#if defined( SYS_BEOS ) + if( getsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void*) &i_opt, &i_opt_size ) == -1 ) +#else + if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void*) &i_opt, &i_opt_size ) == -1 ) +#endif { +#ifdef HAVE_ERRNO_H msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)", strerror(errno) ); +#else + msg_Warn( p_this, "cannot query socket (SO_RCVBUF)" ); +#endif } else if( i_opt < 0x80000 ) { - msg_Warn( p_this, "socket buffer size is 0x%x instead of 0x%x", - i_opt, 0x80000 ); + msg_Dbg( p_this, "socket buffer size is 0x%x instead of 0x%x", + i_opt, 0x80000 ); } - - + + /* Build the local socket */ -#ifdef WIN32 - /* Under Win32 and for the multicast, we bind on INADDR_ANY, +#if defined( WIN32 ) && !defined( UNDER_CE ) + /* Under Win32 and for multicasting, we bind to INADDR_ANY, * so let's call BuildAddr with "" instead of psz_bind_addr */ - psz_bind_win32 = psz_bind_addr ; - - /* Check if this is a multicast socket */ - if (IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) ) - { - psz_bind_win32 = ""; - } - if ( BuildAddr( &sock, psz_bind_win32, i_bind_port ) == -1 ) + if( BuildAddr( &sock, IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) ? + "" : psz_bind_addr, i_bind_port ) == -1 ) #else - if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 ) -#endif + if( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 ) +#endif { + msg_Dbg( p_this, "could not build local address" ); close( i_handle ); return( -1 ); } - + /* Bind it */ if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 ) { - msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) ); +#ifdef HAVE_ERRNO_H + msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) ); +#else + msg_Warn( p_this, "cannot bind socket" ); +#endif close( i_handle ); return( -1 ); } +#if defined( WIN32 ) && !defined( UNDER_CE ) + /* Restore the sock struct so we can spare a few #ifdef WIN32 later on */ + if( IN_MULTICAST( ntohl( inet_addr(psz_bind_addr) ) ) ) + { + if ( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 ) + { + msg_Dbg( p_this, "could not build local address" ); + close( i_handle ); + return( -1 ); + } + } +#endif + /* Allow broadcast reception if we bound on INADDR_ANY */ if( !*psz_bind_addr ) { i_opt = 1; - if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, - (void*) &i_opt, sizeof( i_opt ) ) == -1 ) +#if defined( SYS_BEOS ) + if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void*) &i_opt, sizeof( i_opt ) ) == -1 ) +#else + if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, (void*) &i_opt, sizeof( i_opt ) ) == -1 ) +#endif { +#ifdef HAVE_ERRNO_H msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)", strerror(errno) ); +#else + msg_Warn( p_this, "cannot configure socket (SO_BROADCAST)" ); +#endif } } - - /* Join the multicast group if the socket is a multicast address */ -#ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -#endif -#ifndef WIN32 +#if !defined( UNDER_CE ) && !defined( SYS_BEOS ) + /* Join the multicast group if the socket is a multicast address */ if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) ) { - struct ip_mreq imr; + /* Determine interface to be used for multicast */ char * psz_if_addr = config_GetPsz( p_this, "iface-addr" ); - imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr; + + /* If we have a source address, we use IP_ADD_SOURCE_MEMBERSHIP + so that IGMPv3 aware OSes running on IGMPv3 aware networks + will do an IGMPv3 query on the network */ + if( *psz_server_addr ) + { + struct ip_mreq_source imr; + + imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr; + imr.imr_sourceaddr.s_addr = inet_addr(psz_server_addr); + + if( psz_if_addr != NULL && *psz_if_addr + && inet_addr(psz_if_addr) != INADDR_NONE ) + { + imr.imr_interface.s_addr = inet_addr(psz_if_addr); + } + else + { + imr.imr_interface.s_addr = INADDR_ANY; + } + if( psz_if_addr != NULL ) free( psz_if_addr ); + + msg_Dbg( p_this, "IP_ADD_SOURCE_MEMBERSHIP multicast request" ); + /* Join Multicast group with source filter */ + if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, + (char*)&imr, + sizeof(struct ip_mreq_source) ) == -1 ) + { +#ifdef HAVE_ERRNO_H + msg_Err( p_this, "failed to join IP multicast group (%s)", + strerror(errno) ); + msg_Err( p_this, "are you sure your OS supports IGMPv3?" ); #else - if( IN_MULTICAST( ntohl(inet_addr(psz_bind_addr) ) ) ) - { - struct ip_mreq imr; - char * psz_if_addr = config_GetPsz( p_this, "iface-addr" ); - imr.imr_multiaddr.s_addr = inet_addr(psz_bind_addr); + msg_Err( p_this, "failed to join IP multicast group" ); + msg_Err( p_this, "are you sure your OS supports IGMPv3?" ); #endif - if ( *psz_if_addr && inet_addr(psz_if_addr) != -1 ) - { - imr.imr_interface.s_addr = inet_addr(psz_if_addr); - } - else - { - imr.imr_interface.s_addr = INADDR_ANY; - } - free( psz_if_addr ); + close( i_handle ); + return( -1 ); + } + } + /* If there is no source address, we use IP_ADD_MEMBERSHIP */ + else + { + struct ip_mreq imr; - if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char*)&imr, sizeof(struct ip_mreq) ) == -1 ) - { - msg_Err( p_this, "failed to join IP multicast group (%s)", - strerror(errno) ); - close( i_handle ); - return( -1 ); - } + imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr; + if( psz_if_addr != NULL && *psz_if_addr + && inet_addr(psz_if_addr) != INADDR_NONE ) + { + imr.imr_interface.s_addr = inet_addr(psz_if_addr); + } + else + { + imr.imr_interface.s_addr = INADDR_ANY; + } + if( psz_if_addr != NULL ) free( psz_if_addr ); + + msg_Dbg( p_this, "IP_ADD_MEMBERSHIP multicast request" ); + /* Join Multicast group without source filter */ + if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char*)&imr, sizeof(struct ip_mreq) ) == -1 ) + { +#ifdef HAVE_ERRNO_H + msg_Err( p_this, "failed to join IP multicast group (%s)", + strerror(errno) ); +#else + msg_Err( p_this, "failed to join IP multicast group" ); +#endif + close( i_handle ); + return( -1 ); + } + } } +#endif if( *psz_server_addr ) { /* Build socket for remote connection */ if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 ) { - msg_Err( p_this, "cannot build remote address" ); + msg_Warn( p_this, "cannot build remote address" ); close( i_handle ); return( -1 ); } - + /* Connect the socket */ if( connect( i_handle, (struct sockaddr *) &sock, sizeof( sock ) ) == (-1) ) { - msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) ); +#ifdef HAVE_ERRNO_H + msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) ); +#else + msg_Warn( p_this, "cannot connect socket" ); +#endif close( i_handle ); return( -1 ); } + +#if !defined( UNDER_CE ) && !defined( SYS_BEOS ) + if( IN_MULTICAST( ntohl(inet_addr(psz_server_addr) ) ) ) + { + /* set the time-to-live */ + int ttl = p_socket->i_ttl; + if( ttl < 1 ) + { + ttl = config_GetInt( p_this, "ttl" ); + } + if( ttl < 1 ) ttl = 1; + + if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL, + (void *) &ttl, sizeof( ttl ) ) < 0 ) + { +#ifdef HAVE_ERRNO_H + msg_Err( p_this, "failed to set ttl (%s)", strerror(errno) ); +#else + msg_Err( p_this, "failed to set ttl" ); +#endif + close( i_handle ); + return( -1 ); + } + } +#endif } p_socket->i_handle = i_handle; @@ -319,30 +455,119 @@ static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket ) * protocol */ if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) { - msg_Err( p_this, "cannot create socket (%s)", strerror(errno) ); - return( -1 ); +#ifdef HAVE_ERRNO_H + msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) ); +#else + msg_Warn( p_this, "cannot create socket" ); +#endif + goto error; } /* Build remote address */ if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 ) { - close( i_handle ); - return( -1 ); + msg_Dbg( p_this, "could not build local address" ); + goto error; + } + + /* 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 /* Connect the socket */ - if( connect( i_handle, (struct sockaddr *) &sock, - sizeof( sock ) ) == (-1) ) + if( connect( i_handle, (struct sockaddr *) &sock, sizeof( sock ) ) == -1 ) { - msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) ); - close( i_handle ); - return( -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; + int i_opt; + int i_opt_size = sizeof( i_opt ); + struct timeval timeout; + fd_set fds; + + msg_Dbg( p_this, "connection in progress" ); + do + { + if( p_this->b_die ) + { + msg_Dbg( p_this, "connection aborted" ); + goto error; + } + + /* 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( 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; + } + } + 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; - return( 0 ); +error: + if( i_handle > 0 ) + { + close( i_handle ); + } + return VLC_EGENERIC; } /*****************************************************************************