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 slightly altered
 to ease a later migration for supporting both families.
Author: Mats Erik Andersson <debian@gisladisker.se>
Forwarded: yes
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 13:47:51.000000000 +0200
@@ -25,59 +25,113 @@
 
 #include "axel.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, rc, 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. */
 	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( ( rc = getaddrinfo(local_if, NULL, &hints, &ai) ) )
+			return -1;
+
+		for( aiptr = ai; aiptr; aiptr = aiptr->ai_next )
 		{
+			if( (fd = socket(aiptr->ai_family, aiptr->ai_socktype,
+							aiptr->ai_protocol) ) < 0 )
+				continue;
+
+			if( bind( fd, (struct sockaddr *) &local, aiptr->ai_addrlen)
+					== -1 )
+			{
+				close( fd );
+				continue;
+			}
+			/* Success */
 			close( fd );
+			break;
+		} /* Looping over candidates in ai. */
+
+		if( aiptr == NULL )
+		{
+			/* Reached end of candidate list. No success! */
+			freeaddrinfo(ai);
 			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 = aiptr->ai_addrlen;
+		freeaddrinfo(ai);
+	} /* Lookup of local address for binding. */
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	snprintf(portstr, sizeof(portstr)-1, "%d", port);
+
+	if( ( rc = getaddrinfo(hostname, portstr, &hints, &ai) ) )
 	{
-		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( aiptr = ai; aiptr; aiptr = aiptr->ai_next )
+	{
+		if( ( fd = socket( aiptr->ai_family, aiptr->ai_socktype,
+						aiptr->ai_protocol) ) < 0 )
+			continue;
+
+		if( do_bind && ( bind( fd, (struct sockaddr *) &local,
+								local_addrlen) == -1 ) )
+		{
+			close( fd );
+			continue;
+		}
+								
+		if( connect( fd, aiptr->ai_addr, aiptr->ai_addrlen) < 0 )
+		{
+			close( fd );
+			continue;
+		}
+			
+		break;
+	} /* Looping over candidates in ai. */
+
+	if( aiptr == NULL )
+	{
+		/* No answer. */
+		freeaddrinfo(ai);
+		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 );
@@ -91,11 +145,16 @@ int get_if_ip( char *iface, char *ip )
 	memset( &ifr, 0, sizeof( struct ifreq ) );
 	
 	strcpy( ifr.ifr_name, iface );
-	ifr.ifr_addr.sa_family = AF_INET;
+	ifr.ifr_addr.sa_family = AF_UNSPEC;
 	if( ioctl( fd, SIOCGIFADDR, &ifr ) == 0 )
 	{
 		struct sockaddr_in *x = (struct sockaddr_in *) &ifr.ifr_addr;
-		strcpy( ip, inet_ntoa( x->sin_addr ) );
+		inet_ntop( ifr.ifr_addr.sa_family, &x->sin_addr,
+					ip, INET6_ADDRSTRLEN);	/* << MAX_STRING in axel.h */
+		close( fd );
+#if defined(DEBUG) || LOCAL_DEBUG
+		fprintf(stderr, "get_if_ip() succeded with %s\n", ip);
+#endif
 		return( 1 );
 	}
 	else
