Hi Samuel and the Hurd developers,

I have recently updated the httpfs translator to support IPv6 and
modernize its core networking logic. Since httpfs previously relied on
the obsolete gethostbyname and fixed-size IPv4 structures, it was
unable to handle modern dual-stack environments or IPv6-only hosts.

Summary of Changes:
    - Protocol-Agnostic Resolution: Replaced gethostbyname with
getaddrinfo in lookup_host. This allows the translator to
transparently handle IPv4, IPv6, and DNS hostnames.

    - Storage Modernization: Migrated from struct sockaddr_in to
struct sockaddr_storage to ensure sufficient memory alignment and
space for 128-bit addresses, preventing potential buffer overflows
when handling IPv6 records.

    - Unified Connection Logic: Refactored open_connection to unify
proxy and direct connection paths. The logic now determines the target
host first and then performs a generic resolution, simplifying the
code and removing the need for inet_aton.

    - IPv6 URL Syntax: Added support for bracketed IPv6 literals
(e.g., http://[::1]/) by implementing a parser in lookup_host that
strips brackets before resolution while preserving them for the HTTP
Host header.

Verified Scenarios:
    1. Native IPv4: Standard connections to legacy IPv4-only hosts.
    2. Native IPv6: Connections to IPv6-only hosts and bracketed
literal addresses.
    3. Proxy Support: Verified that the unified logic correctly
forwards requests through an IPv4/IPv6 proxy while maintaining the
correct destination in the Host header.

These changes provide a solid foundation for further improvements,
such as adding HTTPS support.

I would appreciate any feedback or comments on these changes.

Best regards,

Gianluca
Index: httpfs/http.c
===================================================================
--- httpfs.orig/http.c
+++ httpfs/http.c
@@ -34,14 +34,59 @@
 #include <hurd/hurd_types.h>
 #include <hurd/netfs.h>
 
-/* do a DNS lookup for NAME and store result in *ENT */
-error_t lookup_host (char *url, struct hostent **ent) 
+/* do a DNS lookup for HOST and store result in *DEST */
+error_t lookup_host (const char *host, struct sockaddr_storage *dest, socklen_t *dest_len) 
 {
-       if ( (*ent = gethostbyname(url)) == NULL )
-       {
-               fprintf(stderr,"wrong host name\n");
+       struct addrinfo hints;
+       struct addrinfo *res;
+       int s;
+
+       /* Buffer that holds a clean host, that is a host without square brackets in case of a IPv6 address */
+       char clean_host[256];
+       const char *host_ptr = host;
+
+       if (!host || !dest || !dest_len)
                return EINVAL;
+
+       /* IPv6 syntax transformation: e.g. [2001:db8::1] -> 2001:db8::1
+        * because getaddrinfo does not accept square brackets, but URLs do.
+        */
+       if (host[0] == '[') {
+               const char *end = strchr (host, ']');
+               if (end) {
+                       size_t host_len = end - host - 1;
+                       if (host_len >= sizeof (clean_host)) return ENAMETOOLONG;
+
+                       strncpy (clean_host, host + 1, host_len);
+                       clean_host[host_len] = '\0';
+                       host_ptr = clean_host;
+               }
        }
+
+       memset (&hints, 0, sizeof (struct addrinfo));
+       hints.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
+       hints.ai_socktype = SOCK_STREAM; /* TCP */
+       /* AI_ADDRCONFIG: Asks for AAAA record only if the host has IPv6 connectivity.
+        * AI_V4MAPPED: Maps IPv4 to IPv6 if necessary. */
+       hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
+
+       /* DNS lookup */
+       s = getaddrinfo (host_ptr, NULL, &hints, &res);
+       if (s != 0) {
+               if (debug_flag)
+                       fprintf (stderr, "DNS lookup failed for %s: %s\n", host_ptr, gai_strerror (s));
+
+               return ENOENT;
+       }
+
+       /* TODO: Shall we copy only the first result or iterate through possible multiple results */
+       if (res != NULL) {
+               memcpy (dest, res->ai_addr, res->ai_addrlen);
+               *dest_len = res->ai_addrlen;
+               dest->ss_family = res->ai_family;
+       }
+
+       freeaddrinfo (res);
        return 0;
 }
 
@@ -52,8 +97,8 @@ error_t open_connection(struct netnode *
         * head. *HEAD_LEN indicates the header length, for pruning upto that */
        
        error_t err;
-       struct hostent *hptr;
-       struct sockaddr_in dest;
+       struct sockaddr_storage server_addr;
+       socklen_t addr_len;
        ssize_t written;
        size_t towrite;
        char buffer[4096];
@@ -63,49 +108,39 @@ error_t open_connection(struct netnode *
        char delimiters0[] = " ";
        char delimiters1[] = "\n";
 
-       bzero(&dest,sizeof(dest));
-       dest.sin_family = AF_INET;
-       dest.sin_port = htons (port);
-
-       if ( !strcmp(ip_addr,"0.0.0.0") )
-       {
-               /* connection is not through a proxy server
-                * find IP addr. of remote server */
-               err = lookup_host (node->url, &hptr);
-               if (err)
-               {
-                       fprintf(stderr,"Could not find IP addr %s\n",node->url);
-                       return err;
-               }
-               dest.sin_addr = *(struct in_addr *)hptr->h_addr;
-       }
-       else
-       {
-               /* connection is through the proxy server
-                * need not find IP of remote server
-                * find IP of the proxy server */
-               if ( inet_aton(ip_addr,&dest.sin_addr) == 0 )
-               {
-                       fprintf(stderr,"Invalid IP for proxy\n");
-                       return -1;
-               }
+       /* 1. Target selection.
+        * If ip_addr (proxy global variable) is set, we use it. 
+        * Otherwise we use the node URL.
+        */
+       const char *target_host = (strcmp (ip_addr, "0.0.0.0") != 0) ? ip_addr : node->url;
+
+       /* 2. Agnostic resolution (IPv4/IPv6) */
+       if ((err = lookup_host (target_host, &server_addr, &addr_len)) != 0) {
+               fprintf (stderr, "Cannot resolve host: %s\n", target_host);
+               return err;
+       }
+
+       /* 3. Set of the port. */
+       if (server_addr.ss_family == AF_INET) {
+               ((struct sockaddr_in *)&server_addr)->sin_port = htons (port);
+       } else if (server_addr.ss_family == AF_INET6) {
+               ((struct sockaddr_in6 *)&server_addr)->sin6_port = htons (port);
        }
 
        if (debug_flag)
-               fprintf (stderr, "trying to open %s:%d/%s\n", node->url,
-                               port, node->conn_req);
+               fprintf (stderr, "Connecting to %s via %s:%d\n", node->url, target_host, port);
 
-       *fd = socket (AF_INET, SOCK_STREAM, 0);
+       /* 4. First connection: HEAD request */
+       *fd = socket (server_addr.ss_family, SOCK_STREAM, 0);
        if (*fd == -1)
        {
-               fprintf(stderr,"Socket creation error\n");
+               perror ("Socket creation error for HEAD request");
                return errno;
        }
 
-       err = connect (*fd, (struct sockaddr *)&dest, sizeof (dest));
-       if (err == -1)
-       {
-               fprintf(stderr,"Cannot connect to remote host\n");
+       if (connect (*fd, (struct sockaddr *)&server_addr, addr_len) == -1) {
+               perror ("Connection to remote host failed");
+               close (*fd);
                return errno;
        }
 
@@ -142,21 +177,20 @@ error_t open_connection(struct netnode *
        
        close(*fd);
        
+       /* 5. Second connection: GET request */
        /* Send the GET request for the url */
-       *fd = socket (AF_INET, SOCK_STREAM, 0);
+       *fd = socket (server_addr.ss_family, SOCK_STREAM, 0);
        if (*fd == -1)
        {
-               fprintf(stderr,"Socket creation error\n");
+               perror ("Socket creation error for GET request");
                return errno;
        }
 
-       err = connect (*fd, (struct sockaddr *)&dest, sizeof (dest));
-       if (err == -1)
-       {
-               fprintf(stderr,"Cannot connect to remote host\n");
+       if (connect (*fd, (struct sockaddr *)&server_addr, addr_len) == -1) {
+               perror ("Connection to remote host failed");
+               close (*fd);
                return errno;
        }
-
        towrite = strlen (node->comm_buf);
 
        /* guard against EINTR failures */
Index: httpfs/httpfs.h
===================================================================
--- httpfs.orig/httpfs.h
+++ httpfs/httpfs.h
@@ -119,8 +119,8 @@ error_t parse(struct netnode *node);
  * string */
 void extract(char *string,char *parent);
 
-/* do a DNS lookup for NAME and store result in *ENT */
-error_t lookup_host (char *url, struct hostent **ent);
+/* do a DNS lookup for HOST and store result in *DEST */
+error_t lookup_host (const char *url, struct sockaddr_storage *dest, socklen_t *dest_len);
 
 /* store the remote socket in *FD and establish the connection and send the 
  * http GET request */

Reply via email to