X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fnetwork%2Fipv4.c;h=fdfaf8057912aa76e03851ce6c7b6e10fa0d3be5;hb=c1b4f4834670dbe158b6035d5b429ed2d772f17e;hp=f3e7b49c5ec81d7c0cca3bbb02317a773207c2af;hpb=7211d64eb0b68c60fdde0148a59f8a9f26b7d5fc;p=vlc diff --git a/modules/misc/network/ipv4.c b/modules/misc/network/ipv4.c index f3e7b49c5e..fdfaf80579 100644 --- a/modules/misc/network/ipv4.c +++ b/modules/misc/network/ipv4.c @@ -2,10 +2,11 @@ * ipv4.c: IPv4 network abstraction layer ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN - * $Id: ipv4.c,v 1.15 2003/03/03 14:21:08 gbazin 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 @@ -51,9 +52,7 @@ #elif defined( WIN32 ) # include # include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif +# define close closesocket #else # include /* hostent ... */ # include @@ -71,6 +70,10 @@ #ifndef INADDR_NONE # define INADDR_NONE 0xFFFFFFFF #endif +#ifndef IN_MULTICAST +# define IN_MULTICAST(a) IN_CLASSD(a) +#endif + /***************************************************************************** * Local prototypes @@ -146,22 +149,31 @@ 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; -#if defined( WIN32 ) && !defined( UNDER_CE ) - char * psz_bind_win32; /* WIN32 multicast kludge */ -#endif int i_handle, i_opt; socklen_t i_opt_size; struct sockaddr_in sock; + /* 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 ) { #ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot create socket (%s)", strerror(errno) ); + msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) ); #else - msg_Err( p_this, "cannot create socket" ); + msg_Warn( p_this, "cannot create socket" ); #endif return( -1 ); } @@ -172,16 +184,12 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) (void *) &i_opt, sizeof( i_opt ) ) == -1 ) { #ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot configure socket (SO_REUSEADDR: %s)", + msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR: %s)", strerror(errno)); #else - msg_Err( p_this, "cannot configure socket (SO_REUSEADDR)" ); + msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" ); #endif -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else close( i_handle ); -#endif return( -1 ); } @@ -189,11 +197,10 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) * packet loss caused by scheduling problems */ i_opt = 0x80000; #if defined( SYS_BEOS ) - if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, + if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void *) &i_opt, sizeof( i_opt ) ) == -1 ) #else - if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, + if( setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i_opt, sizeof( i_opt ) ) == -1 ) #endif - (void *) &i_opt, sizeof( i_opt ) ) == -1 ) { #ifdef HAVE_ERRNO_H msg_Dbg( p_this, "cannot configure socket (SO_RCVBUF: %s)", @@ -209,11 +216,10 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) i_opt = 0; i_opt_size = sizeof( i_opt ); #if defined( SYS_BEOS ) - if( getsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, + if( getsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void*) &i_opt, &i_opt_size ) == -1 ) #else - if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, + if( getsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void*) &i_opt, &i_opt_size ) == -1 ) #endif - (void*) &i_opt, &i_opt_size ) == -1 ) { #ifdef HAVE_ERRNO_H msg_Warn( p_this, "cannot query socket (SO_RCVBUF: %s)", @@ -232,26 +238,16 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) /* Build the local socket */ #if defined( WIN32 ) && !defined( UNDER_CE ) - /* Under Win32 and for the multicast, we bind on INADDR_ANY, + /* 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 ) + if( BuildAddr( &sock, psz_bind_addr, i_bind_port ) == -1 ) #endif { msg_Dbg( p_this, "could not build local address" ); -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else close( i_handle ); -#endif return( -1 ); } @@ -259,28 +255,36 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) if( bind( i_handle, (struct sockaddr *)&sock, sizeof( sock ) ) < 0 ) { #ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot bind socket (%s)", strerror(errno) ); + msg_Warn( p_this, "cannot bind socket (%s)", strerror(errno) ); #else - msg_Err( p_this, "cannot bind socket" ); + msg_Warn( p_this, "cannot bind socket" ); #endif -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else close( i_handle ); -#endif 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 defined( SYS_BEOS ) - if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, + if( setsockopt( i_handle, SOL_SOCKET, SO_NONBLOCK, (void*) &i_opt, sizeof( i_opt ) ) == -1 ) #else - if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, + if( setsockopt( i_handle, SOL_SOCKET, SO_BROADCAST, (void*) &i_opt, sizeof( i_opt ) ) == -1 ) #endif - (void*) &i_opt, sizeof( i_opt ) ) == -1 ) { #ifdef HAVE_ERRNO_H msg_Warn( p_this, "cannot configure socket (SO_BROADCAST: %s)", @@ -293,64 +297,92 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) #if !defined( UNDER_CE ) && !defined( SYS_BEOS ) /* 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( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) ) { - struct ip_mreq imr; - char * psz_if_addr = config_GetPsz( p_this, "iface-addr" ); - imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr; -#else - if( IN_MULTICAST( ntohl(inet_addr(psz_bind_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 = inet_addr(psz_bind_addr); -#endif - 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 ); - if( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char*)&imr, sizeof(struct ip_mreq) ) == -1 ) + /* 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_Warn( p_this, "failed to join IP multicast group (%s)", - strerror(errno) ); + 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 - msg_Warn( p_this, "failed to join IP multicast group" ); + msg_Err( p_this, "failed to join IP multicast group" ); + msg_Err( p_this, "are you sure your OS supports IGMPv3?" ); #endif -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); + close( i_handle ); + return( -1 ); + } + } + /* If there is no source address, we use IP_ADD_MEMBERSHIP */ + else + { + struct ip_mreq imr; + + 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 - close( i_handle ); + msg_Err( p_this, "failed to join IP multicast group" ); #endif - return( -1 ); - } + close( i_handle ); + return( -1 ); + } + } } -#endif /* UNDER_CE */ +#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" ); -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else + msg_Warn( p_this, "cannot build remote address" ); close( i_handle ); -#endif return( -1 ); } @@ -359,46 +391,38 @@ static int OpenUDP( vlc_object_t * p_this, network_socket_t * p_socket ) sizeof( sock ) ) == (-1) ) { #ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) ); + msg_Warn( p_this, "cannot connect socket (%s)", strerror(errno) ); #else - msg_Err( p_this, "cannot connect socket" ); + msg_Warn( p_this, "cannot connect socket" ); #endif -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else close( i_handle ); -#endif return( -1 ); } -#ifndef WIN32 - if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) ) -#else +#if !defined( UNDER_CE ) && !defined( SYS_BEOS ) if( IN_MULTICAST( ntohl(inet_addr(psz_server_addr) ) ) ) -#endif { /* set the time-to-live */ - int ttl = config_GetInt( p_this, "ttl" ); + int ttl = p_socket->i_ttl; if( ttl < 1 ) - ttl = 1; + { + ttl = config_GetInt( p_this, "ttl" ); + } + if( ttl < 1 ) ttl = 1; if( setsockopt( i_handle, IPPROTO_IP, IP_MULTICAST_TTL, - &ttl, sizeof( ttl ) ) < 0 ) + (void *) &ttl, sizeof( ttl ) ) < 0 ) { #ifdef HAVE_ERRNO_H - msg_Warn( p_this, "failed to set ttl (%s)", - strerror(errno) ); + msg_Err( p_this, "failed to set ttl (%s)", strerror(errno) ); #else - msg_Warn( p_this, "failed to set ttl" ); + msg_Err( p_this, "failed to set ttl" ); #endif -#if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else close( i_handle ); -#endif return( -1 ); } } +#endif } p_socket->i_handle = i_handle; @@ -432,46 +456,118 @@ static int OpenTCP( vlc_object_t * p_this, network_socket_t * p_socket ) if( (i_handle = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) { #ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot create socket (%s)", strerror(errno) ); + msg_Warn( p_this, "cannot create socket (%s)", strerror(errno) ); #else - msg_Err( p_this, "cannot create socket" ); + msg_Warn( p_this, "cannot create socket" ); #endif - return( -1 ); + goto error; } /* Build remote address */ if ( BuildAddr( &sock, psz_server_addr, i_server_port ) == -1 ) { msg_Dbg( p_this, "could not build local address" ); + goto error; + } + + /* Set to non-blocking */ #if defined( WIN32 ) || defined( UNDER_CE ) - closesocket( i_handle ); -#else - close( i_handle ); -#endif - return( -1 ); + { + 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 ) { -#ifdef HAVE_ERRNO_H - msg_Err( p_this, "cannot connect socket (%s)", strerror(errno) ); +#if defined( WIN32 ) || defined( UNDER_CE ) + if( WSAGetLastError() == WSAEWOULDBLOCK ) +#elif defined( HAVE_ERRNO_H ) + if( errno == EINPROGRESS ) #else - msg_Err( p_this, "cannot connect socket" ); + 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 ) - closesocket( i_handle ); + ( i_ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK ) ); +#elif defined( HAVE_ERRNO_H ) + ( i_ret < 0 && errno == EINTR ) ); #else - close( i_handle ); + ( i_ret < 0 ) ); #endif - return( -1 ); + + 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; } /*****************************************************************************