-static adns_status pap_addr(const parseinfo *pai, int rrty, size_t rrsz,
- int *cbyte_io, int max, adns_rr_addr *storeto) {
- const byte *dgram= pai->dgram;
- int af, addrlen, salen;
- const void *oaddr= dgram + *cbyte_io;
- int avail= max - *cbyte_io;
- int step= -1;
- void *addrp= 0;
-
- switch (rrty) {
- case adns_r_a:
- af= AF_INET; addrlen= 4;
- addrp= &storeto->addr.inet.sin_addr;
- salen= sizeof(storeto->addr.inet);
- break;
+/* About CNAME handling in addr queries.
+ *
+ * A user-level addr query is translated into a number of protocol-level
+ * queries, and its job is to reassemble the results. This gets tricky if
+ * the answers aren't consistent. In particular, if the answers report
+ * inconsistent indirection via CNAME records (e.g., different CNAMEs, or
+ * some indirect via a CNAME, and some don't) then we have trouble.
+ *
+ * Once we've received an answer, even if it was NODATA, we set
+ * adns__qf_addr_answer on the parent query. This will let us detect a
+ * conflict between a no-CNAME-with-NODATA reply and a subsequent CNAME.
+ *
+ * If we detect a conflict of any kind, then at least one answer came back
+ * with a CNAME record, so we pick the first such answer (somewhat
+ * arbitrarily) as being the `right' canonical name, and set this in the
+ * parent query's answer->cname slot. We discard address records from the
+ * wrong name. And finally we cancel the outstanding child queries, and
+ * resubmit address queries for the address families we don't yet have, with
+ * adns__qf_addr_cname set so that we know that we're in the fixup state.
+ */
+
+static adns_status pap_addr(const parseinfo *pai, int in_rrty, size_t out_rrsz,
+ int *cbyte_io, int cbyte_max, adns_rr_addr *out) {
+ int in_addrlen;
+ int out_af, out_salen;
+ struct in6_addr v6map;
+
+ const void *use_addr= pai->dgram + *cbyte_io;
+
+ switch (in_rrty) {
+ case adns_r_a: in_addrlen= 4; out_af= AF_INET; break;
+ case adns_r_aaaa: in_addrlen= 16; out_af= AF_INET6; break;
+ default: abort();
+ }
+
+ if ((*cbyte_io + in_addrlen) != cbyte_max) return adns_s_invaliddata;
+
+ if (out_af==AF_INET &&
+ (pai->qu->flags & adns_qf_ipv6_mapv4) &&
+ (pai->qu->answer->type & adns__qtf_bigaddr)) {
+ memset(v6map.s6_addr + 0, 0x00, 10);
+ memset(v6map.s6_addr + 10, 0xff, 2);
+ memcpy(v6map.s6_addr + 12, use_addr, 4);
+ use_addr= v6map.s6_addr;
+ out_af= AF_INET6;
+ }
+
+ switch (out_af) {
+ case AF_INET: out_salen= sizeof(out->addr.inet); break;
+ case AF_INET6: out_salen= sizeof(out->addr.inet6); break;
+ default: abort();