X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fnetutils.c;h=03601ddea0c9bc6c366081439b39ef1a6815ef52;hb=16f2ff86c7a1095b0e6365a21edacad85a80303d;hp=43b91f50c8a68e6cc0388ddb2fd1deded47ef51f;hpb=2a8ef11d88512a2717f177f7f8da334feca030b5;p=vlc diff --git a/src/misc/netutils.c b/src/misc/netutils.c index 43b91f50c8..03601ddea0 100644 --- a/src/misc/netutils.c +++ b/src/misc/netutils.c @@ -1,16 +1,21 @@ /***************************************************************************** * netutils.c: various network functions ***************************************************************************** - * Copyright (C) 1999, 2000 VideoLAN + * Copyright (C) 1999-2001 VideoLAN + * $Id: netutils.c,v 1.73 2002/07/31 20:56:53 sam Exp $ * * Authors: Vincent Seguin * Benoit Steiner + * Henri Fallon + * Xavier Marchesini + * Christophe Massiot + * Samuel Hocevar * * 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 @@ -24,309 +29,395 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include "defs.h" - -#include /* gethostbyname() */ #include /* free(), realloc(), atoi() */ #include /* errno() */ -#include /* bzero(), bcopy() */ +#include /* memset() */ + +#include + +#ifdef HAVE_UNISTD_H +# include /* gethostname() */ +#elif defined( _MSC_VER ) && defined( _WIN32 ) +# include +#endif + +#if !defined( _MSC_VER ) +#include /* gettimeofday */ +#endif -#include /* BSD: struct in_addr */ -#include /* BSD: struct sockaddr */ -#ifdef HAVE_ARPA_INET_H -#include /* inet_ntoa(), inet_aton() */ +#ifdef WIN32 +# include +#else +# include /* hostent ... */ +# include /* BSD: struct sockaddr */ +# include /* BSD: struct in_addr */ +# ifdef HAVE_ARPA_INET_H +# include /* inet_ntoa(), inet_aton() */ +# endif #endif -#if defined (HAVE_SYS_IOCTL_H) +#ifdef SYS_LINUX #include /* ioctl() */ #endif -#include /* needed for ioctl on Solaris */ -#include +#if defined( WIN32 ) /* tools to get the MAC adress from */ +#include /* the interface under Windows */ +#include +#include +#endif -#if defined (HAVE_NET_IF_H) +#ifdef HAVE_NET_IF_H #include /* interface (arch-dependent) */ #endif + #ifdef HAVE_SYS_SOCKIO_H #include #endif -#include "config.h" -#include "common.h" -#include "mtime.h" - -#include "intf_msg.h" -#include "debug.h" - #include "netutils.h" +#include "vlc_playlist.h" + +#include "network.h" /***************************************************************************** - * BuildInetAddr: build an Internet address descriptor + * input_channel_t: channel library data ***************************************************************************** - * Build an internet socket descriptor from a host name, or an ip, and a port. - * If the address is NULL, then INADDR_ANY will be used, allowing to receive - * on any address for a local socket. Usually, in this case, 'port' is also null - * and the function always succeeds. + * Store global channel library data. + * The part of the code concerning the channel changing process is unstable + * as it depends on the VideoLAN channel server, which isn't frozen for + * the time being. *****************************************************************************/ -int BuildInetAddr( struct sockaddr_in *p_sa_in, char *psz_in_addr, int i_port ) +struct input_channel_t { - struct hostent *p_hostent; /* host descriptor */ - - memset( p_sa_in, 0, sizeof( struct sockaddr_in ) ); - p_sa_in->sin_family = AF_INET; /* family */ - p_sa_in->sin_port = htons( i_port ); /* port */ + int i_channel; /* current channel number */ + mtime_t last_change; /* last change date */ +}; - /* Use INADDR_ANY if psz_in_addr is NULL */ - if( psz_in_addr == NULL ) - { - p_sa_in->sin_addr.s_addr = htonl(INADDR_ANY); - } - /* Try to convert address directly from in_addr - this will work if - * psz_in_addr is dotted decimal. */ -#if defined HAVE_ARPA_INET_H && !defined SYS_SOLARIS - else if( !inet_aton( psz_in_addr, &p_sa_in->sin_addr) ) -#else - else if( (p_sa_in->sin_addr.s_addr = inet_addr( psz_in_addr )) == -1 ) +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int GetMacAddress ( vlc_object_t *, int i_fd, char *psz_mac ); +#ifdef WIN32 +static int GetAdapterInfo ( int i_adapter, char *psz_string ); #endif - { - /* The convertion failed: the address is an host name, which needs - * to be resolved */ - intf_DbgMsg("debug: resolving internet address %s...", psz_in_addr); - if ( (p_hostent = gethostbyname(psz_in_addr)) == NULL) - { - intf_ErrMsg( "net error: unknown host %s", psz_in_addr ); - return( -1 ); - } - - /* Copy the first address of the host in the socket address */ - memmove( &p_sa_in->sin_addr, p_hostent->h_addr_list[0], p_hostent->h_length ); - } - return( 0 ); -} - /***************************************************************************** - * ServerPort: extract port from a "server:port" adress + * network_ChannelCreate: initialize global channel method data ***************************************************************************** - * Returns the port number in a "server:port" address and replace the ":" by - * a NUL character, or returns -1. + * Initialize channel input method global data. This function should be called + * once before any input thread is created or any call to other + * input_Channel*() function is attempted. *****************************************************************************/ -int ServerPort( char *psz_addr ) +int __network_ChannelCreate( vlc_object_t *p_this ) { - char *psz_index; +#if !defined( SYS_LINUX ) && !defined( WIN32 ) + msg_Err( p_this, "VLAN-based channels are not supported " + "on this architecture" ); +#endif - /* Scan string for ':' */ - for( psz_index = psz_addr; *psz_index && (*psz_index != ':'); psz_index++ ) + /* Allocate structure */ + p_this->p_vlc->p_channel = malloc( sizeof( input_channel_t ) ); + if( p_this->p_vlc->p_channel == NULL ) { - ; + msg_Err( p_this, "out of memory" ); + return( -1 ); } - /* If a port number has been found, convert it and return it */ - if( *psz_index == ':' ) - { - *psz_index = '\0'; - return( atoi( psz_index + 1 ) ); - } + /* Initialize structure */ + p_this->p_vlc->p_channel->i_channel = 0; + p_this->p_vlc->p_channel->last_change = 0; - return( - 1 ); + msg_Dbg( p_this, "channels initialized" ); + return( 0 ); } - /***************************************************************************** - * ReadIfConf: Read the configuration of an interface + * network_ChannelJoin: join a channel ***************************************************************************** - * i_sockfd must reference a socket open as follow: AF_INET, DOCK_DGRAM, 0 + * This function will try to join a channel. If the relevant interface is + * already on the good channel, nothing will be done. Else, and if possible + * (if the interface is not locked), the channel server will be contacted + * and a change will be requested. The function will block until the change + * is effective. Note that once a channel is no more used, its interface + * should be unlocked using input_ChannelLeave(). + * Non 0 will be returned in case of error. *****************************************************************************/ -int ReadIfConf(int i_sockfd, if_descr_t* p_ifdescr, char* psz_name) +int __network_ChannelJoin( vlc_object_t *p_this, int i_channel ) { - int i_rc = 0; -#if defined (HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) - struct ifreq ifr_config; - - ASSERT(p_ifdescr); - ASSERT(psz_name); - - /* Which interface are we interested in ? */ - strcpy(ifr_config.ifr_name, psz_name); - - /* Read the flags for that interface */ - i_rc = ioctl(i_sockfd, SIOCGIFFLAGS, (byte_t *)&ifr_config); - if( !i_rc ) +#define VLCS_VERSION 13 +#define MESSAGE_LENGTH 256 + + module_t * p_network; + char * psz_network = NULL; + network_socket_t socket_desc; + char psz_mess[ MESSAGE_LENGTH ]; + char psz_mac[ 40 ]; + int i_fd, i_port; + char *psz_vlcs; + struct timeval delay; + fd_set fds; + + if( p_this->p_vlc->p_channel->i_channel == i_channel ) { - p_ifdescr->i_flags = ifr_config.ifr_flags; - intf_DbgMsg("%s flags: 0x%x", psz_name, p_ifdescr->i_flags); + return 0; } - else + + if( !config_GetInt( p_this, "network-channel" ) ) { - intf_ErrMsg( "net error: cannot read flags for interface %s (%s)", - psz_name, strerror(errno) ); + msg_Err( p_this, "channels disabled, to enable them, use the" + " --channels option" ); return -1; } - /* Read physical address of the interface and store it in our description */ -#ifdef SYS_SOLARIS - i_rc = ioctl(i_sockfd, SIOCGENADDR, (byte_t *)&ifr_config); -#else - i_rc = ioctl(i_sockfd, SIOCGIFHWADDR, (byte_t *)&ifr_config); -#endif - if( !i_rc ) + if( config_GetInt( p_this, "ipv4" ) ) { - memcpy(&p_ifdescr->sa_phys_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr)); - intf_DbgMsg("%s MAC address: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", psz_name, - p_ifdescr->sa_phys_addr.sa_data[0]&0xff, - p_ifdescr->sa_phys_addr.sa_data[1]&0xff, - p_ifdescr->sa_phys_addr.sa_data[2]&0xff, - p_ifdescr->sa_phys_addr.sa_data[3]&0xff, - p_ifdescr->sa_phys_addr.sa_data[4]&0xff, - p_ifdescr->sa_phys_addr.sa_data[5]&0xff); + psz_network = "ipv4"; } - else + if( config_GetInt( p_this, "ipv6" ) ) { - intf_ErrMsg( "net error: cannot read hardware address for %s (%s)", - psz_name, strerror(errno) ); + psz_network = "ipv6"; + } + + /* Getting information about the channel server */ + if( !(psz_vlcs = config_GetPsz( p_this, "channel-server" )) ) + { + msg_Err( p_this, "configuration variable channel-server empty" ); return -1; } - /* Read IP address of the interface and store it in our description */ - i_rc = ioctl(i_sockfd, SIOCGIFADDR, (byte_t *)&ifr_config); - if( !i_rc ) + i_port = config_GetInt( p_this, "channel-port" ); + + msg_Dbg( p_this, "connecting to %s:%d", psz_vlcs, i_port ); + + /* Prepare the network_socket_t structure */ + socket_desc.i_type = NETWORK_UDP; + socket_desc.psz_bind_addr = ""; + socket_desc.i_bind_port = 4321; + socket_desc.psz_server_addr = psz_vlcs; + socket_desc.i_server_port = i_port; + + /* Find an appropriate network module */ + p_network = module_Need( p_this, "network", psz_network/*, &socket_desc*/ ); + if( p_network == NULL ) { - memcpy(&p_ifdescr->sa_net_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr)); - intf_DbgMsg("%s IP address: %s", psz_name, - inet_ntoa(p_ifdescr->sa_net_addr.sin_addr)); + return( -1 ); } - else + module_Unneed( p_this, p_network ); + + free( psz_vlcs ); /* Do we really need this ? -- Meuuh */ + i_fd = socket_desc.i_handle; + + /* Look for the interface MAC address */ + if( GetMacAddress( p_this, i_fd, psz_mac ) ) { - intf_ErrMsg( "net error: cannot read network address for %s (%s)", - psz_name, strerror(errno) ); + msg_Err( p_this, "failed getting MAC address" ); + close( i_fd ); return -1; } - /* Read broadcast address of the interface and store it in our description */ - if(p_ifdescr->i_flags & IFF_POINTOPOINT) + msg_Dbg( p_this, "MAC address is %s", psz_mac ); + + /* Build the message */ + sprintf( psz_mess, "%d %u %lu %s \n", i_channel, VLCS_VERSION, + (unsigned long)(mdate() / (u64)1000000), + psz_mac ); + + /* Send the message */ + send( i_fd, psz_mess, MESSAGE_LENGTH, 0 ); + + msg_Dbg( p_this, "attempting to join channel %d", i_channel ); + + /* We have changed channels ! (or at least, we tried) */ + p_this->p_vlc->p_channel->last_change = mdate(); + p_this->p_vlc->p_channel->i_channel = i_channel; + + /* Wait 5 sec for an answer from the server */ + delay.tv_sec = 5; + delay.tv_usec = 0; + FD_ZERO( &fds ); + FD_SET( i_fd, &fds ); + + switch( select( i_fd + 1, &fds, NULL, NULL, &delay ) ) { - intf_DbgMsg("%s doen't not support broadcast", psz_name); - i_rc = ioctl(i_sockfd, SIOCGIFDSTADDR, (byte_t *)&ifr_config); + case 0: + msg_Err( p_this, "no answer from vlcs" ); + close( i_fd ); + return -1; + + case -1: + msg_Err( p_this, "error while listening to vlcs" ); + close( i_fd ); + return -1; } - else + + recv( i_fd, psz_mess, MESSAGE_LENGTH, 0 ); + psz_mess[ MESSAGE_LENGTH - 1 ] = '\0'; + + if( !strncasecmp( psz_mess, "E:", 2 ) ) { - intf_DbgMsg("%s supports broadcast", psz_name); - i_rc = ioctl(i_sockfd, SIOCGIFBRDADDR, (byte_t *)&ifr_config); + msg_Err( p_this, "vlcs said '%s'", psz_mess + 2 ); + close( i_fd ); + return -1; } - if( !i_rc ) + else if( !strncasecmp( psz_mess, "I:", 2 ) ) { - memcpy(&p_ifdescr->sa_bcast_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr)); - intf_DbgMsg("%s broadcast address: %s", psz_name, - inet_ntoa(p_ifdescr->sa_bcast_addr.sin_addr)); + msg_Dbg( p_this, "vlcs said '%s'", psz_mess + 2 ); } else { - intf_ErrMsg( "net error: cannot read broadcast address for %s (%s)", - psz_name, strerror(errno)); - return -1; + /* We got something to play ! */ + playlist_t *p_playlist; + p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + if( p_playlist != NULL ) + { + playlist_Add( p_playlist, psz_mess, + PLAYLIST_APPEND | PLAYLIST_GO, PLAYLIST_END ); + vlc_object_release( p_playlist ); + } } + + /* Close the socket and return nicely */ +#ifndef WIN32 + close( i_fd ); +#else + closesocket( i_fd ); #endif - return i_rc; + return 0; } - +/* Following functions are local */ /***************************************************************************** - * ReadNetConf: Retrieve the network configuration of the host - ***************************************************************************** - * Only IP interfaces are listed, and only if they are up - * i_sockfd must reference a socket open as follow: AF_INET, DOCK_DGRAM, 0 + * GetMacAddress: extract the MAC Address *****************************************************************************/ -int ReadNetConf(int i_sockfd, net_descr_t* p_net_descr) +static int GetMacAddress( vlc_object_t *p_this, int i_fd, char *psz_mac ) { - int i_rc = 0; +#if defined( SYS_LINUX ) + struct ifreq interface; + int i_ret; + char *psz_interface; + + /* + * Looking for information about the eth0 interface + */ + interface.ifr_addr.sa_family = AF_INET; + if( !(psz_interface = config_GetPsz( p_this, "iface" )) ) + { + msg_Err( p_this, "configuration variable iface empty" ); + return -1; + } + strcpy( interface.ifr_name, psz_interface ); + free( psz_interface ); -#if defined (HAVE_SYS_IOCTL_H) && defined (HAVE_NET_IF_H) - struct ifreq* a_ifr_ifconf = NULL; - struct ifreq* p_ifr_current_if; - struct ifconf ifc_netconf; + i_ret = ioctl( i_fd, SIOCGIFHWADDR, &interface ); - int i_if_number; - int i_remaining; + if( i_ret ) + { + msg_Err( p_this, "ioctl SIOCGIFHWADDR failed" ); + return( i_ret ); + } - ASSERT(p_net_descr); + sprintf( psz_mac, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + interface.ifr_hwaddr.sa_data[0] & 0xff, + interface.ifr_hwaddr.sa_data[1] & 0xff, + interface.ifr_hwaddr.sa_data[2] & 0xff, + interface.ifr_hwaddr.sa_data[3] & 0xff, + interface.ifr_hwaddr.sa_data[4] & 0xff, + interface.ifr_hwaddr.sa_data[5] & 0xff ); - /* Start by assuming we have few than 3 interfaces (i_if_number will - be incremented by 1 when entering the loop) */ - i_if_number = 2; + return( 0 ); - /* Retrieve network configuration for that host */ - do - { - i_if_number++; - a_ifr_ifconf = realloc(a_ifr_ifconf, i_if_number*sizeof(struct ifreq)); - ifc_netconf.ifc_len = i_if_number*sizeof(struct ifreq); - ifc_netconf.ifc_req = a_ifr_ifconf; +#elif defined( WIN32 ) + int i, i_ret = -1; - i_rc = ioctl(i_sockfd, SIOCGIFCONF, (byte_t*)&ifc_netconf); - if( i_rc ) - { - intf_ErrMsg( "net error: cannot read network configuration (%s)", - strerror(errno)); - break; - } - } - /* If we detected ifc_len interfaces, this may mean that others have - been missed because the a_ifr_ifconf was to little, so increase - it's size and retry */ - while( ifc_netconf.ifc_len >= i_if_number*sizeof(struct ifreq) ); + /* Get adapter list - support for more than one adapter */ + LANA_ENUM AdapterList; + NCB Ncb; - /* No see what we detected */ - if( !i_rc ) + msg_Dbg( p_this, "looking for MAC address" ); + + memset( &Ncb, 0, sizeof( NCB ) ); + Ncb.ncb_command = NCBENUM; + Ncb.ncb_buffer = (unsigned char *)&AdapterList; + Ncb.ncb_length = sizeof( AdapterList ); + Netbios( &Ncb ); + + /* Get all of the local ethernet addresses */ + for ( i = 0; i < AdapterList.length ; ++i ) { - /* Init the given net_descr_t struct */ - p_net_descr->i_if_number = 0; - p_net_descr->a_if = NULL; - - /* Iterate through the entries of the a_ifr_ifconf table */ - p_ifr_current_if = ifc_netconf.ifc_req; - for( i_remaining = ifc_netconf.ifc_len / sizeof (struct ifreq); - i_remaining-- > 0; p_ifr_current_if++ ) + if ( GetAdapterInfo ( AdapterList.lana[ i ], psz_mac ) == 0 ) { - intf_DbgMsg("Found interface %s", p_ifr_current_if->ifr_name); - - /* Don't use an interface devoted to an address family other than IP */ - if(p_ifr_current_if->ifr_addr.sa_family != AF_INET) - continue; - - /* Read the status of this interface */ - if( ioctl(i_sockfd, SIOCGIFFLAGS, (byte_t *)p_ifr_current_if) < 0 ) - { - intf_ErrMsg( "net error: cannot access interface %s (%s)", - p_ifr_current_if->ifr_name, strerror(errno) ); - i_rc = -1; - break; - } - else - { - /* Skip this interface if it is not up or if this is a loopback one */ - if( !p_ifr_current_if->ifr_flags & IFF_UP || - p_ifr_current_if->ifr_flags & IFF_LOOPBACK ) - continue; - - /* Add an entry to the net_descr struct to store the description of - that interface */ - p_net_descr->i_if_number++; - p_net_descr->a_if = realloc(p_net_descr->a_if, - p_net_descr->i_if_number*sizeof(if_descr_t)); - /* FIXME: Read the info ?? */ - i_rc = ReadIfConf(i_sockfd, &p_net_descr->a_if[p_net_descr->i_if_number-1], - p_ifr_current_if->ifr_name); - } + i_ret = 0; } } - /* Don't need the a_ifr_ifconf anymore */ - free( a_ifr_ifconf ); -#endif + return( i_ret ); - return i_rc; +#else + strcpy( psz_mac, "00:00:00:00:00:00" ); + return( 0 ); + +#endif } +#ifdef WIN32 +/***************************************************************************** + * GetAdapterInfo : gets some informations about the interface using NETBIOS + *****************************************************************************/ +static int GetAdapterInfo( int i_adapter, char *psz_string ) +{ + struct ASTAT + { + ADAPTER_STATUS adapt; + NAME_BUFFER psz_name[30]; + } Adapter; + + /* Reset the LAN adapter so that we can begin querying it */ + NCB Ncb; + memset( &Ncb, 0, sizeof ( Ncb ) ); + Ncb.ncb_command = NCBRESET; + Ncb.ncb_lana_num = i_adapter; + + if( Netbios( &Ncb ) != NRC_GOODRET ) + { +//X intf_ErrMsg( "network error: reset returned %i", Ncb.ncb_retcode ); + return -1; + } + + /* Prepare to get the adapter status block */ + memset( &Ncb, 0, sizeof( Ncb ) ) ; /* Initialization */ + Ncb.ncb_command = NCBASTAT; + Ncb.ncb_lana_num = i_adapter; + + strcpy( (char *)Ncb.ncb_callname, "*" ); + + memset( &Adapter, 0, sizeof ( Adapter ) ); + Ncb.ncb_buffer = ( unsigned char * ) &Adapter; + Ncb.ncb_length = sizeof ( Adapter ); + + /* Get the adapter's info and, if this works, return it in standard, + * colon-delimited form. */ + if ( Netbios( &Ncb ) == 0 ) + { + sprintf ( psz_string, "%02X:%02X:%02X:%02X:%02X:%02X", + (int) ( Adapter.adapt.adapter_address[0] ), + (int) ( Adapter.adapt.adapter_address[1] ), + (int) ( Adapter.adapt.adapter_address[2] ), + (int) ( Adapter.adapt.adapter_address[3] ), + (int) ( Adapter.adapt.adapter_address[4] ), + (int) ( Adapter.adapt.adapter_address[5] ) ); + +//X intf_WarnMsg( 2, "network: found MAC address %s", psz_string ); + + return 0; + } + else + { +//X intf_ErrMsg( "network error: ASTAT returned %i", Ncb.ncb_retcode ); + return -1; + } +} +#endif /* WIN32 */