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 */