Subject: lib/7899: getaddrinfo() / resolver weirdness with cnames and ipv6
To: None <gnats-bugs@gnats.netbsd.org>
From: Luke Mewburn <lukem@karybdis.cs.rmit.edu.au>
List: netbsd-bugs
Date: 07/03/1999 06:22:47
>Number:         7899
>Category:       lib
>Synopsis:       getaddrinfo() / resolver weirdness with cnames and ipv6
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Jul  3 06:05:00 1999
>Last-Modified:
>Originator:     Luke Mewburn
>Organization:
ftp hackers r us
>Release:        Sat Jul  3 22:34:47 EST 1999
>Environment:
System: NetBSD karybdis.cs.rmit.edu.au 1.4D NetBSD 1.4D (LUKEM) #143: Sat Jul 3 14:31:44 EST 1999 lukem@karybdis.cs.rmit.edu.au:/echidna/netbsd/src/sys/arch/i386/compile/LUKEM i386

>Description:

whilst working on some fixes to ftp I started getting errors like:
    ftp: non-recoverable failure in name resolution: Undefined error: 0
for certain hosts.

further investigation reveals that getaddrinfo() fails for CNAME
lookups if the family is AF_UNSPEC, because AF_INET6 CNAME lookups
appear to be tried before AF_INET CNAME lookups in that function.

I wrote a little test program which basically does
    res = gethostbyname2(argv[1], AF_INET)
    printf("got inet %s", res ? res->h_name : hstrerror(h_errno));
    res = gethostbyname2(argv[1], AF_INET6)
    printf("got inet6 %s", res ? res->h_name : hstrerror(h_errno));

	~/test> ./a.out emu
	inet emu: returned emu.cs.rmit.edu.au
	inet6 emu: failed with (4) No address associated with name

	~/test> ./a.out emu
	inet proxywww: returned emu.cs.rmit.edu.au
	inet6 proxywww: failed with (3) Unknown server error

notice that:
	* the first inet6 lookup fails with (4), because there isn't an
	  AAAA record for emu
	* the second inet6 lookups fails with (3), because the CNAME
	  for proxywww points to an A record not an AAAA record.

for the second case, i whipped out gdb and did a break in
gethnamaddr.c::getanswer() and used __p_reply(answer) when the
breakpoint kicked in.

the first lookup (AF_INET CNAME) returned:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36896
    ;; flags: qr aa rd ra; Ques: 1, Ans: 2, Auth: 3, Addit: 3
    ;; QUESTIONS:
    ;;      proxywww.cs.rmit.edu.au, type = A, class = IN
    ;; ANSWERS:
    proxywww.cs.rmit.edu.au.        43200   IN      CNAME emu.cs.rmit.edu.au.
    emu.cs.rmit.edu.au.     43200   IN      A       131.170.24.43

the second lookup (AF_INET6 CNAME) returned:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36897
    ;; flags: qr aa rd ra; Ques: 1, Ans: 1, Auth: 1, Addit: 0
    ;; QUESTIONS:
    ;;      proxywww.cs.rmit.edu.au, type = AAAA, class = IN
    ;; ANSWERS:
    proxywww.cs.rmit.edu.au.        43200   IN      CNAME emu.cs.rmit.edu.au.

whilst this is probably correct from the resolver's point of view, 
gethostbyname2() or getaddrinfo() should be able to cope with this.


>How-To-Repeat:

run a program which uses getaddrinfo (e.g, ftp) and try to lookup a
hostname that is a CNAME. my nameserver isn't ipv6 aware (it's a
netbsd 1.3.3 machine), and I don't have `options inet6' in
resolv.conf.


>Fix:

there's a couple of possible solutions, but I'd rather someone with
more in depth knowledge of the resolver (hi vixie! :-) and/or ipv6
(hi itojun! :-) to comment and/or provide a better solution:
	* hack the appropriate bits of gethostbyname2 so that
	  when a cname lookup is requested by there's no data
	  returned, if qtype == AAAA or A then return NO_DATA
	  instead of NO_RECOVERY. this may break other stuff
	  though.
	* instead hack _dns_gethtbyname() in a similar fashion.
	* something else?
>Audit-Trail:
>Unformatted: