| /** |
| * @file dns/client.c DNS Client |
| * |
| * Copyright (C) 2010 Creytiv.com |
| */ |
| #include <string.h> |
| #include <re_types.h> |
| #include <re_fmt.h> |
| #include <re_mem.h> |
| #include <re_mbuf.h> |
| #include <re_list.h> |
| #include <re_hash.h> |
| #include <re_tmr.h> |
| #include <re_sa.h> |
| #include <re_udp.h> |
| #include <re_tcp.h> |
| #include <re_sys.h> |
| #include <re_dns.h> |
| |
| |
| #define DEBUG_MODULE "dnsc" |
| #define DEBUG_LEVEL 5 |
| #include <re_dbg.h> |
| |
| |
| enum { |
| NTX_MAX = 20, |
| QUERY_HASH_SIZE = 16, |
| TCP_HASH_SIZE = 2, |
| CONN_TIMEOUT = 10 * 1000, |
| IDLE_TIMEOUT = 30 * 1000, |
| SRVC_MAX = 32, |
| }; |
| |
| |
| struct tcpconn { |
| struct le le; |
| struct list ql; |
| struct tmr tmr; |
| struct sa srv; |
| struct tcp_conn *conn; |
| struct mbuf *mb; |
| bool connected; |
| uint16_t flen; |
| struct dnsc *dnsc; /* parent */ |
| }; |
| |
| |
| struct dns_query { |
| struct le le; |
| struct le le_tc; |
| struct tmr tmr; |
| struct mbuf mb; |
| struct list rrlv[3]; |
| char *name; |
| const struct sa *srvv; |
| const uint32_t *srvc; |
| struct tcpconn *tc; |
| struct dnsc *dnsc; /* parent */ |
| struct dns_query **qp; /* app ref */ |
| uint32_t ntx; |
| uint16_t id; |
| uint16_t type; |
| uint16_t dnsclass; |
| uint8_t opcode; |
| dns_query_h *qh; |
| void *arg; |
| }; |
| |
| |
| struct dnsquery { |
| struct dnshdr hdr; |
| char *name; |
| uint16_t type; |
| uint16_t dnsclass; |
| }; |
| |
| |
| struct dnsc { |
| struct dnsc_conf conf; |
| struct hash *ht_query; |
| struct hash *ht_tcpconn; |
| struct udp_sock *us; |
| struct sa srvv[SRVC_MAX]; |
| uint32_t srvc; |
| }; |
| |
| |
| static const struct dnsc_conf default_conf = { |
| QUERY_HASH_SIZE, |
| TCP_HASH_SIZE, |
| CONN_TIMEOUT, |
| IDLE_TIMEOUT, |
| }; |
| |
| |
| static void tcpconn_close(struct tcpconn *tc, int err); |
| static int send_tcp(struct dns_query *q); |
| static void udp_timeout_handler(void *arg); |
| |
| |
| static bool rr_unlink_handler(struct le *le, void *arg) |
| { |
| struct dnsrr *rr = le->data; |
| (void)arg; |
| |
| list_unlink(&rr->le_priv); |
| mem_deref(rr); |
| |
| return false; |
| } |
| |
| |
| static void query_abort(struct dns_query *q) |
| { |
| if (q->tc) { |
| list_unlink(&q->le_tc); |
| q->tc = mem_deref(q->tc); |
| } |
| |
| tmr_cancel(&q->tmr); |
| hash_unlink(&q->le); |
| } |
| |
| |
| static void query_destructor(void *data) |
| { |
| struct dns_query *q = data; |
| uint32_t i; |
| |
| query_abort(q); |
| mbuf_reset(&q->mb); |
| mem_deref(q->name); |
| |
| for (i=0; i<ARRAY_SIZE(q->rrlv); i++) |
| (void)list_apply(&q->rrlv[i], true, rr_unlink_handler, NULL); |
| } |
| |
| |
| static void query_handler(struct dns_query *q, int err, |
| const struct dnshdr *hdr, struct list *ansl, |
| struct list *authl, struct list *addl) |
| { |
| /* deref here - before calling handler */ |
| if (q->qp) |
| *q->qp = NULL; |
| |
| /* The handler must only be called _once_ */ |
| if (q->qh) { |
| q->qh(err, hdr, ansl, authl, addl, q->arg); |
| q->qh = NULL; |
| } |
| |
| /* in case we have more (than one) q refs */ |
| query_abort(q); |
| } |
| |
| |
| static bool query_close_handler(struct le *le, void *arg) |
| { |
| struct dns_query *q = le->data; |
| (void)arg; |
| |
| query_handler(q, ECONNABORTED, NULL, NULL, NULL, NULL); |
| mem_deref(q); |
| |
| return false; |
| } |
| |
| |
| static bool query_cmp_handler(struct le *le, void *arg) |
| { |
| struct dns_query *q = le->data; |
| struct dnsquery *dq = arg; |
| |
| if (q->id != dq->hdr.id) |
| return false; |
| |
| if (q->opcode != dq->hdr.opcode) |
| return false; |
| |
| if (q->type != dq->type) |
| return false; |
| |
| if (q->dnsclass != dq->dnsclass) |
| return false; |
| |
| if (str_casecmp(q->name, dq->name)) |
| return false; |
| |
| return true; |
| } |
| |
| |
| 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 udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) |
| { |
| (void)src; |
| (void)reply_recv(arg, mb); |
| } |
| |
| |
| static void tcp_recv_handler(struct mbuf *mbrx, void *arg) |
| { |
| struct tcpconn *tc = arg; |
| struct mbuf *mb = tc->mb; |
| int err = 0; |
| size_t n; |
| |
| next: |
| /* frame length */ |
| if (!tc->flen) { |
| |
| n = min(2 - mb->end, mbuf_get_left(mbrx)); |
| |
| err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); |
| if (err) |
| goto error; |
| |
| mbrx->pos += n; |
| |
| if (mb->end < 2) |
| return; |
| |
| mb->pos = 0; |
| tc->flen = ntohs(mbuf_read_u16(mb)); |
| mb->pos = 0; |
| mb->end = 0; |
| } |
| |
| /* content */ |
| n = min(tc->flen - mb->end, mbuf_get_left(mbrx)); |
| |
| err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); |
| if (err) |
| goto error; |
| |
| mbrx->pos += n; |
| |
| if (mb->end < tc->flen) |
| return; |
| |
| mb->pos = 0; |
| |
| err = reply_recv(tc->dnsc, mb); |
| if (err) |
| goto error; |
| |
| /* reset tcp buffer */ |
| tc->flen = 0; |
| mb->pos = 0; |
| mb->end = 0; |
| |
| /* more data ? */ |
| if (mbuf_get_left(mbrx) > 0) { |
| DEBUG_INFO("%u bytes of tcp data left\n", mbuf_get_left(mbrx)); |
| goto next; |
| } |
| |
| return; |
| |
| error: |
| tcpconn_close(tc, err); |
| } |
| |
| |
| static void tcpconn_timeout_handler(void *arg) |
| { |
| struct tcpconn *tc = arg; |
| |
| DEBUG_NOTICE("tcp (%J) %s timeout \n", &tc->srv, |
| tc->connected ? "idle" : "connect"); |
| |
| tcpconn_close(tc, ETIMEDOUT); |
| } |
| |
| |
| static void tcp_estab_handler(void *arg) |
| { |
| struct tcpconn *tc = arg; |
| struct le *le = list_head(&tc->ql); |
| int err = 0; |
| |
| DEBUG_INFO("connection (%J) established\n", &tc->srv); |
| |
| while (le) { |
| struct dns_query *q = le->data; |
| |
| le = le->next; |
| |
| q->mb.pos = 0; |
| err = tcp_send(tc->conn, &q->mb); |
| if (err) |
| break; |
| |
| DEBUG_INFO("tcp send %J\n", &tc->srv); |
| } |
| |
| if (err) { |
| tcpconn_close(tc, err); |
| return; |
| } |
| |
| tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout, |
| tcpconn_timeout_handler, tc); |
| tc->connected = true; |
| } |
| |
| |
| static void tcp_close_handler(int err, void *arg) |
| { |
| struct tcpconn *tc = arg; |
| |
| DEBUG_NOTICE("connection (%J) closed: %m\n", &tc->srv, err); |
| tcpconn_close(tc, err); |
| } |
| |
| |
| static bool tcpconn_cmp_handler(struct le *le, void *arg) |
| { |
| const struct tcpconn *tc = le->data; |
| |
| /* avoid trying this connection if dead */ |
| if (!tc->conn) |
| return false; |
| |
| return sa_cmp(&tc->srv, arg, SA_ALL); |
| } |
| |
| |
| static bool tcpconn_fail_handler(struct le *le, void *arg) |
| { |
| struct dns_query *q = le->data; |
| int err = *((int *)arg); |
| |
| list_unlink(&q->le_tc); |
| q->tc = mem_deref(q->tc); |
| |
| if (q->ntx >= *q->srvc) { |
| DEBUG_WARNING("all servers failed, giving up!!\n"); |
| err = err ? err : ECONNREFUSED; |
| goto out; |
| } |
| |
| /* try next server(s) */ |
| err = send_tcp(q); |
| if (err) { |
| DEBUG_WARNING("all servers failed, giving up\n"); |
| goto out; |
| } |
| |
| out: |
| if (err) { |
| query_handler(q, err, NULL, NULL, NULL, NULL); |
| mem_deref(q); |
| } |
| |
| return false; |
| } |
| |
| |
| static void tcpconn_close(struct tcpconn *tc, int err) |
| { |
| if (!tc) |
| return; |
| |
| /* avoid trying this connection again (e.g. same address) */ |
| tc->conn = mem_deref(tc->conn); |
| (void)list_apply(&tc->ql, true, tcpconn_fail_handler, &err); |
| mem_deref(tc); |
| } |
| |
| |
| static void tcpconn_destructor(void *arg) |
| { |
| struct tcpconn *tc = arg; |
| |
| hash_unlink(&tc->le); |
| tmr_cancel(&tc->tmr); |
| mem_deref(tc->conn); |
| mem_deref(tc->mb); |
| } |
| |
| |
| static int tcpconn_alloc(struct tcpconn **tcpp, struct dnsc *dnsc, |
| const struct sa *srv) |
| { |
| struct tcpconn *tc; |
| int err = ENOMEM; |
| |
| if (!tcpp || !dnsc || !srv) |
| return EINVAL; |
| |
| tc = mem_zalloc(sizeof(struct tcpconn), tcpconn_destructor); |
| if (!tc) |
| goto out; |
| |
| hash_append(dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), &tc->le, tc); |
| tc->srv = *srv; |
| tc->dnsc = dnsc; |
| |
| tc->mb = mbuf_alloc(1500); |
| if (!tc->mb) |
| goto out; |
| |
| err = tcp_connect(&tc->conn, srv, tcp_estab_handler, |
| tcp_recv_handler, tcp_close_handler, tc); |
| if (err) |
| goto out; |
| |
| tmr_start(&tc->tmr, tc->dnsc->conf.conn_timeout, |
| tcpconn_timeout_handler, tc); |
| out: |
| if (err) |
| mem_deref(tc); |
| else |
| *tcpp = tc; |
| |
| return err; |
| } |
| |
| |
| static int send_tcp(struct dns_query *q) |
| { |
| const struct sa *srv; |
| struct tcpconn *tc; |
| int err = 0; |
| |
| if (!q) |
| return EINVAL; |
| |
| while (q->ntx < *q->srvc) { |
| |
| srv = &q->srvv[q->ntx++]; |
| |
| DEBUG_NOTICE("trying tcp server#%u: %J\n", q->ntx-1, srv); |
| |
| tc = list_ledata(hash_lookup(q->dnsc->ht_tcpconn, |
| sa_hash(srv, SA_ALL), |
| tcpconn_cmp_handler, |
| (void *)srv)); |
| if (!tc) { |
| err = tcpconn_alloc(&tc, q->dnsc, srv); |
| if (err) |
| continue; |
| } |
| |
| if (tc->connected) { |
| q->mb.pos = 0; |
| err = tcp_send(tc->conn, &q->mb); |
| if (err) { |
| tcpconn_close(tc, err); |
| continue; |
| } |
| |
| tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout, |
| tcpconn_timeout_handler, tc); |
| DEBUG_NOTICE("tcp send %J\n", srv); |
| } |
| |
| list_append(&tc->ql, &q->le_tc, q); |
| q->tc = mem_ref(tc); |
| break; |
| } |
| |
| return err; |
| } |
| |
| |
| static void tcp_timeout_handler(void *arg) |
| { |
| struct dns_query *q = arg; |
| |
| query_handler(q, ETIMEDOUT, NULL, NULL, NULL, NULL); |
| mem_deref(q); |
| } |
| |
| |
| static int send_udp(struct dns_query *q) |
| { |
| const struct sa *srv; |
| int err = ETIMEDOUT; |
| uint32_t i; |
| |
| if (!q) |
| return EINVAL; |
| |
| for (i=0; i<*q->srvc; i++) { |
| |
| srv = &q->srvv[q->ntx++%*q->srvc]; |
| |
| DEBUG_INFO("trying udp server#%u: %J\n", i, srv); |
| |
| q->mb.pos = 0; |
| err = udp_send(q->dnsc->us, srv, &q->mb); |
| if (!err) |
| break; |
| } |
| |
| return err; |
| } |
| |
| |
| static void udp_timeout_handler(void *arg) |
| { |
| struct dns_query *q = arg; |
| int err = ETIMEDOUT; |
| |
| if (q->ntx >= NTX_MAX) |
| goto out; |
| |
| err = send_udp(q); |
| if (err) |
| goto out; |
| |
| tmr_start(&q->tmr, 1000<<MIN(2, q->ntx - 2), |
| udp_timeout_handler, q); |
| |
| out: |
| if (err) { |
| query_handler(q, err, NULL, NULL, NULL, NULL); |
| mem_deref(q); |
| } |
| } |
| |
| |
| 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; |
| } |
| |
| |
| /** |
| * Query a DNS name |
| * |
| * @param qp Pointer to allocated DNS query |
| * @param dnsc DNS Client |
| * @param name DNS name |
| * @param type DNS Resource Record type |
| * @param dnsclass DNS Class |
| * @param rd Recursion Desired (RD) flag |
| * @param qh Query handler |
| * @param arg Handler argument |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| int dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name, |
| uint16_t type, uint16_t dnsclass, |
| bool rd, dns_query_h *qh, void *arg) |
| { |
| if (!dnsc) |
| return EINVAL; |
| |
| return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, NULL, |
| IPPROTO_UDP, dnsc->srvv, &dnsc->srvc, false, rd, qh, arg); |
| } |
| |
| |
| /** |
| * Query a DNS name SRV record |
| * |
| * @param qp Pointer to allocated DNS query |
| * @param dnsc DNS Client |
| * @param name DNS name |
| * @param type DNS Resource Record type |
| * @param dnsclass DNS Class |
| * @param proto Protocol |
| * @param srvv DNS Nameservers |
| * @param srvc Number of DNS nameservers |
| * @param rd Recursion Desired (RD) flag |
| * @param qh Query handler |
| * @param arg Handler argument |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| int dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name, |
| uint16_t type, uint16_t dnsclass, int proto, |
| const struct sa *srvv, const uint32_t *srvc, |
| bool rd, dns_query_h *qh, void *arg) |
| { |
| return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, |
| NULL, proto, srvv, srvc, false, rd, qh, arg); |
| } |
| |
| |
| /** |
| * Send a DNS query with NOTIFY opcode |
| * |
| * @param qp Pointer to allocated DNS query |
| * @param dnsc DNS Client |
| * @param name DNS name |
| * @param type DNS Resource Record type |
| * @param dnsclass DNS Class |
| * @param ans_rr Answer Resource Record |
| * @param proto Protocol |
| * @param srvv DNS Nameservers |
| * @param srvc Number of DNS nameservers |
| * @param qh Query handler |
| * @param arg Handler argument |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| int dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name, |
| uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr, |
| int proto, const struct sa *srvv, const uint32_t *srvc, |
| dns_query_h *qh, void *arg) |
| { |
| return query(qp, dnsc, DNS_OPCODE_NOTIFY, name, type, dnsclass, |
| ans_rr, proto, srvv, srvc, true, false, qh, arg); |
| } |
| |
| |
| static void dnsc_destructor(void *data) |
| { |
| struct dnsc *dnsc = data; |
| |
| (void)hash_apply(dnsc->ht_query, query_close_handler, NULL); |
| hash_flush(dnsc->ht_tcpconn); |
| |
| mem_deref(dnsc->ht_tcpconn); |
| mem_deref(dnsc->ht_query); |
| mem_deref(dnsc->us); |
| } |
| |
| |
| /** |
| * Allocate a DNS Client |
| * |
| * @param dcpp Pointer to allocated DNS Client |
| * @param conf Optional DNS configuration, NULL for default |
| * @param srvv DNS servers |
| * @param srvc Number of DNS Servers |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| int dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf, |
| const struct sa *srvv, uint32_t srvc) |
| { |
| struct dnsc *dnsc; |
| int err; |
| |
| if (!dcpp) |
| return EINVAL; |
| |
| dnsc = mem_zalloc(sizeof(*dnsc), dnsc_destructor); |
| if (!dnsc) |
| return ENOMEM; |
| |
| if (conf) |
| dnsc->conf = *conf; |
| else |
| dnsc->conf = default_conf; |
| |
| err = dnsc_srv_set(dnsc, srvv, srvc); |
| if (err) |
| goto out; |
| |
| err = udp_listen(&dnsc->us, NULL, udp_recv_handler, dnsc); |
| if (err) |
| goto out; |
| |
| err = hash_alloc(&dnsc->ht_query, dnsc->conf.query_hash_size); |
| if (err) |
| goto out; |
| |
| err = hash_alloc(&dnsc->ht_tcpconn, dnsc->conf.tcp_hash_size); |
| if (err) |
| goto out; |
| |
| out: |
| if (err) |
| mem_deref(dnsc); |
| else |
| *dcpp = dnsc; |
| |
| return err; |
| } |
| |
| |
| /** |
| * Set the DNS Servers on a DNS Client |
| * |
| * @param dnsc DNS Client |
| * @param srvv DNS Nameservers |
| * @param srvc Number of nameservers |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| int dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc) |
| { |
| uint32_t i; |
| |
| if (!dnsc) |
| return EINVAL; |
| |
| dnsc->srvc = min((uint32_t)ARRAY_SIZE(dnsc->srvv), srvc); |
| |
| if (srvv) { |
| for (i=0; i<dnsc->srvc; i++) |
| dnsc->srvv[i] = srvv[i]; |
| } |
| |
| return 0; |
| } |