Improved and updated patch. The function tcp_connect() mostly sees variable name changes. One adaption of address families improves resolution.
get_if_ip() is completely reworked, since trustworthy
documentation says that ioctl(SIOCGIFADDR) can NEVER
support IPv6. That support comes now from getifaddrs()
instead!
The present code has been tested in six cases:
* TCP stack with only one address family configured,
with and without "interfaces = eth0".
These make for four cases. Functionality
is perfect, once the name resolver can
use the correct address family.
* Dual address spaces without "interfaces = eth0".
Perfect functionality.
* Dual address spaces with config "interfaces = eth0".
Using glibc 2.10, the present implementation's use
of getaddrinfo() without further configuration, will
prefer IPv6-addresses over IPv4-addresses. However,
the function getifaddrs() used in get_if_ip() turns
to all surprise out to function using the reverse
preference. Thus, practical experiments shows that
in this case incorrect denials can and do occur.
I conclude that this second patch suggestion of mine
looks very promising for lending IPv6 support to Axel.
Best regards,
Mats Erik Andersson, fil. dr
Description: Implement IPv6 prototocol support. The transport sockets are reimplemented to fully accept address family IPv4 as well as IPv6. . The lookup function get_if_ip() is reimplemented using getifaddrs(), as ioctl() with SIOCGIFADDR cannot retreive IPv6 addresses. Author: Mats Erik Andersson <[email protected]> Forwarded: yes X-Comment: Version 2 Last-Update: 2010-04-15 --- axel-2.4.orig/tcp.c 2009-04-27 16:19:03.000000000 +0200 +++ axel-2.4/tcp.c 2010-04-15 20:12:11.000000000 +0200 @@ -24,60 +24,116 @@ */ #include "axel.h" +#include <ifaddrs.h> + +#define LOCAL_DEBUG 0 /* Get a TCP connection */ int tcp_connect( char *hostname, int port, char *local_if ) { - struct hostent *host = NULL; - struct sockaddr_in addr; - struct sockaddr_in local; - int fd; + struct addrinfo hints, *ai, *aiptr; + struct sockaddr_storage local; + socklen_t local_addrlen = 0; + int fd, do_bind = 0; + char portstr[12]; -#ifdef DEBUG +#if defined(DEBUG) || LOCAL_DEBUG + struct sockaddr_in *local_sa4 = (struct sockaddr_in *) &local; + struct sockaddr_in6 *local_sa6 = (struct sockaddr_in6 *) &local; socklen_t i = sizeof( local ); fprintf( stderr, "tcp_connect( %s, %i ) = ", hostname, port ); #endif - - /* Why this loop? Because the call might return an empty record. - At least it very rarely does, on my system... */ - for( fd = 0; fd < 5; fd ++ ) - { - if( ( host = gethostbyname( hostname ) ) == NULL ) - return( -1 ); - if( *host->h_name ) break; - } - if( !host || !host->h_name || !*host->h_name ) - return( -1 ); - - if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) - return( -1 ); - + + /* First check the validity of any local part. + * This chosen address prescribes the address family. */ if( local_if && *local_if ) { - local.sin_family = AF_INET; - local.sin_port = 0; - local.sin_addr.s_addr = inet_addr( local_if ); - if( bind( fd, (struct sockaddr *) &local, sizeof( struct sockaddr_in ) ) == -1 ) + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST | AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if( getaddrinfo(local_if, NULL, &hints, &aiptr) ) + return -1; + + for( ai = aiptr; ai; ai = ai->ai_next ) { + if( (fd = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol) ) < 0 ) + continue; + + if( bind( fd, (struct sockaddr *) &local, ai->ai_addrlen) + == -1 ) + { + close( fd ); + continue; + } + /* Success */ close( fd ); + break; + } /* Looping over candidates in aiptr. */ + + if( ai == NULL ) + { + /* Reached end of candidate list. No success! */ + freeaddrinfo(aiptr); return( -1 ); } - } - - addr.sin_family = AF_INET; - addr.sin_port = htons( port ); - addr.sin_addr = *( (struct in_addr *) host->h_addr ); - - if( connect( fd, (struct sockaddr *) &addr, sizeof( struct sockaddr_in ) ) == -1 ) + + /* Access was granted to local address. */ + do_bind = 1; + local_addrlen = ai->ai_addrlen; + freeaddrinfo(aiptr); + } /* Lookup of local address for binding. */ + + memset(&hints, 0, sizeof(hints)); + snprintf(portstr, sizeof(portstr)-1, "%d", port); + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = do_bind ? local.ss_family : AF_UNSPEC; + + if( getaddrinfo(hostname, portstr, &hints, &aiptr) ) { - close( fd ); + /* Failed host lookup. */ return( -1 ); } - -#ifdef DEBUG - getsockname( fd, &local, &i ); - fprintf( stderr, "%i\n", ntohs( local.sin_port ) ); + + /* Successfully resolved hostname. Loop through candidates. */ + for( ai = aiptr; ai; ai = ai->ai_next ) + { + if( ( fd = socket( ai->ai_family, ai->ai_socktype, + ai->ai_protocol) ) < 0 ) + continue; + + if( do_bind && + bind( fd, (struct sockaddr *) &local, local_addrlen) == -1 ) + { + close( fd ); + continue; + } + + if( connect( fd, ai->ai_addr, ai->ai_addrlen) < 0 ) + { + close( fd ); + continue; + } + + break; + } /* Looping over candidates in aiptr. */ + + if( ai == NULL ) + { + /* No answer. */ + freeaddrinfo(aiptr); + return -1; + } + +#if defined(DEBUG) || LOCAL_DEBUG + getsockname( fd, (struct sockaddr *) &local, &i ); + fprintf( stderr, "%i\n", + ntohs( (local.ss_family == AF_INET6) ? local_sa6->sin6_port + : local_sa4->sin_port ) ); #endif return( fd ); @@ -85,21 +141,40 @@ int tcp_connect( char *hostname, int por int get_if_ip( char *iface, char *ip ) { - struct ifreq ifr; - int fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_IP ); - - memset( &ifr, 0, sizeof( struct ifreq ) ); + struct ifaddrs *ifa, *ifaptr; - strcpy( ifr.ifr_name, iface ); - ifr.ifr_addr.sa_family = AF_INET; - if( ioctl( fd, SIOCGIFADDR, &ifr ) == 0 ) + if( getifaddrs(&ifaptr) == -1 ) + return( 0 ); + + for( ifa = ifaptr; ifa; ifa = ifa->ifa_next ) { - struct sockaddr_in *x = (struct sockaddr_in *) &ifr.ifr_addr; - strcpy( ip, inet_ntoa( x->sin_addr ) ); - return( 1 ); + if( strcmp(ifa->ifa_name, iface) ) + continue; + + if( ifa->ifa_addr->sa_family != AF_INET + && ifa->ifa_addr->sa_family != AF_INET6 ) + continue; + + /* We have IPv4 or IPv6 address in 'ifa'. */ + if( getnameinfo(ifa->ifa_addr, + (ifa->ifa_addr->sa_family == AF_INET6) + ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in), + ip, MAX_STRING, NULL, 0, NI_NUMERICHOST) != 0 ) + continue; + + /* Success. */ + break; } - else - { + + freeifaddrs(ifaptr); + + /* Over the edge? */ + if( ifa == NULL ) return( 0 ); - } + +#if defined(DEBUG) || LOCAL_DEBUG + fprintf(stderr, "get_if_ip() succeded with %s\n", ip); +#endif + return( 1 ); }
signature.asc
Description: Digital signature

