Bug 25222

Summary: getaddrinfo("localhost") returns 2x 127.0.0.1
Product: glibc Reporter: Tim Rühsen <tim.ruehsen>
Component: nssAssignee: Not yet assigned to anyone <unassigned>
Status: UNCONFIRMED ---    
Severity: enhancement Flags: fweimer: security-
Priority: P2    
Version: 2.31   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: [getaddrinfo] ignore ::1 in /etc/hosts if asked for AF_INET localhost

Description Tim Rühsen 2019-11-23 17:05:39 UTC
Created attachment 12090 [details]
[getaddrinfo] ignore ::1 in /etc/hosts if asked for AF_INET localhost

Given that /etc/hosts contains ::1 and 127.0.0.1 for localhost, getaddrinfo() returns two identical entries 127.0.0.1, even if the hints are set to AF_INET.

Example /etc/hosts
127.0.0.1  localhost localhost4
::1        localhost localhost6

The code for this was introduced in 2007 by commit 11bf311edc7.

There is an explicit code branch executed if AF_INET has been requested and a IPv6 address has been found (makes sense with V4MAPPED). But if IPv4 mapping is not used and the IP is ::1, the code simply adds an entry with 127.0.0.1. This creates the second 127.0.0.1 entry mentioned above.

Removing the code (attached patch), the glibc test suite results do not change here.

C example code
###
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>

int main(void)
{
        struct addrinfo hints = {
                .ai_family = AF_INET,
                .ai_socktype = SOCK_STREAM,
                .ai_flags = AI_ADDRCONFIG
        };
        const char *host = "localhost";
        struct addrinfo *addrinfo;
        int rc;

        if ((rc = getaddrinfo(host, NULL, &hints, &addrinfo)) == 0) {
                for (struct addrinfo *ai = addrinfo; ai; ai = ai->ai_next) {
                        char adr[NI_MAXHOST];
                        int rc;

                        if ((rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, adr, sizeof(adr), NULL, 0, NI_NUMERICHOST)) == 0)
                                printf("%s\n", adr);
                        else
                                printf("??? (%s)\n", gai_strerror(rc));
                }
        } else
                printf("rc = %d %s, errno %d\n", rc, gai_strerror(rc), errno);

        return 0;
}
###