1 /**
2 * @file dns/client.c DNS Client
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6 #include <string.h>
7 #include <re_types.h>
8 #include <re_fmt.h>
9 #include <re_mem.h>
10 #include <re_mbuf.h>
11 #include <re_list.h>
12 #include <re_hash.h>
13 #include <re_tmr.h>
14 #include <re_sa.h>
15 #include <re_udp.h>
16 #include <re_tcp.h>
17 #include <re_sys.h>
18 #include <re_dns.h>
19
20
21 #define DEBUG_MODULE "dnsc"
22 #define DEBUG_LEVEL 5
23 #include <re_dbg.h>
24
25
26 enum {
27 NTX_MAX = 20,
28 QUERY_HASH_SIZE = 16,
29 TCP_HASH_SIZE = 2,
30 CONN_TIMEOUT = 10 * 1000,
31 IDLE_TIMEOUT = 30 * 1000,
32 SRVC_MAX = 32,
33 };
34
35
36 struct tcpconn {
37 struct le le;
38 struct list ql;
39 struct tmr tmr;
40 struct sa srv;
41 struct tcp_conn *conn;
42 struct mbuf *mb;
43 bool connected;
44 uint16_t flen;
45 struct dnsc *dnsc; /* parent */
46 };
47
48
49 struct dns_query {
50 struct le le;
51 struct le le_tc;
52 struct tmr tmr;
53 struct mbuf mb;
54 struct list rrlv[3];
55 char *name;
56 const struct sa *srvv;
57 const uint32_t *srvc;
58 struct tcpconn *tc;
59 struct dnsc *dnsc; /* parent */
60 struct dns_query **qp; /* app ref */
61 uint32_t ntx;
62 uint16_t id;
63 uint16_t type;
64 uint16_t dnsclass;
65 uint8_t opcode;
66 dns_query_h *qh;
67 void *arg;
68 };
69
70
71 struct dnsquery {
72 struct dnshdr hdr;
73 char *name;
74 uint16_t type;
75 uint16_t dnsclass;
76 };
77
78
79 struct dnsc {
80 struct dnsc_conf conf;
81 struct hash *ht_query;
82 struct hash *ht_tcpconn;
83 struct udp_sock *us;
84 struct sa srvv[SRVC_MAX];
85 uint32_t srvc;
86 };
87
88
89 static const struct dnsc_conf default_conf = {
90 QUERY_HASH_SIZE,
91 TCP_HASH_SIZE,
92 CONN_TIMEOUT,
93 IDLE_TIMEOUT,
94 };
95
96
97 static void tcpconn_close(struct tcpconn *tc, int err);
98 static int send_tcp(struct dns_query *q);
99 static void udp_timeout_handler(void *arg);
100
101
rr_unlink_handler(struct le * le,void * arg)102 static bool rr_unlink_handler(struct le *le, void *arg)
103 {
104 struct dnsrr *rr = le->data;
105 (void)arg;
106
107 list_unlink(&rr->le_priv);
108 mem_deref(rr);
109
110 return false;
111 }
112
113
query_abort(struct dns_query * q)114 static void query_abort(struct dns_query *q)
115 {
116 if (q->tc) {
117 list_unlink(&q->le_tc);
118 q->tc = mem_deref(q->tc);
119 }
120
121 tmr_cancel(&q->tmr);
122 hash_unlink(&q->le);
123 }
124
125
query_destructor(void * data)126 static void query_destructor(void *data)
127 {
128 struct dns_query *q = data;
129 uint32_t i;
130
131 query_abort(q);
132 mbuf_reset(&q->mb);
133 mem_deref(q->name);
134
135 for (i=0; i<ARRAY_SIZE(q->rrlv); i++)
136 (void)list_apply(&q->rrlv[i], true, rr_unlink_handler, NULL);
137 }
138
139
query_handler(struct dns_query * q,int err,const struct dnshdr * hdr,struct list * ansl,struct list * authl,struct list * addl)140 static void query_handler(struct dns_query *q, int err,
141 const struct dnshdr *hdr, struct list *ansl,
142 struct list *authl, struct list *addl)
143 {
144 /* deref here - before calling handler */
145 if (q->qp)
146 *q->qp = NULL;
147
148 /* The handler must only be called _once_ */
149 if (q->qh) {
150 q->qh(err, hdr, ansl, authl, addl, q->arg);
151 q->qh = NULL;
152 }
153
154 /* in case we have more (than one) q refs */
155 query_abort(q);
156 }
157
158
query_close_handler(struct le * le,void * arg)159 static bool query_close_handler(struct le *le, void *arg)
160 {
161 struct dns_query *q = le->data;
162 (void)arg;
163
164 query_handler(q, ECONNABORTED, NULL, NULL, NULL, NULL);
165 mem_deref(q);
166
167 return false;
168 }
169
170
query_cmp_handler(struct le * le,void * arg)171 static bool query_cmp_handler(struct le *le, void *arg)
172 {
173 struct dns_query *q = le->data;
174 struct dnsquery *dq = arg;
175
176 if (q->id != dq->hdr.id)
177 return false;
178
179 if (q->opcode != dq->hdr.opcode)
180 return false;
181
182 if (q->type != dq->type)
183 return false;
184
185 if (q->dnsclass != dq->dnsclass)
186 return false;
187
188 if (str_casecmp(q->name, dq->name))
189 return false;
190
191 return true;
192 }
193
194
reply_recv(struct dnsc * dnsc,struct mbuf * mb)195 static int reply_recv(struct dnsc *dnsc, struct mbuf *mb)
196 {
197 struct dns_query *q = NULL;
198 uint32_t i, j, nv[3];
199 struct dnsquery dq;
200 int err = 0;
201
202 if (!dnsc || !mb)
203 return EINVAL;
204
205 dq.name = NULL;
206
207 if (dns_hdr_decode(mb, &dq.hdr) || !dq.hdr.qr) {
208 err = EBADMSG;
209 goto out;
210 }
211
212 err = dns_dname_decode(mb, &dq.name, 0);
213 if (err)
214 goto out;
215
216 if (mbuf_get_left(mb) < 4) {
217 err = EBADMSG;
218 goto out;
219 }
220
221 dq.type = ntohs(mbuf_read_u16(mb));
222 dq.dnsclass = ntohs(mbuf_read_u16(mb));
223
224 q = list_ledata(hash_lookup(dnsc->ht_query, hash_joaat_str_ci(dq.name),
225 query_cmp_handler, &dq));
226 if (!q) {
227 err = ENOENT;
228 goto out;
229 }
230
231 /* try next server */
232 if (dq.hdr.rcode == DNS_RCODE_SRV_FAIL && q->ntx < *q->srvc) {
233
234 if (!q->tc) /* try next UDP server immediately */
235 tmr_start(&q->tmr, 0, udp_timeout_handler, q);
236
237 err = EPROTO;
238 goto out;
239 }
240
241 nv[0] = dq.hdr.nans;
242 nv[1] = dq.hdr.nauth;
243 nv[2] = dq.hdr.nadd;
244
245 for (i=0; i<ARRAY_SIZE(nv); i++) {
246
247 for (j=0; j<nv[i]; j++) {
248
249 struct dnsrr *rr = NULL;
250
251 err = dns_rr_decode(mb, &rr, 0);
252 if (err) {
253 query_handler(q, err, NULL, NULL, NULL, NULL);
254 mem_deref(q);
255 goto out;
256 }
257
258 list_append(&q->rrlv[i], &rr->le_priv, rr);
259 }
260 }
261
262 if (q->type == DNS_QTYPE_AXFR) {
263
264 struct dnsrr *rrh, *rrt;
265
266 rrh = list_ledata(list_head(&q->rrlv[0]));
267 rrt = list_ledata(list_tail(&q->rrlv[0]));
268
269 /* Wait for last AXFR reply with terminating SOA record */
270 if (dq.hdr.rcode == DNS_RCODE_OK && dq.hdr.nans > 0 &&
271 (!rrt || rrt->type != DNS_TYPE_SOA || rrh == rrt)) {
272 DEBUG_INFO("waiting for last SOA record in reply\n");
273 goto out;
274 }
275 }
276
277 query_handler(q, 0, &dq.hdr, &q->rrlv[0], &q->rrlv[1], &q->rrlv[2]);
278 mem_deref(q);
279
280 out:
281 mem_deref(dq.name);
282
283 return err;
284 }
285
286
udp_recv_handler(const struct sa * src,struct mbuf * mb,void * arg)287 static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
288 {
289 (void)src;
290 (void)reply_recv(arg, mb);
291 }
292
293
tcp_recv_handler(struct mbuf * mbrx,void * arg)294 static void tcp_recv_handler(struct mbuf *mbrx, void *arg)
295 {
296 struct tcpconn *tc = arg;
297 struct mbuf *mb = tc->mb;
298 int err = 0;
299 size_t n;
300
301 next:
302 /* frame length */
303 if (!tc->flen) {
304
305 n = min(2 - mb->end, mbuf_get_left(mbrx));
306
307 err = mbuf_write_mem(mb, mbuf_buf(mbrx), n);
308 if (err)
309 goto error;
310
311 mbrx->pos += n;
312
313 if (mb->end < 2)
314 return;
315
316 mb->pos = 0;
317 tc->flen = ntohs(mbuf_read_u16(mb));
318 mb->pos = 0;
319 mb->end = 0;
320 }
321
322 /* content */
323 n = min(tc->flen - mb->end, mbuf_get_left(mbrx));
324
325 err = mbuf_write_mem(mb, mbuf_buf(mbrx), n);
326 if (err)
327 goto error;
328
329 mbrx->pos += n;
330
331 if (mb->end < tc->flen)
332 return;
333
334 mb->pos = 0;
335
336 err = reply_recv(tc->dnsc, mb);
337 if (err)
338 goto error;
339
340 /* reset tcp buffer */
341 tc->flen = 0;
342 mb->pos = 0;
343 mb->end = 0;
344
345 /* more data ? */
346 if (mbuf_get_left(mbrx) > 0) {
347 DEBUG_INFO("%u bytes of tcp data left\n", mbuf_get_left(mbrx));
348 goto next;
349 }
350
351 return;
352
353 error:
354 tcpconn_close(tc, err);
355 }
356
357
tcpconn_timeout_handler(void * arg)358 static void tcpconn_timeout_handler(void *arg)
359 {
360 struct tcpconn *tc = arg;
361
362 DEBUG_NOTICE("tcp (%J) %s timeout \n", &tc->srv,
363 tc->connected ? "idle" : "connect");
364
365 tcpconn_close(tc, ETIMEDOUT);
366 }
367
368
tcp_estab_handler(void * arg)369 static void tcp_estab_handler(void *arg)
370 {
371 struct tcpconn *tc = arg;
372 struct le *le = list_head(&tc->ql);
373 int err = 0;
374
375 DEBUG_INFO("connection (%J) established\n", &tc->srv);
376
377 while (le) {
378 struct dns_query *q = le->data;
379
380 le = le->next;
381
382 q->mb.pos = 0;
383 err = tcp_send(tc->conn, &q->mb);
384 if (err)
385 break;
386
387 DEBUG_INFO("tcp send %J\n", &tc->srv);
388 }
389
390 if (err) {
391 tcpconn_close(tc, err);
392 return;
393 }
394
395 tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout,
396 tcpconn_timeout_handler, tc);
397 tc->connected = true;
398 }
399
400
tcp_close_handler(int err,void * arg)401 static void tcp_close_handler(int err, void *arg)
402 {
403 struct tcpconn *tc = arg;
404
405 DEBUG_NOTICE("connection (%J) closed: %m\n", &tc->srv, err);
406 tcpconn_close(tc, err);
407 }
408
409
tcpconn_cmp_handler(struct le * le,void * arg)410 static bool tcpconn_cmp_handler(struct le *le, void *arg)
411 {
412 const struct tcpconn *tc = le->data;
413
414 /* avoid trying this connection if dead */
415 if (!tc->conn)
416 return false;
417
418 return sa_cmp(&tc->srv, arg, SA_ALL);
419 }
420
421
tcpconn_fail_handler(struct le * le,void * arg)422 static bool tcpconn_fail_handler(struct le *le, void *arg)
423 {
424 struct dns_query *q = le->data;
425 int err = *((int *)arg);
426
427 list_unlink(&q->le_tc);
428 q->tc = mem_deref(q->tc);
429
430 if (q->ntx >= *q->srvc) {
431 DEBUG_WARNING("all servers failed, giving up!!\n");
432 err = err ? err : ECONNREFUSED;
433 goto out;
434 }
435
436 /* try next server(s) */
437 err = send_tcp(q);
438 if (err) {
439 DEBUG_WARNING("all servers failed, giving up\n");
440 goto out;
441 }
442
443 out:
444 if (err) {
445 query_handler(q, err, NULL, NULL, NULL, NULL);
446 mem_deref(q);
447 }
448
449 return false;
450 }
451
452
tcpconn_close(struct tcpconn * tc,int err)453 static void tcpconn_close(struct tcpconn *tc, int err)
454 {
455 if (!tc)
456 return;
457
458 /* avoid trying this connection again (e.g. same address) */
459 tc->conn = mem_deref(tc->conn);
460 (void)list_apply(&tc->ql, true, tcpconn_fail_handler, &err);
461 mem_deref(tc);
462 }
463
464
tcpconn_destructor(void * arg)465 static void tcpconn_destructor(void *arg)
466 {
467 struct tcpconn *tc = arg;
468
469 hash_unlink(&tc->le);
470 tmr_cancel(&tc->tmr);
471 mem_deref(tc->conn);
472 mem_deref(tc->mb);
473 }
474
475
tcpconn_alloc(struct tcpconn ** tcpp,struct dnsc * dnsc,const struct sa * srv)476 static int tcpconn_alloc(struct tcpconn **tcpp, struct dnsc *dnsc,
477 const struct sa *srv)
478 {
479 struct tcpconn *tc;
480 int err = ENOMEM;
481
482 if (!tcpp || !dnsc || !srv)
483 return EINVAL;
484
485 tc = mem_zalloc(sizeof(struct tcpconn), tcpconn_destructor);
486 if (!tc)
487 goto out;
488
489 hash_append(dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), &tc->le, tc);
490 tc->srv = *srv;
491 tc->dnsc = dnsc;
492
493 tc->mb = mbuf_alloc(1500);
494 if (!tc->mb)
495 goto out;
496
497 err = tcp_connect(&tc->conn, srv, tcp_estab_handler,
498 tcp_recv_handler, tcp_close_handler, tc);
499 if (err)
500 goto out;
501
502 tmr_start(&tc->tmr, tc->dnsc->conf.conn_timeout,
503 tcpconn_timeout_handler, tc);
504 out:
505 if (err)
506 mem_deref(tc);
507 else
508 *tcpp = tc;
509
510 return err;
511 }
512
513
send_tcp(struct dns_query * q)514 static int send_tcp(struct dns_query *q)
515 {
516 const struct sa *srv;
517 struct tcpconn *tc;
518 int err = 0;
519
520 if (!q)
521 return EINVAL;
522
523 while (q->ntx < *q->srvc) {
524
525 srv = &q->srvv[q->ntx++];
526
527 DEBUG_NOTICE("trying tcp server#%u: %J\n", q->ntx-1, srv);
528
529 tc = list_ledata(hash_lookup(q->dnsc->ht_tcpconn,
530 sa_hash(srv, SA_ALL),
531 tcpconn_cmp_handler,
532 (void *)srv));
533 if (!tc) {
534 err = tcpconn_alloc(&tc, q->dnsc, srv);
535 if (err)
536 continue;
537 }
538
539 if (tc->connected) {
540 q->mb.pos = 0;
541 err = tcp_send(tc->conn, &q->mb);
542 if (err) {
543 tcpconn_close(tc, err);
544 continue;
545 }
546
547 tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout,
548 tcpconn_timeout_handler, tc);
549 DEBUG_NOTICE("tcp send %J\n", srv);
550 }
551
552 list_append(&tc->ql, &q->le_tc, q);
553 q->tc = mem_ref(tc);
554 break;
555 }
556
557 return err;
558 }
559
560
tcp_timeout_handler(void * arg)561 static void tcp_timeout_handler(void *arg)
562 {
563 struct dns_query *q = arg;
564
565 query_handler(q, ETIMEDOUT, NULL, NULL, NULL, NULL);
566 mem_deref(q);
567 }
568
569
send_udp(struct dns_query * q)570 static int send_udp(struct dns_query *q)
571 {
572 const struct sa *srv;
573 int err = ETIMEDOUT;
574 uint32_t i;
575
576 if (!q)
577 return EINVAL;
578
579 for (i=0; i<*q->srvc; i++) {
580
581 srv = &q->srvv[q->ntx++%*q->srvc];
582
583 DEBUG_INFO("trying udp server#%u: %J\n", i, srv);
584
585 q->mb.pos = 0;
586 err = udp_send(q->dnsc->us, srv, &q->mb);
587 if (!err)
588 break;
589 }
590
591 return err;
592 }
593
594
udp_timeout_handler(void * arg)595 static void udp_timeout_handler(void *arg)
596 {
597 struct dns_query *q = arg;
598 int err = ETIMEDOUT;
599
600 if (q->ntx >= NTX_MAX)
601 goto out;
602
603 err = send_udp(q);
604 if (err)
605 goto out;
606
607 tmr_start(&q->tmr, 1000<<MIN(2, q->ntx - 2),
608 udp_timeout_handler, q);
609
610 out:
611 if (err) {
612 query_handler(q, err, NULL, NULL, NULL, NULL);
613 mem_deref(q);
614 }
615 }
616
617
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)618 static int query(struct dns_query **qp, struct dnsc *dnsc, uint8_t opcode,
619 const char *name, uint16_t type, uint16_t dnsclass,
620 const struct dnsrr *ans_rr, int proto,
621 const struct sa *srvv, const uint32_t *srvc,
622 bool aa, bool rd, dns_query_h *qh, void *arg)
623 {
624 struct dns_query *q = NULL;
625 struct dnshdr hdr;
626 int err = 0;
627 uint32_t i;
628
629 if (!dnsc || !name || !srvv || !srvc || !(*srvc))
630 return EINVAL;
631
632 if (DNS_QTYPE_AXFR == type)
633 proto = IPPROTO_TCP;
634
635 q = mem_zalloc(sizeof(*q), query_destructor);
636 if (!q)
637 goto nmerr;
638
639 hash_append(dnsc->ht_query, hash_joaat_str_ci(name), &q->le, q);
640 tmr_init(&q->tmr);
641 mbuf_init(&q->mb);
642
643 for (i=0; i<ARRAY_SIZE(q->rrlv); i++)
644 list_init(&q->rrlv[i]);
645
646 err = str_dup(&q->name, name);
647 if (err)
648 goto error;
649
650 q->srvv = srvv;
651 q->srvc = srvc;
652 q->id = rand_u16();
653 q->type = type;
654 q->opcode = opcode;
655 q->dnsclass = dnsclass;
656 q->dnsc = dnsc;
657
658 memset(&hdr, 0, sizeof(hdr));
659
660 hdr.id = q->id;
661 hdr.opcode = q->opcode;
662 hdr.aa = aa;
663 hdr.rd = rd;
664 hdr.nq = 1;
665 hdr.nans = ans_rr ? 1 : 0;
666
667 if (proto == IPPROTO_TCP)
668 q->mb.pos += 2;
669
670 err = dns_hdr_encode(&q->mb, &hdr);
671 if (err)
672 goto error;
673
674 err = dns_dname_encode(&q->mb, name, NULL, 0, false);
675 if (err)
676 goto error;
677
678 err |= mbuf_write_u16(&q->mb, htons(type));
679 err |= mbuf_write_u16(&q->mb, htons(dnsclass));
680 if (err)
681 goto error;
682
683 if (ans_rr) {
684 err = dns_rr_encode(&q->mb, ans_rr, 0, NULL, 0);
685 if (err)
686 goto error;
687 }
688
689 q->qh = qh;
690 q->arg = arg;
691
692 switch (proto) {
693
694 case IPPROTO_TCP:
695 q->mb.pos = 0;
696 (void)mbuf_write_u16(&q->mb, htons(q->mb.end - 2));
697
698 err = send_tcp(q);
699 if (err)
700 goto error;
701
702 tmr_start(&q->tmr, 60 * 1000, tcp_timeout_handler, q);
703 break;
704
705 case IPPROTO_UDP:
706 err = send_udp(q);
707 if (err)
708 goto error;
709
710 tmr_start(&q->tmr, 500, udp_timeout_handler, q);
711 break;
712
713 default:
714 err = EPROTONOSUPPORT;
715 goto error;
716 }
717
718 if (qp) {
719 q->qp = qp;
720 *qp = q;
721 }
722
723 return 0;
724
725 nmerr:
726 err = ENOMEM;
727 error:
728 mem_deref(q);
729
730 return err;
731 }
732
733
734 /**
735 * Query a DNS name
736 *
737 * @param qp Pointer to allocated DNS query
738 * @param dnsc DNS Client
739 * @param name DNS name
740 * @param type DNS Resource Record type
741 * @param dnsclass DNS Class
742 * @param rd Recursion Desired (RD) flag
743 * @param qh Query handler
744 * @param arg Handler argument
745 *
746 * @return 0 if success, otherwise errorcode
747 */
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)748 int dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name,
749 uint16_t type, uint16_t dnsclass,
750 bool rd, dns_query_h *qh, void *arg)
751 {
752 if (!dnsc)
753 return EINVAL;
754
755 return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, NULL,
756 IPPROTO_UDP, dnsc->srvv, &dnsc->srvc, false, rd, qh, arg);
757 }
758
759
760 /**
761 * Query a DNS name SRV record
762 *
763 * @param qp Pointer to allocated DNS query
764 * @param dnsc DNS Client
765 * @param name DNS name
766 * @param type DNS Resource Record type
767 * @param dnsclass DNS Class
768 * @param proto Protocol
769 * @param srvv DNS Nameservers
770 * @param srvc Number of DNS nameservers
771 * @param rd Recursion Desired (RD) flag
772 * @param qh Query handler
773 * @param arg Handler argument
774 *
775 * @return 0 if success, otherwise errorcode
776 */
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)777 int dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name,
778 uint16_t type, uint16_t dnsclass, int proto,
779 const struct sa *srvv, const uint32_t *srvc,
780 bool rd, dns_query_h *qh, void *arg)
781 {
782 return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass,
783 NULL, proto, srvv, srvc, false, rd, qh, arg);
784 }
785
786
787 /**
788 * Send a DNS query with NOTIFY opcode
789 *
790 * @param qp Pointer to allocated DNS query
791 * @param dnsc DNS Client
792 * @param name DNS name
793 * @param type DNS Resource Record type
794 * @param dnsclass DNS Class
795 * @param ans_rr Answer Resource Record
796 * @param proto Protocol
797 * @param srvv DNS Nameservers
798 * @param srvc Number of DNS nameservers
799 * @param qh Query handler
800 * @param arg Handler argument
801 *
802 * @return 0 if success, otherwise errorcode
803 */
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)804 int dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name,
805 uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr,
806 int proto, const struct sa *srvv, const uint32_t *srvc,
807 dns_query_h *qh, void *arg)
808 {
809 return query(qp, dnsc, DNS_OPCODE_NOTIFY, name, type, dnsclass,
810 ans_rr, proto, srvv, srvc, true, false, qh, arg);
811 }
812
813
dnsc_destructor(void * data)814 static void dnsc_destructor(void *data)
815 {
816 struct dnsc *dnsc = data;
817
818 (void)hash_apply(dnsc->ht_query, query_close_handler, NULL);
819 hash_flush(dnsc->ht_tcpconn);
820
821 mem_deref(dnsc->ht_tcpconn);
822 mem_deref(dnsc->ht_query);
823 mem_deref(dnsc->us);
824 }
825
826
827 /**
828 * Allocate a DNS Client
829 *
830 * @param dcpp Pointer to allocated DNS Client
831 * @param conf Optional DNS configuration, NULL for default
832 * @param srvv DNS servers
833 * @param srvc Number of DNS Servers
834 *
835 * @return 0 if success, otherwise errorcode
836 */
dnsc_alloc(struct dnsc ** dcpp,const struct dnsc_conf * conf,const struct sa * srvv,uint32_t srvc)837 int dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf,
838 const struct sa *srvv, uint32_t srvc)
839 {
840 struct dnsc *dnsc;
841 int err;
842
843 if (!dcpp)
844 return EINVAL;
845
846 dnsc = mem_zalloc(sizeof(*dnsc), dnsc_destructor);
847 if (!dnsc)
848 return ENOMEM;
849
850 if (conf)
851 dnsc->conf = *conf;
852 else
853 dnsc->conf = default_conf;
854
855 err = dnsc_srv_set(dnsc, srvv, srvc);
856 if (err)
857 goto out;
858
859 err = udp_listen(&dnsc->us, NULL, udp_recv_handler, dnsc);
860 if (err)
861 goto out;
862
863 err = hash_alloc(&dnsc->ht_query, dnsc->conf.query_hash_size);
864 if (err)
865 goto out;
866
867 err = hash_alloc(&dnsc->ht_tcpconn, dnsc->conf.tcp_hash_size);
868 if (err)
869 goto out;
870
871 out:
872 if (err)
873 mem_deref(dnsc);
874 else
875 *dcpp = dnsc;
876
877 return err;
878 }
879
880
881 /**
882 * Set the DNS Servers on a DNS Client
883 *
884 * @param dnsc DNS Client
885 * @param srvv DNS Nameservers
886 * @param srvc Number of nameservers
887 *
888 * @return 0 if success, otherwise errorcode
889 */
dnsc_srv_set(struct dnsc * dnsc,const struct sa * srvv,uint32_t srvc)890 int dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc)
891 {
892 uint32_t i;
893
894 if (!dnsc)
895 return EINVAL;
896
897 dnsc->srvc = min((uint32_t)ARRAY_SIZE(dnsc->srvv), srvc);
898
899 if (srvv) {
900 for (i=0; i<dnsc->srvc; i++)
901 dnsc->srvv[i] = srvv[i];
902 }
903
904 return 0;
905 }
906