xref: /openbsd-src/usr.sbin/smtpd/dns.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: dns.c,v 1.78 2014/04/19 12:26:15 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
6  * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/tree.h>
24 #include <sys/queue.h>
25 #include <sys/uio.h>
26 
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <arpa/nameser.h>
30 #include <netdb.h>
31 
32 #include <asr.h>
33 #include <event.h>
34 #include <imsg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include "smtpd.h"
40 #include "log.h"
41 
42 struct dns_lookup {
43 	struct dns_session	*session;
44 	int			 preference;
45 };
46 
47 struct dns_session {
48 	struct mproc		*p;
49 	uint64_t		 reqid;
50 	int			 type;
51 	char			 name[SMTPD_MAXHOSTNAMELEN];
52 	size_t			 mxfound;
53 	int			 error;
54 	int			 refcount;
55 };
56 
57 static void dns_lookup_host(struct dns_session *, const char *, int);
58 static void dns_dispatch_host(struct asr_result *, void *);
59 static void dns_dispatch_ptr(struct asr_result *, void *);
60 static void dns_dispatch_mx(struct asr_result *, void *);
61 static void dns_dispatch_mx_preference(struct asr_result *, void *);
62 
63 struct unpack {
64 	const char	*buf;
65 	size_t		 len;
66 	size_t		 offset;
67 	const char	*err;
68 };
69 
70 struct dns_header {
71 	uint16_t	id;
72 	uint16_t	flags;
73 	uint16_t	qdcount;
74 	uint16_t	ancount;
75 	uint16_t	nscount;
76 	uint16_t	arcount;
77 };
78 
79 struct dns_query {
80 	char		q_dname[MAXDNAME];
81 	uint16_t	q_type;
82 	uint16_t	q_class;
83 };
84 
85 struct dns_rr {
86 	char		rr_dname[MAXDNAME];
87 	uint16_t	rr_type;
88 	uint16_t	rr_class;
89 	uint32_t	rr_ttl;
90 	union {
91 		struct {
92 			char	cname[MAXDNAME];
93 		} cname;
94 		struct {
95 			uint16_t	preference;
96 			char		exchange[MAXDNAME];
97 		} mx;
98 		struct {
99 			char	nsname[MAXDNAME];
100 		} ns;
101 		struct {
102 			char	ptrname[MAXDNAME];
103 		} ptr;
104 		struct {
105 			char		mname[MAXDNAME];
106 			char		rname[MAXDNAME];
107 			uint32_t	serial;
108 			uint32_t	refresh;
109 			uint32_t	retry;
110 			uint32_t	expire;
111 			uint32_t	minimum;
112 		} soa;
113 		struct {
114 			struct in_addr	addr;
115 		} in_a;
116 		struct {
117 			struct in6_addr	addr6;
118 		} in_aaaa;
119 		struct {
120 			uint16_t	 rdlen;
121 			const void	*rdata;
122 		} other;
123 	} rr;
124 };
125 
126 static char *print_dname(const char *, char *, size_t);
127 static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
128     char *, size_t);
129 static int unpack_data(struct unpack *, void *, size_t);
130 static int unpack_u16(struct unpack *, uint16_t *);
131 static int unpack_u32(struct unpack *, uint32_t *);
132 static int unpack_inaddr(struct unpack *, struct in_addr *);
133 static int unpack_in6addr(struct unpack *, struct in6_addr *);
134 static int unpack_dname(struct unpack *, char *, size_t);
135 static void unpack_init(struct unpack *, const char *, size_t);
136 static int unpack_header(struct unpack *, struct dns_header *);
137 static int unpack_query(struct unpack *, struct dns_query *);
138 static int unpack_rr(struct unpack *, struct dns_rr *);
139 
140 
141 static int
142 domainname_is_addr(const char *s, struct sockaddr *sa, socklen_t *sl)
143 {
144 	struct addrinfo	hints, *res;
145 	socklen_t	sl2;
146 	size_t		l;
147 	char		buf[SMTPD_MAXDOMAINPARTSIZE];
148 	int		i6, error;
149 
150 	if (*s != '[')
151 		return (0);
152 
153 	i6 = (strncasecmp("[IPv6:", s, 6) == 0);
154 	s += i6 ? 6 : 1;
155 
156 	l = strlcpy(buf, s, sizeof(buf));
157 	if (l >= sizeof(buf) || l == 0 || buf[l - 1] != ']')
158 		return (0);
159 
160 	buf[l - 1] = '\0';
161 	memset(&hints, 0, sizeof(hints));
162 	hints.ai_flags = AI_NUMERICHOST;
163 	hints.ai_socktype = SOCK_STREAM;
164 	if (i6)
165 		hints.ai_family = AF_INET6;
166 
167 	res = NULL;
168 	if ((error = getaddrinfo(buf, NULL, &hints, &res))) {
169 		log_warnx("getaddrinfo: %s", gai_strerror(error));
170 	}
171 
172 	if (!res)
173 		return (0);
174 
175 	if (sa && sl) {
176 		sl2 = *sl;
177 		if (sl2 > res->ai_addrlen)
178 			sl2 = res->ai_addrlen;
179 		memmove(sa, res->ai_addr, sl2);
180 		*sl = res->ai_addrlen;
181 	}
182 
183 	freeaddrinfo(res);
184 	return (1);
185 }
186 
187 void
188 dns_imsg(struct mproc *p, struct imsg *imsg)
189 {
190 	struct sockaddr_storage	 ss;
191 	struct dns_session	*s;
192 	struct sockaddr		*sa;
193 	struct asr_query	*as;
194 	struct msg		 m;
195 	const char		*domain, *mx, *host;
196 	socklen_t		 sl;
197 
198 	s = xcalloc(1, sizeof *s, "dns_imsg");
199 	s->type = imsg->hdr.type;
200 	s->p = p;
201 
202 	m_msg(&m, imsg);
203 	m_get_id(&m, &s->reqid);
204 
205 	switch (s->type) {
206 
207 	case IMSG_MTA_DNS_HOST:
208 		m_get_string(&m, &host);
209 		m_end(&m);
210 		dns_lookup_host(s, host, -1);
211 		return;
212 
213 	case IMSG_MTA_DNS_PTR:
214 	case IMSG_SMTP_DNS_PTR:
215 		sa = (struct sockaddr *)&ss;
216 		m_get_sockaddr(&m, sa);
217 		m_end(&m);
218 		as = getnameinfo_async(sa, sa->sa_len, s->name, sizeof(s->name),
219 		    NULL, 0, 0, NULL);
220 		event_asr_run(as, dns_dispatch_ptr, s);
221 		return;
222 
223 	case IMSG_MTA_DNS_MX:
224 		m_get_string(&m, &domain);
225 		m_end(&m);
226 		(void)strlcpy(s->name, domain, sizeof(s->name));
227 
228 		sa = (struct sockaddr *)&ss;
229 		sl = sizeof(ss);
230 
231 		if (domainname_is_addr(domain, sa, &sl)) {
232 			m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
233 			m_add_id(s->p, s->reqid);
234 			m_add_sockaddr(s->p, sa);
235 			m_add_int(s->p, -1);
236 			m_close(s->p);
237 
238 			m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
239 			m_add_id(s->p, s->reqid);
240 			m_add_int(s->p, DNS_OK);
241 			m_close(s->p);
242 			free(s);
243 			return;
244 		}
245 
246 		as = res_query_async(s->name, C_IN, T_MX, NULL);
247 		if (as ==  NULL) {
248 			log_warn("warn: req_query_async: %s", s->name);
249 			m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
250 			m_add_id(s->p, s->reqid);
251 			m_add_int(s->p, DNS_EINVAL);
252 			m_close(s->p);
253 			free(s);
254 			return;
255 		}
256 
257 		event_asr_run(as, dns_dispatch_mx, s);
258 		return;
259 
260 	case IMSG_MTA_DNS_MX_PREFERENCE:
261 		m_get_string(&m, &domain);
262 		m_get_string(&m, &mx);
263 		m_end(&m);
264 		(void)strlcpy(s->name, mx, sizeof(s->name));
265 
266 		sa = (struct sockaddr *)&ss;
267 		sl = sizeof(ss);
268 
269 		as = res_query_async(domain, C_IN, T_MX, NULL);
270 		if (as == NULL) {
271 			m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
272 			m_add_id(s->p, s->reqid);
273 			m_add_int(s->p, DNS_ENOTFOUND);
274 			m_close(s->p);
275 			free(s);
276 			return;
277 		}
278 
279 		event_asr_run(as, dns_dispatch_mx_preference, s);
280 		return;
281 
282 	default:
283 		log_warnx("warn: bad dns request %d", s->type);
284 		fatal(NULL);
285 	}
286 }
287 
288 static void
289 dns_dispatch_host(struct asr_result *ar, void *arg)
290 {
291 	struct dns_session	*s;
292 	struct dns_lookup	*lookup = arg;
293 	struct addrinfo		*ai;
294 
295 	s = lookup->session;
296 
297 	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
298 		s->mxfound++;
299 		m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
300 		m_add_id(s->p, s->reqid);
301 		m_add_sockaddr(s->p, ai->ai_addr);
302 		m_add_int(s->p, lookup->preference);
303 		m_close(s->p);
304 	}
305 	free(lookup);
306 	if (ar->ar_addrinfo)
307 		freeaddrinfo(ar->ar_addrinfo);
308 
309 	if (ar->ar_gai_errno)
310 		s->error = ar->ar_gai_errno;
311 
312 	if (--s->refcount)
313 		return;
314 
315 	m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
316 	m_add_id(s->p, s->reqid);
317 	m_add_int(s->p, s->mxfound ? DNS_OK : DNS_ENOTFOUND);
318 	m_close(s->p);
319 	free(s);
320 }
321 
322 static void
323 dns_dispatch_ptr(struct asr_result *ar, void *arg)
324 {
325 	struct dns_session	*s = arg;
326 
327 	/* The error code could be more precise, but we don't currently care */
328 	m_create(s->p,  s->type, 0, 0, -1);
329 	m_add_id(s->p, s->reqid);
330 	m_add_int(s->p, ar->ar_gai_errno ? DNS_ENOTFOUND : DNS_OK);
331 	if (ar->ar_gai_errno == 0)
332 		m_add_string(s->p, s->name);
333 	m_close(s->p);
334 	free(s);
335 }
336 
337 static void
338 dns_dispatch_mx(struct asr_result *ar, void *arg)
339 {
340 	struct dns_session	*s = arg;
341 	struct unpack		 pack;
342 	struct dns_header	 h;
343 	struct dns_query	 q;
344 	struct dns_rr	 rr;
345 	char			 buf[512];
346 	size_t			 found;
347 
348 	if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) {
349 
350 		m_create(s->p,  IMSG_MTA_DNS_HOST_END, 0, 0, -1);
351 		m_add_id(s->p, s->reqid);
352 		if (ar->ar_rcode == NXDOMAIN)
353 			m_add_int(s->p, DNS_ENONAME);
354 		else if (ar->ar_h_errno == NO_RECOVERY)
355 			m_add_int(s->p, DNS_EINVAL);
356 		else
357 			m_add_int(s->p, DNS_RETRY);
358 		m_close(s->p);
359 		free(s);
360 		free(ar->ar_data);
361 		return;
362 	}
363 
364 	unpack_init(&pack, ar->ar_data, ar->ar_datalen);
365 	unpack_header(&pack, &h);
366 	unpack_query(&pack, &q);
367 
368 	found = 0;
369 	for (; h.ancount; h.ancount--) {
370 		unpack_rr(&pack, &rr);
371 		if (rr.rr_type != T_MX)
372 			continue;
373 		print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
374 		buf[strlen(buf) - 1] = '\0';
375 		dns_lookup_host(s, buf, rr.rr.mx.preference);
376 		found++;
377 	}
378 	free(ar->ar_data);
379 
380 	/* fallback to host if no MX is found. */
381 	if (found == 0)
382 		dns_lookup_host(s, s->name, 0);
383 }
384 
385 static void
386 dns_dispatch_mx_preference(struct asr_result *ar, void *arg)
387 {
388 	struct dns_session	*s = arg;
389 	struct unpack		 pack;
390 	struct dns_header	 h;
391 	struct dns_query	 q;
392 	struct dns_rr		 rr;
393 	char			 buf[512];
394 	int			 error;
395 
396 	if (ar->ar_h_errno) {
397 		if (ar->ar_rcode == NXDOMAIN)
398 			error = DNS_ENONAME;
399 		else if (ar->ar_h_errno == NO_RECOVERY
400 		    || ar->ar_h_errno == NO_DATA)
401 			error = DNS_EINVAL;
402 		else
403 			error = DNS_RETRY;
404 	}
405 	else {
406 		error = DNS_ENOTFOUND;
407 		unpack_init(&pack, ar->ar_data, ar->ar_datalen);
408 		unpack_header(&pack, &h);
409 		unpack_query(&pack, &q);
410 		for (; h.ancount; h.ancount--) {
411 			unpack_rr(&pack, &rr);
412 			if (rr.rr_type != T_MX)
413 				continue;
414 			print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
415 			buf[strlen(buf) - 1] = '\0';
416 			if (!strcasecmp(s->name, buf)) {
417 				error = DNS_OK;
418 				break;
419 			}
420 		}
421 	}
422 
423 	free(ar->ar_data);
424 
425 	m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
426 	m_add_id(s->p, s->reqid);
427 	m_add_int(s->p, error);
428 	if (error == DNS_OK)
429 		m_add_int(s->p, rr.rr.mx.preference);
430 	m_close(s->p);
431 	free(s);
432 }
433 
434 static void
435 dns_lookup_host(struct dns_session *s, const char *host, int preference)
436 {
437 	struct dns_lookup	*lookup;
438 	struct addrinfo		 hints;
439 	void			*as;
440 
441 	lookup = xcalloc(1, sizeof *lookup, "dns_lookup_host");
442 	lookup->preference = preference;
443 	lookup->session = s;
444 	s->refcount++;
445 
446 	memset(&hints, 0, sizeof(hints));
447 	hints.ai_family = PF_UNSPEC;
448 	hints.ai_socktype = SOCK_STREAM;
449 	as = getaddrinfo_async(host, NULL, &hints, NULL);
450 	event_asr_run(as, dns_dispatch_host, lookup);
451 }
452 
453 static char *
454 print_dname(const char *_dname, char *buf, size_t max)
455 {
456 	const unsigned char *dname = _dname;
457 	char    *res;
458 	size_t   left, n, count;
459 
460 	if (_dname[0] == 0) {
461 		(void)strlcpy(buf, ".", max);
462 		return buf;
463 	}
464 
465 	res = buf;
466 	left = max - 1;
467 	for (n = 0; dname[0] && left; n += dname[0]) {
468 		count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
469 		memmove(buf, dname + 1, count);
470 		dname += dname[0] + 1;
471 		left -= count;
472 		buf += count;
473 		if (left) {
474 			left -= 1;
475 			*buf++ = '.';
476 		}
477 	}
478 	buf[0] = 0;
479 
480 	return (res);
481 }
482 
483 static ssize_t
484 dname_expand(const unsigned char *data, size_t len, size_t offset,
485     size_t *newoffset, char *dst, size_t max)
486 {
487 	size_t		 n, count, end, ptr, start;
488 	ssize_t		 res;
489 
490 	if (offset >= len)
491 		return (-1);
492 
493 	res = 0;
494 	end = start = offset;
495 
496 	for (; (n = data[offset]); ) {
497 		if ((n & 0xc0) == 0xc0) {
498 			if (offset + 2 > len)
499 				return (-1);
500 			ptr = 256 * (n & ~0xc0) + data[offset + 1];
501 			if (ptr >= start)
502 				return (-1);
503 			if (end < offset + 2)
504 				end = offset + 2;
505 			offset = start = ptr;
506 			continue;
507 		}
508 		if (offset + n + 1 > len)
509 			return (-1);
510 
511 		/* copy n + at offset+1 */
512 		if (dst != NULL && max != 0) {
513 			count = (max < n + 1) ? (max) : (n + 1);
514 			memmove(dst, data + offset, count);
515 			dst += count;
516 			max -= count;
517 		}
518 		res += n + 1;
519 		offset += n + 1;
520 		if (end < offset)
521 			end = offset;
522 	}
523 	if (end < offset + 1)
524 		end = offset + 1;
525 
526 	if (dst != NULL && max != 0)
527 		dst[0] = 0;
528 	if (newoffset)
529 		*newoffset = end;
530 	return (res + 1);
531 }
532 
533 void
534 unpack_init(struct unpack *unpack, const char *buf, size_t len)
535 {
536 	unpack->buf = buf;
537 	unpack->len = len;
538 	unpack->offset = 0;
539 	unpack->err = NULL;
540 }
541 
542 static int
543 unpack_data(struct unpack *p, void *data, size_t len)
544 {
545 	if (p->err)
546 		return (-1);
547 
548 	if (p->len - p->offset < len) {
549 		p->err = "too short";
550 		return (-1);
551 	}
552 
553 	memmove(data, p->buf + p->offset, len);
554 	p->offset += len;
555 
556 	return (0);
557 }
558 
559 static int
560 unpack_u16(struct unpack *p, uint16_t *u16)
561 {
562 	if (unpack_data(p, u16, 2) == -1)
563 		return (-1);
564 
565 	*u16 = ntohs(*u16);
566 
567 	return (0);
568 }
569 
570 static int
571 unpack_u32(struct unpack *p, uint32_t *u32)
572 {
573 	if (unpack_data(p, u32, 4) == -1)
574 		return (-1);
575 
576 	*u32 = ntohl(*u32);
577 
578 	return (0);
579 }
580 
581 static int
582 unpack_inaddr(struct unpack *p, struct in_addr *a)
583 {
584 	return (unpack_data(p, a, 4));
585 }
586 
587 static int
588 unpack_in6addr(struct unpack *p, struct in6_addr *a6)
589 {
590 	return (unpack_data(p, a6, 16));
591 }
592 
593 static int
594 unpack_dname(struct unpack *p, char *dst, size_t max)
595 {
596 	ssize_t e;
597 
598 	if (p->err)
599 		return (-1);
600 
601 	e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
602 	if (e == -1) {
603 		p->err = "bad domain name";
604 		return (-1);
605 	}
606 	if (e < 0 || e > MAXDNAME) {
607 		p->err = "domain name too long";
608 		return (-1);
609 	}
610 
611 	return (0);
612 }
613 
614 static int
615 unpack_header(struct unpack *p, struct dns_header *h)
616 {
617 	if (unpack_data(p, h, HFIXEDSZ) == -1)
618 		return (-1);
619 
620 	h->flags = ntohs(h->flags);
621 	h->qdcount = ntohs(h->qdcount);
622 	h->ancount = ntohs(h->ancount);
623 	h->nscount = ntohs(h->nscount);
624 	h->arcount = ntohs(h->arcount);
625 
626 	return (0);
627 }
628 
629 static int
630 unpack_query(struct unpack *p, struct dns_query *q)
631 {
632 	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
633 	unpack_u16(p, &q->q_type);
634 	unpack_u16(p, &q->q_class);
635 
636 	return (p->err) ? (-1) : (0);
637 }
638 
639 static int
640 unpack_rr(struct unpack *p, struct dns_rr *rr)
641 {
642 	uint16_t	rdlen;
643 	size_t		save_offset;
644 
645 	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
646 	unpack_u16(p, &rr->rr_type);
647 	unpack_u16(p, &rr->rr_class);
648 	unpack_u32(p, &rr->rr_ttl);
649 	unpack_u16(p, &rdlen);
650 
651 	if (p->err)
652 		return (-1);
653 
654 	if (p->len - p->offset < rdlen) {
655 		p->err = "too short";
656 		return (-1);
657 	}
658 
659 	save_offset = p->offset;
660 
661 	switch (rr->rr_type) {
662 
663 	case T_CNAME:
664 		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
665 		break;
666 
667 	case T_MX:
668 		unpack_u16(p, &rr->rr.mx.preference);
669 		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
670 		break;
671 
672 	case T_NS:
673 		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
674 		break;
675 
676 	case T_PTR:
677 		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
678 		break;
679 
680 	case T_SOA:
681 		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
682 		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
683 		unpack_u32(p, &rr->rr.soa.serial);
684 		unpack_u32(p, &rr->rr.soa.refresh);
685 		unpack_u32(p, &rr->rr.soa.retry);
686 		unpack_u32(p, &rr->rr.soa.expire);
687 		unpack_u32(p, &rr->rr.soa.minimum);
688 		break;
689 
690 	case T_A:
691 		if (rr->rr_class != C_IN)
692 			goto other;
693 		unpack_inaddr(p, &rr->rr.in_a.addr);
694 		break;
695 
696 	case T_AAAA:
697 		if (rr->rr_class != C_IN)
698 			goto other;
699 		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
700 		break;
701 	default:
702 	other:
703 		rr->rr.other.rdata = p->buf + p->offset;
704 		rr->rr.other.rdlen = rdlen;
705 		p->offset += rdlen;
706 	}
707 
708 	if (p->err)
709 		return (-1);
710 
711 	/* make sure that the advertised rdlen is really ok */
712 	if (p->offset - save_offset != rdlen)
713 		p->err = "bad dlen";
714 
715 	return (p->err) ? (-1) : (0);
716 }
717