1 /**
2  * @file dns/rr.c  DNS Resource Records
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_list.h>
10 #include <re_mem.h>
11 #include <re_mbuf.h>
12 #include <re_net.h>
13 #include <re_sa.h>
14 #include <re_dns.h>
15 
16 
17 static void rr_destructor(void *data)
18 {
19 	struct dnsrr *rr = data;
20 
21 	mem_deref(rr->name);
22 
23 	switch (rr->type) {
24 
25 	case DNS_TYPE_NS:
26 		mem_deref(rr->rdata.ns.nsdname);
27 		break;
28 
29 	case DNS_TYPE_CNAME:
30 		mem_deref(rr->rdata.cname.cname);
31 		break;
32 
33 	case DNS_TYPE_SOA:
34 		mem_deref(rr->rdata.soa.mname);
35 		mem_deref(rr->rdata.soa.rname);
36 		break;
37 
38 	case DNS_TYPE_PTR:
39 		mem_deref(rr->rdata.ptr.ptrdname);
40 		break;
41 
42 	case DNS_TYPE_MX:
43 		mem_deref(rr->rdata.mx.exchange);
44 		break;
45 
46 	case DNS_TYPE_SRV:
47 		mem_deref(rr->rdata.srv.target);
48 		break;
49 
50 	case DNS_TYPE_NAPTR:
51 		mem_deref(rr->rdata.naptr.flags);
52 		mem_deref(rr->rdata.naptr.services);
53 		mem_deref(rr->rdata.naptr.regexp);
54 		mem_deref(rr->rdata.naptr.replace);
55 		break;
56 	}
57 }
58 
59 
60 /**
61  * Allocate a new DNS Resource Record (RR)
62  *
63  * @return Newly allocated Resource Record, or NULL if no memory
64  */
65 struct dnsrr *dns_rr_alloc(void)
66 {
67 	return mem_zalloc(sizeof(struct dnsrr), rr_destructor);
68 }
69 
70 
71 /**
72  * Encode a DNS Resource Record
73  *
74  * @param mb       Memory buffer to encode into
75  * @param rr       DNS Resource Record
76  * @param ttl_offs TTL Offset
77  * @param ht_dname Domain name hash-table
78  * @param start    Start position
79  *
80  * @return 0 if success, otherwise errorcode
81  */
82 int dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs,
83 		  struct hash *ht_dname, size_t start)
84 {
85 	uint32_t ttl;
86 	uint16_t len;
87 	size_t start_rdata;
88 	int err = 0;
89 
90 	if (!mb || !rr)
91 		return EINVAL;
92 
93 	ttl = (uint32_t)((rr->ttl > ttl_offs) ? (rr->ttl - ttl_offs) : 0);
94 
95 	err |= dns_dname_encode(mb, rr->name, ht_dname, start, true);
96 	err |= mbuf_write_u16(mb, htons(rr->type));
97 	err |= mbuf_write_u16(mb, htons(rr->dnsclass));
98 	err |= mbuf_write_u32(mb, htonl(ttl));
99 	err |= mbuf_write_u16(mb, htons(rr->rdlen));
100 
101 	start_rdata = mb->pos;
102 
103 	switch (rr->type) {
104 
105 	case DNS_TYPE_A:
106 		err |= mbuf_write_u32(mb, htonl(rr->rdata.a.addr));
107 		break;
108 
109 	case DNS_TYPE_NS:
110 		err |= dns_dname_encode(mb, rr->rdata.ns.nsdname,
111 					ht_dname, start, true);
112 		break;
113 
114 	case DNS_TYPE_CNAME:
115 		err |= dns_dname_encode(mb, rr->rdata.cname.cname,
116 					ht_dname, start, true);
117 		break;
118 
119 	case DNS_TYPE_SOA:
120 		err |= dns_dname_encode(mb, rr->rdata.soa.mname,
121 					ht_dname, start, true);
122 		err |= dns_dname_encode(mb, rr->rdata.soa.rname,
123 					ht_dname, start, true);
124 		err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.serial));
125 		err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.refresh));
126 		err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.retry));
127 		err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.expire));
128 		err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.ttlmin));
129 		break;
130 
131 	case DNS_TYPE_PTR:
132 		err |= dns_dname_encode(mb, rr->rdata.ptr.ptrdname,
133 					ht_dname, start, true);
134 		break;
135 
136 	case DNS_TYPE_MX:
137 		err |= mbuf_write_u16(mb, htons(rr->rdata.mx.pref));
138 		err |= dns_dname_encode(mb, rr->rdata.mx.exchange,
139 					ht_dname, start, true);
140 		break;
141 
142 	case DNS_TYPE_AAAA:
143 		err |= mbuf_write_mem(mb, rr->rdata.aaaa.addr, 16);
144 		break;
145 
146 	case DNS_TYPE_SRV:
147 		err |= mbuf_write_u16(mb, htons(rr->rdata.srv.pri));
148 		err |= mbuf_write_u16(mb, htons(rr->rdata.srv.weight));
149 		err |= mbuf_write_u16(mb, htons(rr->rdata.srv.port));
150 		err |= dns_dname_encode(mb, rr->rdata.srv.target,
151 					ht_dname, start, false);
152 		break;
153 
154 	case DNS_TYPE_NAPTR:
155 		err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.order));
156 		err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.pref));
157 		err |= dns_cstr_encode(mb, rr->rdata.naptr.flags);
158 		err |= dns_cstr_encode(mb, rr->rdata.naptr.services);
159 		err |= dns_cstr_encode(mb, rr->rdata.naptr.regexp);
160 		err |= dns_dname_encode(mb, rr->rdata.naptr.replace,
161 					ht_dname, start, false);
162 		break;
163 
164 	default:
165 		err = EINVAL;
166 		break;
167 	}
168 
169 	len = mb->pos - start_rdata;
170 	mb->pos = start_rdata - 2;
171 	err |= mbuf_write_u16(mb, htons(len));
172 	mb->pos += len;
173 
174 	return err;
175 }
176 
177 
178 /**
179  * Decode a DNS Resource Record (RR) from a memory buffer
180  *
181  * @param mb    Memory buffer to decode from
182  * @param rr    Pointer to allocated Resource Record
183  * @param start Start position
184  *
185  * @return 0 if success, otherwise errorcode
186  */
187 int dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start)
188 {
189 	int err = 0;
190 	struct dnsrr *lrr;
191 
192 	if (!mb || !rr)
193 		return EINVAL;
194 
195 	lrr = dns_rr_alloc();
196 	if (!lrr)
197 		return ENOMEM;
198 
199 	err = dns_dname_decode(mb, &lrr->name, start);
200 	if (err)
201 		goto error;
202 
203 	if (mbuf_get_left(mb) < 10)
204 		goto fmerr;
205 
206 	lrr->type     = ntohs(mbuf_read_u16(mb));
207 	lrr->dnsclass = ntohs(mbuf_read_u16(mb));
208 	lrr->ttl      = ntohl(mbuf_read_u32(mb));
209 	lrr->rdlen    = ntohs(mbuf_read_u16(mb));
210 
211 	if (mbuf_get_left(mb) < lrr->rdlen)
212 		goto fmerr;
213 
214 	switch (lrr->type) {
215 
216 	case DNS_TYPE_A:
217 		if (lrr->rdlen != 4)
218 			goto fmerr;
219 
220 		lrr->rdata.a.addr = ntohl(mbuf_read_u32(mb));
221 		break;
222 
223 	case DNS_TYPE_NS:
224 		err = dns_dname_decode(mb, &lrr->rdata.ns.nsdname, start);
225 		if (err)
226 			goto error;
227 
228 		break;
229 
230 	case DNS_TYPE_CNAME:
231 		err = dns_dname_decode(mb, &lrr->rdata.cname.cname, start);
232 		if (err)
233 			goto error;
234 
235 		break;
236 
237 	case DNS_TYPE_SOA:
238 		err = dns_dname_decode(mb, &lrr->rdata.soa.mname, start);
239 		if (err)
240 			goto error;
241 
242 		err = dns_dname_decode(mb, &lrr->rdata.soa.rname, start);
243 		if (err)
244 			goto error;
245 
246 		if (mbuf_get_left(mb) < 20)
247 			goto fmerr;
248 
249 		lrr->rdata.soa.serial  = ntohl(mbuf_read_u32(mb));
250 		lrr->rdata.soa.refresh = ntohl(mbuf_read_u32(mb));
251 		lrr->rdata.soa.retry   = ntohl(mbuf_read_u32(mb));
252 		lrr->rdata.soa.expire  = ntohl(mbuf_read_u32(mb));
253 		lrr->rdata.soa.ttlmin  = ntohl(mbuf_read_u32(mb));
254 		break;
255 
256 	case DNS_TYPE_PTR:
257 		err = dns_dname_decode(mb, &lrr->rdata.ptr.ptrdname, start);
258 		if (err)
259 			goto error;
260 
261 		break;
262 
263 	case DNS_TYPE_MX:
264 		if (mbuf_get_left(mb) < 2)
265 			goto fmerr;
266 
267 		lrr->rdata.mx.pref = ntohs(mbuf_read_u16(mb));
268 
269 		err = dns_dname_decode(mb, &lrr->rdata.mx.exchange, start);
270 		if (err)
271 			goto error;
272 
273 		break;
274 
275 	case DNS_TYPE_AAAA:
276 		if (lrr->rdlen != 16)
277 			goto fmerr;
278 
279 		err = mbuf_read_mem(mb, lrr->rdata.aaaa.addr, 16);
280 		if (err)
281 			goto error;
282 		break;
283 
284 	case DNS_TYPE_SRV:
285 		if (mbuf_get_left(mb) < 6)
286 			goto fmerr;
287 
288 		lrr->rdata.srv.pri    = ntohs(mbuf_read_u16(mb));
289 		lrr->rdata.srv.weight = ntohs(mbuf_read_u16(mb));
290 		lrr->rdata.srv.port   = ntohs(mbuf_read_u16(mb));
291 
292 		err = dns_dname_decode(mb, &lrr->rdata.srv.target, start);
293 		if (err)
294 			goto error;
295 
296 		break;
297 
298 	case DNS_TYPE_NAPTR:
299 		if (mbuf_get_left(mb) < 4)
300 			goto fmerr;
301 
302 		lrr->rdata.naptr.order = ntohs(mbuf_read_u16(mb));
303 		lrr->rdata.naptr.pref  = ntohs(mbuf_read_u16(mb));
304 
305 		err = dns_cstr_decode(mb, &lrr->rdata.naptr.flags);
306 		if (err)
307 			goto error;
308 
309 		err = dns_cstr_decode(mb, &lrr->rdata.naptr.services);
310 		if (err)
311 			goto error;
312 
313 		err = dns_cstr_decode(mb, &lrr->rdata.naptr.regexp);
314 		if (err)
315 			goto error;
316 
317 		err = dns_dname_decode(mb, &lrr->rdata.naptr.replace, start);
318 		if (err)
319 			goto error;
320 
321 		break;
322 
323 	default:
324 		mb->pos += lrr->rdlen;
325 		break;
326 	}
327 
328 	*rr = lrr;
329 
330 	return 0;
331 
332  fmerr:
333 	err = EINVAL;
334  error:
335 	mem_deref(lrr);
336 
337 	return err;
338 }
339 
340 
341 /**
342  * Compare two DNS Resource Records
343  *
344  * @param rr1   First Resource Record
345  * @param rr2   Second Resource Record
346  * @param rdata If true, also compares Resource Record data
347  *
348  * @return True if match, false if not match
349  */
350 bool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata)
351 {
352 	if (!rr1 || !rr2)
353 		return false;
354 
355 	if (rr1 == rr2)
356 		return true;
357 
358 	if (rr1->type != rr2->type)
359 		return false;
360 
361 	if (rr1->dnsclass != rr2->dnsclass)
362 		return false;
363 
364 	if (str_casecmp(rr1->name, rr2->name))
365 		return false;
366 
367 	if (!rdata)
368 		return true;
369 
370 	switch (rr1->type) {
371 
372 	case DNS_TYPE_A:
373 		if (rr1->rdata.a.addr != rr2->rdata.a.addr)
374 			return false;
375 
376 		break;
377 
378 	case DNS_TYPE_NS:
379 		if (str_casecmp(rr1->rdata.ns.nsdname, rr2->rdata.ns.nsdname))
380 			return false;
381 
382 		break;
383 
384 	case DNS_TYPE_CNAME:
385 		if (str_casecmp(rr1->rdata.cname.cname,
386 				rr2->rdata.cname.cname))
387 			return false;
388 
389 		break;
390 
391 	case DNS_TYPE_SOA:
392 		if (str_casecmp(rr1->rdata.soa.mname, rr2->rdata.soa.mname))
393 			return false;
394 
395 		if (str_casecmp(rr1->rdata.soa.rname, rr2->rdata.soa.rname))
396 			return false;
397 
398 		if (rr1->rdata.soa.serial != rr2->rdata.soa.serial)
399 			return false;
400 
401 		if (rr1->rdata.soa.refresh != rr2->rdata.soa.refresh)
402 			return false;
403 
404 		if (rr1->rdata.soa.retry != rr2->rdata.soa.retry)
405 			return false;
406 
407 		if (rr1->rdata.soa.expire != rr2->rdata.soa.expire)
408 			return false;
409 
410 		if (rr1->rdata.soa.ttlmin != rr2->rdata.soa.ttlmin)
411 			return false;
412 
413 		break;
414 
415 	case DNS_TYPE_PTR:
416 		if (str_casecmp(rr1->rdata.ptr.ptrdname,
417 				rr2->rdata.ptr.ptrdname))
418 			return false;
419 
420 		break;
421 
422 	case DNS_TYPE_MX:
423 		if (rr1->rdata.mx.pref != rr2->rdata.mx.pref)
424 			return false;
425 
426 		if (str_casecmp(rr1->rdata.mx.exchange,
427 				rr2->rdata.mx.exchange))
428 			return false;
429 
430 		break;
431 
432 	case DNS_TYPE_AAAA:
433 		if (memcmp(rr1->rdata.aaaa.addr, rr2->rdata.aaaa.addr, 16))
434 			return false;
435 
436 		break;
437 
438 	case DNS_TYPE_SRV:
439 		if (rr1->rdata.srv.pri != rr2->rdata.srv.pri)
440 			return false;
441 
442 		if (rr1->rdata.srv.weight != rr2->rdata.srv.weight)
443 			return false;
444 
445 		if (rr1->rdata.srv.port != rr2->rdata.srv.port)
446 			return false;
447 
448 		if (str_casecmp(rr1->rdata.srv.target, rr2->rdata.srv.target))
449 			return false;
450 
451 		break;
452 
453 	case DNS_TYPE_NAPTR:
454 		if (rr1->rdata.naptr.order != rr2->rdata.naptr.order)
455 			return false;
456 
457 		if (rr1->rdata.naptr.pref != rr2->rdata.naptr.pref)
458 			return false;
459 
460 		/* todo check case sensitiveness */
461 		if (str_casecmp(rr1->rdata.naptr.flags,
462 				rr2->rdata.naptr.flags))
463 			return false;
464 
465 		/* todo check case sensitiveness */
466 		if (str_casecmp(rr1->rdata.naptr.services,
467 				rr2->rdata.naptr.services))
468 			return false;
469 
470 		/* todo check case sensitiveness */
471 		if (str_casecmp(rr1->rdata.naptr.regexp,
472 				rr2->rdata.naptr.regexp))
473 			return false;
474 
475 		/* todo check case sensitiveness */
476 		if (str_casecmp(rr1->rdata.naptr.replace,
477 				rr2->rdata.naptr.replace))
478 			return false;
479 
480 		break;
481 
482 	default:
483 		return false;
484 	}
485 
486 	return true;
487 }
488 
489 
490 /**
491  * Get the DNS Resource Record (RR) name
492  *
493  * @param type DNS Resource Record type
494  *
495  * @return DNS Resource Record name
496  */
497 const char *dns_rr_typename(uint16_t type)
498 {
499 	switch (type) {
500 
501 	case DNS_TYPE_A:     return "A";
502 	case DNS_TYPE_NS:    return "NS";
503 	case DNS_TYPE_CNAME: return "CNAME";
504 	case DNS_TYPE_SOA:   return "SOA";
505 	case DNS_TYPE_PTR:   return "PTR";
506 	case DNS_TYPE_MX:    return "MX";
507 	case DNS_TYPE_AAAA:  return "AAAA";
508 	case DNS_TYPE_SRV:   return "SRV";
509 	case DNS_TYPE_NAPTR: return "NAPTR";
510 	case DNS_QTYPE_IXFR: return "IXFR";
511 	case DNS_QTYPE_AXFR: return "AXFR";
512 	case DNS_QTYPE_ANY:  return "ANY";
513 	default:             return "??";
514 	}
515 }
516 
517 
518 /**
519  * Get the DNS Resource Record (RR) class name
520  *
521  * @param dnsclass DNS Class
522  *
523  * @return DNS Class name
524  */
525 const char *dns_rr_classname(uint16_t dnsclass)
526 {
527 	switch (dnsclass) {
528 
529 	case DNS_CLASS_IN:   return "IN";
530 	case DNS_QCLASS_ANY: return "ANY";
531 	default:             return "??";
532 	}
533 }
534 
535 
536 /**
537  * Print a DNS Resource Record
538  *
539  * @param pf Print function
540  * @param rr DNS Resource Record
541  *
542  * @return 0 if success, otherwise errorcode
543  */
544 int dns_rr_print(struct re_printf *pf, const struct dnsrr *rr)
545 {
546 	static const size_t w = 24;
547 	struct sa sa;
548 	size_t n, l;
549 	int err;
550 
551 	if (!pf || !rr)
552 		return EINVAL;
553 
554 	l = str_len(rr->name);
555 	n = (w > l) ? w - l : 0;
556 
557 	err = re_hprintf(pf, "%s.", rr->name);
558 	while (n--)
559 		err |= pf->vph(" ", 1, pf->arg);
560 
561 	err |= re_hprintf(pf, " %10lld %-4s %-7s ",
562 			  rr->ttl,
563 			  dns_rr_classname(rr->dnsclass),
564 			  dns_rr_typename(rr->type));
565 
566 	switch (rr->type) {
567 
568 	case DNS_TYPE_A:
569 		sa_set_in(&sa, rr->rdata.a.addr, 0);
570 		err |= re_hprintf(pf, "%j", &sa);
571 		break;
572 
573 	case DNS_TYPE_NS:
574 		err |= re_hprintf(pf, "%s.", rr->rdata.ns.nsdname);
575 		break;
576 
577 	case DNS_TYPE_CNAME:
578 		err |= re_hprintf(pf, "%s.", rr->rdata.cname.cname);
579 		break;
580 
581 	case DNS_TYPE_SOA:
582 		err |= re_hprintf(pf, "%s. %s. %u %u %u %u %u",
583 				  rr->rdata.soa.mname,
584 				  rr->rdata.soa.rname,
585 				  rr->rdata.soa.serial,
586 				  rr->rdata.soa.refresh,
587 				  rr->rdata.soa.retry,
588 				  rr->rdata.soa.expire,
589 				  rr->rdata.soa.ttlmin);
590 		break;
591 
592 	case DNS_TYPE_PTR:
593 		err |= re_hprintf(pf, "%s.", rr->rdata.ptr.ptrdname);
594 		break;
595 
596 	case DNS_TYPE_MX:
597 		err |= re_hprintf(pf, "%3u %s.", rr->rdata.mx.pref,
598 				  rr->rdata.mx.exchange);
599 		break;
600 
601 	case DNS_TYPE_AAAA:
602 		sa_set_in6(&sa, rr->rdata.aaaa.addr, 0);
603 		err |= re_hprintf(pf, "%j", &sa);
604 		break;
605 
606 	case DNS_TYPE_SRV:
607 		err |= re_hprintf(pf, "%3u %3u %u %s.",
608 				  rr->rdata.srv.pri,
609 				  rr->rdata.srv.weight,
610 				  rr->rdata.srv.port,
611 				  rr->rdata.srv.target);
612 		break;
613 
614 	case DNS_TYPE_NAPTR:
615 		err |= re_hprintf(pf, "%3u %3u \"%s\" \"%s\" \"%s\" %s.",
616 				  rr->rdata.naptr.order,
617 				  rr->rdata.naptr.pref,
618 				  rr->rdata.naptr.flags,
619 				  rr->rdata.naptr.services,
620 				  rr->rdata.naptr.regexp,
621 				  rr->rdata.naptr.replace);
622 		break;
623 
624 	default:
625 		err |= re_hprintf(pf, "?");
626 		break;
627 	}
628 
629 	return err;
630 }
631