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