Hi,

I've come up the the following patch to support both IPv6 and IPv4 via
getaddrinfo(). Following the example on the getaddrinfo() manual page,
when multiple IPv6/IPv4 lookup results are returned it walks through
that list, attempting to connect until it reaches a successful one. It
only uses the local_if information for IPv4 connections, as IPv6 source
address selection is influenced via /etc/gai.conf, which is consulted
during the call to getaddrinfo().

Regards,
Mark.


diff --git a/tcp.c b/tcp.c
index 1551c23..d2bbd84 100644
--- a/tcp.c
+++ b/tcp.c
@@ -28,59 +28,73 @@
 /* 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 sockaddr_in local_addr;
+       const int portstr_len = 10;
+       char portstr[portstr_len];
+       struct addrinfo ai_hints;
+       struct addrinfo *gai_results, *gai_result;
+       int ret;
+       int sock_fd = -1;
 
-#ifdef DEBUG
-       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 (local_if && *local_if) {
+               local_addr.sin_family = AF_INET;
+               local_addr.sin_port = 0;
+               local_addr.sin_addr.s_addr = inet_addr(local_if);
        }
-       if( !host || !host->h_name || !*host->h_name )
-               return( -1 );
-       
-       if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
-               return( -1 );
-       
-       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 )
-               {
-                       close( fd );
-                       return( -1 );
-               }
+
+       snprintf(portstr, portstr_len, "%d", port);
+
+       memset(&ai_hints, 0, sizeof(ai_hints));
+       ai_hints.ai_family = AF_UNSPEC;
+       ai_hints.ai_socktype = SOCK_STREAM;
+       ai_hints.ai_flags = AI_ADDRCONFIG;
+       ai_hints.ai_protocol = 0;
+
+       ret = getaddrinfo(hostname, portstr, &ai_hints, &gai_results);
+       if (ret != 0) {
+               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 )
-       {
-               close( fd );
-               return( -1 );
+
+       gai_result = gai_results;
+       sock_fd = -1;
+       while ((sock_fd == -1) && (gai_result != NULL)) {
+
+               sock_fd = socket(gai_result->ai_family,
+                       gai_result->ai_socktype, gai_result->ai_protocol);
+
+               if (sock_fd != -1) {
+
+                       if (gai_result->ai_family == AF_INET) {
+                               if (local_if && *local_if) {
+                                       ret = bind(sock_fd,
+                                               (struct sockaddr *) &local_addr,
+                                               sizeof(local_addr));
+                                       if (ret == -1) {
+                                               close(sock_fd);
+                                               sock_fd = -1;
+                                               gai_result =
+                                                       gai_result->ai_next;
+                                       }
+                               }
+                       }
+
+                       if (sock_fd != -1) {
+                               ret = connect(sock_fd, gai_result->ai_addr,
+                                               gai_result->ai_addrlen);
+                               if (ret == -1) {
+                                       close(sock_fd);
+                                       sock_fd = -1;
+                                       gai_result = gai_result->ai_next;
+                               }
+                       }
+               }
        }
-       
-#ifdef DEBUG
-       getsockname( fd, &local, &i );
-       fprintf( stderr, "%i\n", ntohs( local.sin_port ) );
-#endif
-       
-       return( fd );
+
+       freeaddrinfo(gai_results);
+
+       return sock_fd;
+
 }
 
 int get_if_ip( char *iface, char *ip )



-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to