[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

correct definition of localhost?

Hi folks,

I've run across an ipv4/ipv6 configuration issue which I think needs to have
light cast on it so we can try to resolve this in time for lenny (whatever
the right resolution actually is), in order to avoid a pile-up of
/etc/hosts-related kludges as has been known to happen before...

In response to bug #427067, the netbase maintainer made a change that adds
localhost as an alias for ::1 on new installs.  In April of this year, the
Debian Installer team followed suit, adding this line in the netcfg udeb.

The result of these changes is that since July 2007, any new lenny or sid
chroots have had two addresses listed for localhost, and since April of this
year, any new installs of lenny done using d-i have had it as well.

Now, the problem I ran into is that when I enabled the test suite in the
openldap2.3 package, the build failed mysteriously on a seemingly random set
of architectures.  The reason?  The test suite configures slapd to run on a
particular port on localhost, and the glibc "files" NSS backend
special-cases the ::1 IPv6 loopback address, so that when you request an
IPv4 address, it will map any ::1 entries to for you.  But of
course we already have an entry for localhost as, so now we end up
with duplicate addresses returned, and slapd tries to bind twice to the same
address and port!

A test program showing this behavior is attached - compile and run it on a
system with '::1 localhost' set in /etc/hosts, and you'll see
returned twice.  An alternate test case, which also works on systems with
older /etc/hosts and which I think shows the counterintuitiveness of the
nss_files special-casing, is to run "getent ahostsv4 ip6-localhost".

I don't think it's the responsibility of callers such as slapd to check that
getaddrinfo() hasn't returned duplicate entries, so I see a couple of
solutions here:

- the ::1 address should *not* be special-cased by nss_files.  I really
  can't perceive any reason why it should be special-cased in the first
  place; i.e., why should the files backend behave differently than the DNS
  backend, and why would we want names that were specifically assigned to
  ::1, including names like "ip6-loopback", to be automatically mapped to

- we should only set up a single 'localhost' entry in /etc/hosts, pointing
  at ::1, and let nss_files handle the mapping to automatically.

Are there other solutions that should be considered?  Is one of these more
acceptable than the other?  To me it seems obvious that the best choice is
to not treat the files backend specially in the first place, but I don't
know the rationale behind this special-casing either.

Steve Langasek                   Give me a lever long enough and a Free OS
Debian Developer                   to set it on, and I can move the world.
Ubuntu Developer                                    http://www.debian.org/
slangasek@ubuntu.com                                     vorlon@debian.org
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main() {
	const char *host= "localhost";
	const char *port= "9011";
	struct addrinfo hints, *res, *sai;
	struct hostent *result;
	char buf[INET6_ADDRSTRLEN];
	int buflen = sizeof(buf);
	int err;

	/* this call is just here to force glibc to set up the internal
	 * _res state, so that it sees the "multi on" that's configured
	 * by default in /etc/host.conf when we call getaddrinfo() below.
	 * Of course we could just use gethostbyname_r() itself, but
	 * getaddrinfo() is a truer test case.
	gethostbyname_r(host, NULL, NULL, 0, &result, &err);

	memset( &hints, '\0', sizeof(hints) );
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = AF_UNSPEC;

	err = getaddrinfo(host, port, &hints, &res);
	if (err) {
		perror("getaddrinfo failed");

	for (sai = res; sai != NULL; sai = sai->ai_next)
		switch (sai->ai_family) {
		case AF_INET6:
			          &((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr,
			          buf, buflen);
		case AF_INET:
			          &((struct sockaddr_in *)sai->ai_addr)->sin_addr,
			          buf, buflen);

		printf("name returned: %s\n",buf);

Reply to: