]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/dns/rfc1035.cc
SourceLayout: shuffle DNS primitives to dns/libdns.la
[thirdparty/squid.git] / src / dns / rfc1035.cc
diff --git a/src/dns/rfc1035.cc b/src/dns/rfc1035.cc
new file mode 100644 (file)
index 0000000..ba20e66
--- /dev/null
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+/*
+ * Low level DNS protocol routines
+ *
+ * KNOWN BUGS:
+ *
+ * UDP replies with TC set should be retried via TCP
+ */
+
+#include "squid.h"
+#include "dns/rfc1035.h"
+#include "dns/rfc2671.h"
+#include "util.h"
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#define RFC1035_MAXLABELSZ 63
+#define rfc1035_unpack_error 15
+
+#if 0
+#define RFC1035_UNPACK_DEBUG  fprintf(stderr, "unpack error at %s:%d\n", __FILE__,__LINE__)
+#else
+#define RFC1035_UNPACK_DEBUG  (void)0
+#endif
+
+/*
+ * rfc1035HeaderPack()
+ *
+ * Packs a rfc1035_header structure into a buffer.
+ * Returns number of octets packed (should always be 12)
+ */
+int
+rfc1035HeaderPack(char *buf, size_t sz, rfc1035_message * hdr)
+{
+    int off = 0;
+    unsigned short s;
+    unsigned short t;
+    assert(sz >= 12);
+    s = htons(hdr->id);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    t = 0;
+    t |= hdr->qr << 15;
+    t |= (hdr->opcode << 11);
+    t |= (hdr->aa << 10);
+    t |= (hdr->tc << 9);
+    t |= (hdr->rd << 8);
+    t |= (hdr->ra << 7);
+    t |= hdr->rcode;
+    s = htons(t);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(hdr->qdcount);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(hdr->ancount);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(hdr->nscount);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(hdr->arcount);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    assert(off == 12);
+    return off;
+}
+
+/*
+ * rfc1035LabelPack()
+ *
+ * Packs a label into a buffer.  The format of
+ * a label is one octet specifying the number of character
+ * bytes to follow.  Labels must be smaller than 64 octets.
+ * Returns number of octets packed.
+ */
+static int
+rfc1035LabelPack(char *buf, size_t sz, const char *label)
+{
+    int off = 0;
+    size_t len = label ? strlen(label) : 0;
+    if (label)
+        assert(!strchr(label, '.'));
+    if (len > RFC1035_MAXLABELSZ)
+        len = RFC1035_MAXLABELSZ;
+    assert(sz >= len + 1);
+    *(buf + off) = (char) len;
+    off++;
+    memcpy(buf + off, label, len);
+    off += len;
+    return off;
+}
+
+/*
+ * rfc1035NamePack()
+ *
+ * Packs a name into a buffer.  Names are packed as a
+ * sequence of labels, terminated with NULL label.
+ * Note message compression is not supported here.
+ * Returns number of octets packed.
+ */
+static int
+rfc1035NamePack(char *buf, size_t sz, const char *name)
+{
+    unsigned int off = 0;
+    char *copy = xstrdup(name);
+    char *t;
+    /*
+     * NOTE: use of strtok here makes names like foo....com valid.
+     */
+    for (t = strtok(copy, "."); t; t = strtok(NULL, "."))
+        off += rfc1035LabelPack(buf + off, sz - off, t);
+    xfree(copy);
+    off += rfc1035LabelPack(buf + off, sz - off, NULL);
+    assert(off <= sz);
+    return off;
+}
+
+/*
+ * rfc1035QuestionPack()
+ *
+ * Packs a QUESTION section of a message.
+ * Returns number of octets packed.
+ */
+int
+rfc1035QuestionPack(char *buf,
+                    const size_t sz,
+                    const char *name,
+                    const unsigned short type,
+                    const unsigned short _class)
+{
+    unsigned int off = 0;
+    unsigned short s;
+    off += rfc1035NamePack(buf + off, sz - off, name);
+    s = htons(type);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(_class);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    assert(off <= sz);
+    return off;
+}
+
+/*
+ * rfc1035HeaderUnpack()
+ *
+ * Unpacks a RFC1035 message header buffer into the header fields
+ * of the rfc1035_message structure.
+ *
+ * Updates the buffer offset, which is the same as number of
+ * octects unpacked since the header starts at offset 0.
+ *
+ * Returns 0 (success) or 1 (error)
+ */
+int
+rfc1035HeaderUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_message * h)
+{
+    unsigned short s;
+    unsigned short t;
+    assert(*off == 0);
+    /*
+     * The header is 12 octets.  This is a bogus message if the size
+     * is less than that.
+     */
+    if (sz < 12)
+        return 1;
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    h->id = ntohs(s);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    t = ntohs(s);
+    h->qr = (t >> 15) & 0x01;
+    h->opcode = (t >> 11) & 0x0F;
+    h->aa = (t >> 10) & 0x01;
+    h->tc = (t >> 9) & 0x01;
+    h->rd = (t >> 8) & 0x01;
+    h->ra = (t >> 7) & 0x01;
+    /*
+     * We might want to check that the reserved 'Z' bits (6-4) are
+     * all zero as per RFC 1035.  If not the message should be
+     * rejected.
+     * NO! RFCs say ignore inbound reserved, they may be used in future.
+     *  NEW messages need to be set 0, thats all.
+     */
+    h->rcode = t & 0x0F;
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    h->qdcount = ntohs(s);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    h->ancount = ntohs(s);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    h->nscount = ntohs(s);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    h->arcount = ntohs(s);
+    assert((*off) == 12);
+    return 0;
+}
+
+/*
+ * rfc1035NameUnpack()
+ *
+ * Unpacks a Name in a message buffer into a char*.
+ * Note 'buf' points to the beginning of the whole message,
+ * 'off' points to the spot where the Name begins, and 'sz'
+ * is the size of the whole message.  'name' must be allocated
+ * by the caller.
+ *
+ * Supports the RFC1035 message compression through recursion.
+ *
+ * Updates the new buffer offset.
+ *
+ * Returns 0 (success) or 1 (error)
+ */
+static int
+rfc1035NameUnpack(const char *buf, size_t sz, unsigned int *off, unsigned short *rdlength, char *name, size_t ns, int rdepth)
+{
+    unsigned int no = 0;
+    unsigned char c;
+    size_t len;
+    assert(ns > 0);
+    do {
+        if ((*off) >= sz) {
+            RFC1035_UNPACK_DEBUG;
+            return 1;
+        }
+        c = *(buf + (*off));
+        if (c > 191) {
+            /* blasted compression */
+            unsigned short s;
+            unsigned int ptr;
+            if (rdepth > 64) {  /* infinite pointer loop */
+                RFC1035_UNPACK_DEBUG;
+                return 1;
+            }
+            memcpy(&s, buf + (*off), sizeof(s));
+            s = ntohs(s);
+            (*off) += sizeof(s);
+            /* Sanity check */
+            if ((*off) > sz) {
+                RFC1035_UNPACK_DEBUG;
+                return 1;
+            }
+            ptr = s & 0x3FFF;
+            /* Make sure the pointer is inside this message */
+            if (ptr >= sz) {
+                RFC1035_UNPACK_DEBUG;
+                return 1;
+            }
+            return rfc1035NameUnpack(buf, sz, &ptr, rdlength, name + no, ns - no, rdepth + 1);
+        } else if (c > RFC1035_MAXLABELSZ) {
+            /*
+             * "(The 10 and 01 combinations are reserved for future use.)"
+             */
+            RFC1035_UNPACK_DEBUG;
+            return 1;
+        } else {
+            (*off)++;
+            len = (size_t) c;
+            if (len == 0)
+                break;
+            if (len > (ns - no - 1)) {  /* label won't fit */
+                RFC1035_UNPACK_DEBUG;
+                return 1;
+            }
+            if ((*off) + len >= sz) {   /* message is too short */
+                RFC1035_UNPACK_DEBUG;
+                return 1;
+            }
+            memcpy(name + no, buf + (*off), len);
+            (*off) += len;
+            no += len;
+            *(name + (no++)) = '.';
+            if (rdlength)
+                *rdlength += len + 1;
+        }
+    } while (c > 0 && no < ns);
+    if (no)
+        *(name + no - 1) = '\0';
+    else
+        *name = '\0';
+    /* make sure we didn't allow someone to overflow the name buffer */
+    assert(no <= ns);
+    return 0;
+}
+
+/*
+ * rfc1035RRPack()
+ *
+ * Packs a RFC1035 Resource Record into a message buffer from 'RR'.
+ * The caller must allocate and free RR->rdata and RR->name!
+ *
+ * Updates the new message buffer.
+ *
+ * Returns the number of bytes added to the buffer or 0 for error.
+ */
+int
+rfc1035RRPack(char *buf, const size_t sz, const rfc1035_rr * RR)
+{
+    unsigned int off;
+    uint16_t s;
+    uint32_t i;
+
+    off = rfc1035NamePack(buf, sz, RR->name);
+
+    /*
+     * Make sure the remaining message has enough octets for the
+     * rest of the RR fields.
+     */
+    if ((off + sizeof(s)*3 + sizeof(i) + RR->rdlength) > sz) {
+        return 0;
+    }
+    s = htons(RR->type);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    s = htons(RR->_class);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    i = htonl(RR->ttl);
+    memcpy(buf + off, &i, sizeof(i));
+    off += sizeof(i);
+    s = htons(RR->rdlength);
+    memcpy(buf + off, &s, sizeof(s));
+    off += sizeof(s);
+    memcpy(buf + off, &(RR->rdata), RR->rdlength);
+    off += RR->rdlength;
+    assert(off <= sz);
+    return off;
+}
+
+/*
+ * rfc1035RRUnpack()
+ *
+ * Unpacks a RFC1035 Resource Record into 'RR' from a message buffer.
+ * The caller must free RR->rdata!
+ *
+ * Updates the new message buffer offset.
+ *
+ * Returns 0 (success) or 1 (error)
+ */
+static int
+rfc1035RRUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_rr * RR)
+{
+    unsigned short s;
+    unsigned int i;
+    unsigned short rdlength;
+    unsigned int rdata_off;
+    if (rfc1035NameUnpack(buf, sz, off, NULL, RR->name, RFC1035_MAXHOSTNAMESZ, 0)) {
+        RFC1035_UNPACK_DEBUG;
+        memset(RR, '\0', sizeof(*RR));
+        return 1;
+    }
+    /*
+     * Make sure the remaining message has enough octets for the
+     * rest of the RR fields.
+     */
+    if ((*off) + 10 > sz) {
+        RFC1035_UNPACK_DEBUG;
+        memset(RR, '\0', sizeof(*RR));
+        return 1;
+    }
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    RR->type = ntohs(s);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    RR->_class = ntohs(s);
+    memcpy(&i, buf + (*off), sizeof(i));
+    (*off) += sizeof(i);
+    RR->ttl = ntohl(i);
+    memcpy(&s, buf + (*off), sizeof(s));
+    (*off) += sizeof(s);
+    rdlength = ntohs(s);
+    if ((*off) + rdlength > sz) {
+        /*
+         * We got a truncated packet.  'dnscache' truncates UDP
+         * replies at 512 octets, as per RFC 1035.
+         */
+        RFC1035_UNPACK_DEBUG;
+        memset(RR, '\0', sizeof(*RR));
+        return 1;
+    }
+    RR->rdlength = rdlength;
+    switch (RR->type) {
+#if DNS_CNAME
+    case RFC1035_TYPE_CNAME:
+#endif
+    case RFC1035_TYPE_PTR:
+        RR->rdata = (char*)xmalloc(RFC1035_MAXHOSTNAMESZ);
+        rdata_off = *off;
+        RR->rdlength = 0;   /* Filled in by rfc1035NameUnpack */
+        if (rfc1035NameUnpack(buf, sz, &rdata_off, &RR->rdlength, RR->rdata, RFC1035_MAXHOSTNAMESZ, 0)) {
+            RFC1035_UNPACK_DEBUG;
+            return 1;
+        }
+        if (rdata_off > ((*off) + rdlength)) {
+            /*
+             * This probably doesn't happen for valid packets, but
+             * I want to make sure that NameUnpack doesn't go beyond
+             * the RDATA area.
+             */
+            RFC1035_UNPACK_DEBUG;
+            xfree(RR->rdata);
+            memset(RR, '\0', sizeof(*RR));
+            return 1;
+        }
+        break;
+    case RFC1035_TYPE_A:
+    default:
+        RR->rdata = (char*)xmalloc(rdlength);
+        memcpy(RR->rdata, buf + (*off), rdlength);
+        break;
+    }
+    (*off) += rdlength;
+    assert((*off) <= sz);
+    return 0;
+}
+
+const char *
+rfc1035ErrorMessage(int n)
+{
+    if (n < 0)
+        n = -n;
+    switch (n) {
+    case 0:
+        return "No error condition";
+        break;
+    case 1:
+        return "Format Error: The name server was "
+               "unable to interpret the query.";
+        break;
+    case 2:
+        return "Server Failure: The name server was "
+               "unable to process this query.";
+        break;
+    case 3:
+        return "Name Error: The domain name does "
+               "not exist.";
+        break;
+    case 4:
+        return "Not Implemented: The name server does "
+               "not support the requested kind of query.";
+        break;
+    case 5:
+        return "Refused: The name server refuses to "
+               "perform the specified operation.";
+        break;
+    case rfc1035_unpack_error:
+        return "The DNS reply message is corrupt or could "
+               "not be safely parsed.";
+        break;
+    default:
+        return "Unknown Error";
+        break;
+    }
+}
+
+void
+rfc1035RRDestroy(rfc1035_rr ** rr, int n)
+{
+    if (*rr == NULL) {
+        return;
+    }
+
+    while (n-- > 0) {
+        if ((*rr)[n].rdata)
+            xfree((*rr)[n].rdata);
+    }
+    xfree(*rr);
+    *rr = NULL;
+}
+
+/*
+ * rfc1035QueryUnpack()
+ *
+ * Unpacks a RFC1035 Query Record into 'query' from a message buffer.
+ *
+ * Updates the new message buffer offset.
+ *
+ * Returns 0 (success) or 1 (error)
+ */
+static int
+rfc1035QueryUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_query * query)
+{
+    unsigned short s;
+    if (rfc1035NameUnpack(buf, sz, off, NULL, query->name, RFC1035_MAXHOSTNAMESZ, 0)) {
+        RFC1035_UNPACK_DEBUG;
+        memset(query, '\0', sizeof(*query));
+        return 1;
+    }
+    if (*off + 4 > sz) {
+        RFC1035_UNPACK_DEBUG;
+        memset(query, '\0', sizeof(*query));
+        return 1;
+    }
+    memcpy(&s, buf + *off, 2);
+    *off += 2;
+    query->qtype = ntohs(s);
+    memcpy(&s, buf + *off, 2);
+    *off += 2;
+    query->qclass = ntohs(s);
+    return 0;
+}
+
+void
+rfc1035MessageDestroy(rfc1035_message ** msg)
+{
+    if (!*msg)
+        return;
+    if ((*msg)->query)
+        xfree((*msg)->query);
+    if ((*msg)->answer)
+        rfc1035RRDestroy(&(*msg)->answer, (*msg)->ancount);
+    xfree(*msg);
+    *msg = NULL;
+}
+
+/*
+ * rfc1035QueryCompare()
+ *
+ * Compares two rfc1035_query entries
+ *
+ * Returns 0 (equal) or !=0 (different)
+ */
+int
+rfc1035QueryCompare(const rfc1035_query * a, const rfc1035_query * b)
+{
+    size_t la, lb;
+    if (a->qtype != b->qtype)
+        return 1;
+    if (a->qclass != b->qclass)
+        return 1;
+    la = strlen(a->name);
+    lb = strlen(b->name);
+    if (la != lb) {
+        /* Trim root label(s) */
+        while (la > 0 && a->name[la - 1] == '.')
+            la--;
+        while (lb > 0 && b->name[lb - 1] == '.')
+            lb--;
+    }
+    if (la != lb)
+        return 1;
+
+    return strncasecmp(a->name, b->name, la);
+}
+
+/*
+ * rfc1035MessageUnpack()
+ *
+ * Takes the contents of a DNS reply and fills in an array
+ * of resource record structures.  The records array is allocated
+ * here, and should be freed by calling rfc1035RRDestroy().
+ *
+ * Returns number of records unpacked, zero if DNS reply indicates
+ * zero answers, or an error number < 0.
+ */
+
+int
+rfc1035MessageUnpack(const char *buf,
+                     size_t sz,
+                     rfc1035_message ** answer)
+{
+    unsigned int off = 0;
+    unsigned int i, j;
+    unsigned int nr = 0;
+    rfc1035_message *msg = NULL;
+    rfc1035_rr *recs = NULL;
+    rfc1035_query *querys = NULL;
+    msg = (rfc1035_message*)xcalloc(1, sizeof(*msg));
+    if (rfc1035HeaderUnpack(buf + off, sz - off, &off, msg)) {
+        RFC1035_UNPACK_DEBUG;
+        xfree(msg);
+        return -rfc1035_unpack_error;
+    }
+    i = (unsigned int) msg->qdcount;
+    if (i != 1) {
+        /* This can not be an answer to our queries.. */
+        RFC1035_UNPACK_DEBUG;
+        xfree(msg);
+        return -rfc1035_unpack_error;
+    }
+    querys = msg->query = (rfc1035_query*)xcalloc(i, sizeof(*querys));
+    for (j = 0; j < i; j++) {
+        if (rfc1035QueryUnpack(buf, sz, &off, &querys[j])) {
+            RFC1035_UNPACK_DEBUG;
+            rfc1035MessageDestroy(&msg);
+            return -rfc1035_unpack_error;
+        }
+    }
+    *answer = msg;
+    if (msg->rcode) {
+        RFC1035_UNPACK_DEBUG;
+        return -msg->rcode;
+    }
+    if (msg->ancount == 0)
+        return 0;
+    i = (unsigned int) msg->ancount;
+    recs = msg->answer = (rfc1035_rr*)xcalloc(i, sizeof(*recs));
+    for (j = 0; j < i; j++) {
+        if (off >= sz) {    /* corrupt packet */
+            RFC1035_UNPACK_DEBUG;
+            break;
+        }
+        if (rfc1035RRUnpack(buf, sz, &off, &recs[j])) {     /* corrupt RR */
+            RFC1035_UNPACK_DEBUG;
+            break;
+        }
+        nr++;
+    }
+    if (nr == 0) {
+        /*
+         * we expected to unpack some answers (ancount != 0), but
+         * didn't actually get any.
+         */
+        rfc1035MessageDestroy(&msg);
+        *answer = NULL;
+        return -rfc1035_unpack_error;
+    }
+    return nr;
+}
+
+/*
+ * rfc1035BuildAQuery()
+ *
+ * Builds a message buffer with a QUESTION to lookup A records
+ * for a hostname.  Caller must allocate 'buf' which should
+ * probably be at least 512 octets.  The 'szp' initially
+ * specifies the size of the buffer, on return it contains
+ * the size of the message (i.e. how much to write).
+ * Returns the size of the query
+ */
+ssize_t
+rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
+{
+    static rfc1035_message h;
+    size_t offset = 0;
+    memset(&h, '\0', sizeof(h));
+    h.id = qid;
+    h.qr = 0;
+    h.rd = 1;
+    h.opcode = 0;       /* QUERY */
+    h.qdcount = (unsigned int) 1;
+    h.arcount = (edns_sz > 0 ? 1 : 0);
+    offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
+    offset += rfc1035QuestionPack(buf + offset,
+                                  sz - offset,
+                                  hostname,
+                                  RFC1035_TYPE_A,
+                                  RFC1035_CLASS_IN);
+    if (edns_sz > 0)
+        offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
+    if (query) {
+        query->qtype = RFC1035_TYPE_A;
+        query->qclass = RFC1035_CLASS_IN;
+        xstrncpy(query->name, hostname, sizeof(query->name));
+    }
+    assert(offset <= sz);
+    return offset;
+}
+
+/*
+ * rfc1035BuildPTRQuery()
+ *
+ * Builds a message buffer with a QUESTION to lookup PTR records
+ * for an address.  Caller must allocate 'buf' which should
+ * probably be at least 512 octets.  The 'szp' initially
+ * specifies the size of the buffer, on return it contains
+ * the size of the message (i.e. how much to write).
+ * Returns the size of the query
+ */
+ssize_t
+rfc1035BuildPTRQuery(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query, ssize_t edns_sz)
+{
+    static rfc1035_message h;
+    size_t offset = 0;
+    static char rev[32];
+    unsigned int i;
+    memset(&h, '\0', sizeof(h));
+    i = (unsigned int) ntohl(addr.s_addr);
+    snprintf(rev, 32, "%u.%u.%u.%u.in-addr.arpa.",
+             i & 255,
+             (i >> 8) & 255,
+             (i >> 16) & 255,
+             (i >> 24) & 255);
+    h.id = qid;
+    h.qr = 0;
+    h.rd = 1;
+    h.opcode = 0;       /* QUERY */
+    h.qdcount = (unsigned int) 1;
+    h.arcount = (edns_sz > 0 ? 1 : 0);
+    offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
+    offset += rfc1035QuestionPack(buf + offset,
+                                  sz - offset,
+                                  rev,
+                                  RFC1035_TYPE_PTR,
+                                  RFC1035_CLASS_IN);
+    if (edns_sz > 0)
+        offset += rfc2671RROptPack(buf + offset, sz - offset, edns_sz);
+    if (query) {
+        query->qtype = RFC1035_TYPE_PTR;
+        query->qclass = RFC1035_CLASS_IN;
+        xstrncpy(query->name, rev, sizeof(query->name));
+    }
+    assert(offset <= sz);
+    return offset;
+}
+
+/*
+ * We're going to retry a former query, but we
+ * just need a new ID for it.  Lucky for us ID
+ * is the first field in the message buffer.
+ */
+void
+rfc1035SetQueryID(char *buf, unsigned short qid)
+{
+    unsigned short s = htons(qid);
+    memcpy(buf, &s, sizeof(s));
+}
+
+#if DRIVER
+#include <sys/socket.h>
+int
+main(int argc, char *argv[])
+{
+    char input[SQUID_DNS_BUFSZ];
+    char buf[SQUID_DNS_BUFSZ];
+    char rbuf[SQUID_DNS_BUFSZ];
+    size_t sz = SQUID_DNS_BUFSZ;
+    unsigned short sid;
+    int s;
+    int rl;
+    struct sockaddr_in S;
+    if (3 != argc) {
+        fprintf(stderr, "usage: %s ip port\n", argv[0]);
+        return 1;
+    }
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+    s = socket(PF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+        perror("socket");
+        return 1;
+    }
+    memset(&S, '\0', sizeof(S));
+    S.sin_family = AF_INET;
+    S.sin_port = htons(atoi(argv[2]));
+    S.sin_addr.s_addr = inet_addr(argv[1]);
+    while (fgets(input, RFC1035_DEFAULT_PACKET_SZ, stdin)) {
+        struct in_addr junk;
+        strtok(input, "\r\n");
+        memset(buf, '\0', RFC1035_DEFAULT_PACKET_SZ);
+        sz = RFC1035_DEFAULT_PACKET_SZ;
+        if (inet_pton(AF_INET, input, &junk)) {
+            sid = rfc1035BuildPTRQuery(junk, buf, &sz);
+        } else {
+            sid = rfc1035BuildAQuery(input, buf, &sz);
+        }
+        sendto(s, buf, sz, 0, (struct sockaddr *) &S, sizeof(S));
+        do {
+            fd_set R;
+            struct timeval to;
+            FD_ZERO(&R);
+            FD_SET(s, &R);
+            to.tv_sec = 10;
+            to.tv_usec = 0;
+            rl = select(s + 1, &R, NULL, NULL, &to);
+        } while (0);
+        if (rl < 1) {
+            printf("TIMEOUT\n");
+            continue;
+        }
+        memset(rbuf, '\0', RFC1035_DEFAULT_PACKET_SZ);
+        rl = recv(s, rbuf, RFC1035_DEFAULT_PACKET_SZ, 0);
+        {
+            unsigned short rid = 0;
+            int i;
+            int n;
+            rfc1035_rr *answers = NULL;
+            n = rfc1035AnswersUnpack(rbuf,
+                                     rl,
+                                     &answers,
+                                     &rid);
+            if (n < 0) {
+                printf("ERROR %d\n", -n);
+            } else if (rid != sid) {
+                printf("ERROR, ID mismatch (%#hx, %#hx)\n", sid, rid);
+            } else {
+                printf("%d answers\n", n);
+                for (i = 0; i < n; i++) {
+                    if (answers[i].type == RFC1035_TYPE_A) {
+                        struct in_addr a;
+                        char ipa_str[sizeof(a)];
+                        memcpy(&a, answers[i].rdata, 4);
+                        printf("A\t%d\t%s\n", answers[i].ttl, inet_ntop(AF_INET,&a,tmp,sizeof(a)));
+                    } else if (answers[i].type == RFC1035_TYPE_PTR) {
+                        char ptr[128];
+                        strncpy(ptr, answers[i].rdata, answers[i].rdlength);
+                        printf("PTR\t%d\t%s\n", answers[i].ttl, ptr);
+                    } else {
+                        fprintf(stderr, "can't print answer type %d\n",
+                                (int) answers[i].type);
+                    }
+                }
+            }
+        }
+    }
+    return 0;
+}
+#endif
+