dns_transmit bug

Analysis

dns_transmit implements a state-machine for sending and retrying DNS queries over UDP and TCP. tcpstate == 2 indicates that the file descriptor d->s1 - 1 is a TCP socket connected to a DNS cache and that d->pos bytes of the DNS query have been sent over that socket [see comments on lines 299–300 of dns_transmit.c].

The state-machine arrives at tcpstate == 2 one of two ways. connect(2) can return -1/EINPROGRESS, and the state-machine will enter tcpstate == 1, which will later transition to tcpstate == 2 if the connection succeeds [lines 127–175,286–295]. Alternatively, connect(2) may succeed immediately and the state-machine will transition directly to tcpstate == 2 [lines 168–171].

When dns_transmit changes from tcpstate == 1 to tcpstate == 2, it will reset d->pos to 0. However, it does not do this when connect(2) succeeds immediately.

Patch

--- dns_transmit.c.orig	2008-01-08 13:58:33.000000000 -0800
+++ dns_transmit.c	2008-01-08 13:57:56.000000000 -0800
@@ -166,6 +166,7 @@
       taia_uint(&d->deadline,10);
       taia_add(&d->deadline,&d->deadline,&now);
       if (socket_connect4(d->s1 - 1,ip,53) == 0) {
+        d->pos = 0;
         d->tcpstate = 2;
         return 0;
       }

Affected systems

This bug only affects operating systems where connect(2) can return 0 when given a file descriptor for a non-blocking TCP socket. I have confirmed that this affects OpenBSD 4.0 and 4.2 when connecting to local TCP address/ports. Brief experimentation shows Linux 2.6.13 always returns -1/EINPROGRESS.

Example

On an OpenBSD 4.2 system with an unpatched installation of djbdns-1.05 and running dnscache according to http://cr.yp.to/djbdns/run-cache-x.html:

$ date                                                      
Wed Jan  9 15:56:32 PST 2008
$ DNSCACHEIP=`cat /etc/dnscache/env/IP` dnstxt aol.com | wc -c
467
$ DNSCACHEIP=`cat /etc/dnscache/env/IP` dnstxt aol.com aol.com
dnstxt: fatal: unable to find TXT records for aol.com: bad address

With the patch above applied:

$ DNSCACHEIP=`cat /etc/dnscache/env/IP` dnstxt aol.com aol.com   
v=spf1 ip4:152.163.225.0/24 ip4:205.188.139.0/24 ip4:205.188.144.0/24 ip4:205.188.156.0/23 ip4:205.188.159.0/24 ip4:64.12.136.0/23 ip4:64.12.138.0/24 ip4:64.12.143.99/32 ip4:64.12.143.100/32 ip4:64.12.143.101/32 ptr:mx.aol.com ?allspf2.0/pra ip4:152.163.225.0/24 ip4:205.188.139.0/24 ip4:205.188.144.0/24 ip4:205.188.156.0/23 ip4:205.188.159.0/24 ip4:64.12.136.0/23 ip4:64.12.138.0/24 ip4:64.12.143.99/32 ip4:64.12.143.100/32 ip4:64.12.143.101/32 ptr:mx.aol.com ?all
v=spf1 ip4:152.163.225.0/24 ip4:205.188.139.0/24 ip4:205.188.144.0/24 ip4:205.188.156.0/23 ip4:205.188.159.0/24 ip4:64.12.136.0/23 ip4:64.12.138.0/24 ip4:64.12.143.99/32 ip4:64.12.143.100/32 ip4:64.12.143.101/32 ptr:mx.aol.com ?allspf2.0/pra ip4:152.163.225.0/24 ip4:205.188.139.0/24 ip4:205.188.144.0/24 ip4:205.188.156.0/23 ip4:205.188.159.0/24 ip4:64.12.136.0/23 ip4:64.12.138.0/24 ip4:64.12.143.99/32 ip4:64.12.143.100/32 ip4:64.12.143.101/32 ptr:mx.aol.com ?all