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 );
 }

Attachment: signature.asc
Description: Digital signature

Reply via email to