static void dname_append(struct hash *ht_dname, const char *name, size_t pos) { struct dname *dn; if (!ht_dname || pos > OFFSET_MASK || !*name) return; dn = mem_zalloc(sizeof(*dn), destructor); if (!dn) return; if (str_dup(&dn->name, name)) { mem_deref(dn); return; } hash_append(ht_dname, hash_joaat_str_ci(name), &dn->he, dn); dn->pos = pos; }
static int query(struct dns_query **qp, struct dnsc *dnsc, uint8_t opcode, const char *name, uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr, int proto, const struct sa *srvv, const uint32_t *srvc, bool aa, bool rd, dns_query_h *qh, void *arg) { struct dns_query *q = NULL; struct dnshdr hdr; int err = 0; uint32_t i; if (!dnsc || !name || !srvv || !srvc || !(*srvc)) return EINVAL; if (DNS_QTYPE_AXFR == type) proto = IPPROTO_TCP; q = mem_zalloc(sizeof(*q), query_destructor); if (!q) goto nmerr; hash_append(dnsc->ht_query, hash_joaat_str_ci(name), &q->le, q); tmr_init(&q->tmr); mbuf_init(&q->mb); for (i=0; i<ARRAY_SIZE(q->rrlv); i++) list_init(&q->rrlv[i]); err = str_dup(&q->name, name); if (err) goto error; q->srvv = srvv; q->srvc = srvc; q->id = rand_u16(); q->type = type; q->opcode = opcode; q->dnsclass = dnsclass; q->dnsc = dnsc; memset(&hdr, 0, sizeof(hdr)); hdr.id = q->id; hdr.opcode = q->opcode; hdr.aa = aa; hdr.rd = rd; hdr.nq = 1; hdr.nans = ans_rr ? 1 : 0; if (proto == IPPROTO_TCP) q->mb.pos += 2; err = dns_hdr_encode(&q->mb, &hdr); if (err) goto error; err = dns_dname_encode(&q->mb, name, NULL, 0, false); if (err) goto error; err |= mbuf_write_u16(&q->mb, htons(type)); err |= mbuf_write_u16(&q->mb, htons(dnsclass)); if (err) goto error; if (ans_rr) { err = dns_rr_encode(&q->mb, ans_rr, 0, NULL, 0); if (err) goto error; } q->qh = qh; q->arg = arg; switch (proto) { case IPPROTO_TCP: q->mb.pos = 0; (void)mbuf_write_u16(&q->mb, htons(q->mb.end - 2)); err = send_tcp(q); if (err) goto error; tmr_start(&q->tmr, 60 * 1000, tcp_timeout_handler, q); break; case IPPROTO_UDP: err = send_udp(q); if (err) goto error; tmr_start(&q->tmr, 500, udp_timeout_handler, q); break; default: err = EPROTONOSUPPORT; goto error; } if (qp) { q->qp = qp; *qp = q; } return 0; nmerr: err = ENOMEM; error: mem_deref(q); return err; }
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 inline struct dname *dname_lookup(struct hash *ht_dname, const char *name) { return list_ledata(hash_lookup(ht_dname, hash_joaat_str_ci(name), lookup_handler, (void *)name)); }