static int reply_recv(struct dnsc *dnsc, struct mbuf *mb) { struct dns_query *q = NULL; uint32_t i, j, nv[3]; struct dnsquery dq; int err = 0; if (!dnsc || !mb) return EINVAL; dq.name = NULL; if (dns_hdr_decode(mb, &dq.hdr) || !dq.hdr.qr) { err = EBADMSG; goto out; } err = dns_dname_decode(mb, &dq.name, 0); if (err) goto out; if (mbuf_get_left(mb) < 4) { err = EBADMSG; goto out; } dq.type = ntohs(mbuf_read_u16(mb)); dq.dnsclass = ntohs(mbuf_read_u16(mb)); q = list_ledata(hash_lookup(dnsc->ht_query, hash_joaat_str_ci(dq.name), query_cmp_handler, &dq)); if (!q) { err = ENOENT; goto out; } /* try next server */ if (dq.hdr.rcode == DNS_RCODE_SRV_FAIL && q->ntx < *q->srvc) { if (!q->tc) /* try next UDP server immediately */ tmr_start(&q->tmr, 0, udp_timeout_handler, q); err = EPROTO; goto out; } nv[0] = dq.hdr.nans; nv[1] = dq.hdr.nauth; nv[2] = dq.hdr.nadd; for (i=0; i<ARRAY_SIZE(nv); i++) { for (j=0; j<nv[i]; j++) { struct dnsrr *rr = NULL; err = dns_rr_decode(mb, &rr, 0); if (err) { query_handler(q, err, NULL, NULL, NULL, NULL); mem_deref(q); goto out; } list_append(&q->rrlv[i], &rr->le_priv, rr); } } if (q->type == DNS_QTYPE_AXFR) { struct dnsrr *rrh, *rrt; rrh = list_ledata(list_head(&q->rrlv[0])); rrt = list_ledata(list_tail(&q->rrlv[0])); /* Wait for last AXFR reply with terminating SOA record */ if (dq.hdr.rcode == DNS_RCODE_OK && dq.hdr.nans > 0 && (!rrt || rrt->type != DNS_TYPE_SOA || rrh == rrt)) { DEBUG_INFO("waiting for last SOA record in reply\n"); goto out; } } query_handler(q, 0, &dq.hdr, &q->rrlv[0], &q->rrlv[1], &q->rrlv[2]); mem_deref(q); out: mem_deref(dq.name); return err; }
static void decode_dns_query(struct dns_server *srv, const struct sa *src, struct mbuf *mb) { struct list rrl = LIST_INIT; struct dnshdr hdr; struct le *le; char *qname = NULL; size_t start, end; uint16_t type, dnsclass; int err = 0; start = mb->pos; end = mb->end; if (dns_hdr_decode(mb, &hdr) || hdr.qr || hdr.nq != 1) { DEBUG_WARNING("unable to decode query header\n"); return; } err = dns_dname_decode(mb, &qname, start); if (err) { DEBUG_WARNING("unable to decode query name\n"); goto out; } if (mbuf_get_left(mb) < 4) { err = EBADMSG; DEBUG_WARNING("unable to decode query type/class\n"); goto out; } type = ntohs(mbuf_read_u16(mb)); dnsclass = ntohs(mbuf_read_u16(mb)); DEBUG_INFO("dnssrv: type=%s query-name='%s'\n", dns_rr_typename(type), qname); if (dnsclass == DNS_CLASS_IN) { dns_server_match(srv, &rrl, qname, type); } hdr.qr = true; hdr.tc = false; hdr.rcode = DNS_RCODE_OK; hdr.nq = 1; hdr.nans = list_count(&rrl); mb->pos = start; err = dns_hdr_encode(mb, &hdr); if (err) goto out; mb->pos = end; DEBUG_INFO("dnssrv: @@ found %u answers for %s\n", list_count(&rrl), qname); for (le = rrl.head; le; le = le->next) { struct dnsrr *rr = le->data; err = dns_rr_encode(mb, rr, 0, NULL, start); if (err) goto out; } mb->pos = start; (void)udp_send(srv->us, src, mb); out: list_clear(&rrl); mem_deref(qname); }