1 /* This file is part of Mailfromd.
2    Copyright (C) 2005-2021 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #include <sys/types.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <adns.h>
26 #include <mailutils/alloc.h>
27 #include <mailutils/argcv.h>
28 #include <mailutils/io.h>
29 #include <mailutils/stream.h>
30 #include <mailutils/cstr.h>
31 #include <mailutils/wordsplit.h>
32 #include <mailutils/assoc.h>
33 
34 #include "libmf.h"
35 #include "dns.h"
36 
37 #define DEFAULT_QFLAGS \
38 	(adns_qf_quoteok_cname|adns_qf_cname_loose|adns_qf_quoteok_query)
39 
40 static mu_debug_handle_t debug_handle;
41 static adns_state state;
42 
43 static void
dns_log_cb(adns_state ads,void * logfndata,const char * fmt,va_list al)44 dns_log_cb(adns_state ads, void *logfndata, const char *fmt, va_list al)
45 {
46 /* FIXME: Could have used just:
47      mu_diag_vprintf(MU_DIAG_DEBUG, fmt, al);
48    but it will emit \e<N> directives in the middle of the string, which
49    upsets the mailutils' logstream implementation.
50 
51    A possible work over would be to use logfndata to select between
52    mu_diag_vprintf,mu_diag_cont_vprintf or appropriate mu_debug_log_
53    call.
54 
55    For the time being, a simplified approach is used: */
56 	mu_stream_vprintf(mu_strerr, fmt, al);
57 }
58 
59 void
dnsbase_init(void)60 dnsbase_init(void)
61 {
62 	if (!debug_handle)
63 		debug_handle = mu_debug_register_category("dns");
64 }
65 
66 void
dnsbase_real_init(char * configtext)67 dnsbase_real_init(char *configtext)
68 {
69 	int rc;
70 	int flags;
71 	mu_debug_level_t lev;
72 
73 	flags = adns_if_nosigpipe;
74 	if (mu_debug_get_category_level(debug_handle, &lev) == 0
75 	    && (lev & MU_DEBUG_LEVEL_MASK(MU_DEBUG_TRACE9)))
76 		flags |= adns_if_debug;
77 	rc = adns_init_logfn(&state, flags, configtext, dns_log_cb, NULL);
78 	if (rc) {
79 		mu_diag_funcall(MU_DIAG_ERROR, "adns_init", NULL, rc);
80 		exit(1);
81 	}
82 }
83 
84 void
dnsbase_file_init(char * filename)85 dnsbase_file_init(char *filename)
86 {
87 	if (!filename)
88 		dnsbase_real_init(NULL);
89 	else {
90 		mu_stream_t str;
91 		mu_off_t sz;
92 		int rc;
93 		char *cfg;
94 
95 		rc = mu_file_stream_create(&str, filename, MU_STREAM_READ);
96 		if (rc) {
97 			mu_diag_funcall(MU_DIAG_ERROR, "mu_file_stream_create",
98 					filename, rc);
99 			return;
100 		}
101 		rc = mu_stream_size(str, &sz);
102 		if (rc) {
103 			mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_size",
104 					filename, rc);
105 			mu_stream_destroy(&str);
106 			return;
107 		}
108 
109 		if (sz > ((size_t)~0)) {
110 			mu_error(_("%s too big"), filename);
111 			mu_stream_destroy(&str);
112 			return;
113 		}
114 
115 		cfg = mu_alloc(sz + 1);
116 
117 		rc = mu_stream_read(str, cfg, sz, NULL);
118 		mu_stream_destroy(&str);
119 		if (rc) {
120 			mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_read",
121 					filename, rc);
122 			return;
123 		}
124 		cfg[sz] = 0;
125 		dnsbase_real_init(cfg);
126 		free(cfg);
127 	}
128 }
129 
130 static adns_state
get_state(void)131 get_state(void)
132 {
133 	if (!state)
134 		dnsbase_real_init(NULL);
135 	return state;
136 }
137 
138 static inline size_t
dns_reply_elsize(struct dns_reply * reply)139 dns_reply_elsize(struct dns_reply *reply)
140 {
141 	switch (reply->type) {
142 	case dns_reply_ip:
143 		return sizeof(reply->data.ip[0]);
144 	case dns_reply_str:
145 		return sizeof(reply->data.str[0]);
146 	}
147 	abort();
148 }
149 
150 void
dns_reply_init(struct dns_reply * reply,dns_reply_type type,size_t count)151 dns_reply_init(struct dns_reply *reply, dns_reply_type type, size_t count)
152 {
153 	reply->type = type;
154 	reply->count = count;
155 	reply->maxcount = count;
156 	if (count)
157 		reply->data.ptr = mu_calloc(count, dns_reply_elsize(reply));
158 	else
159 		reply->data.ptr = NULL;
160 }
161 
162 void
dns_reply_ip_push(struct dns_reply * reply,void * item)163 dns_reply_ip_push(struct dns_reply *reply, void *item)
164 {
165 	if (reply->count == reply->maxcount)
166 		reply->data.ip = mu_2nrealloc(reply->data.ip,
167 					      &reply->maxcount,
168 					      sizeof(reply->data.ip[0]));
169 	reply->data.ip[reply->count++] = *(GACOPYZ_UINT32_T*)item;
170 }
171 
172 void
dns_reply_str_push(struct dns_reply * reply,void * item)173 dns_reply_str_push(struct dns_reply *reply, void *item)
174 {
175 	if (reply->count == reply->maxcount)
176 		reply->data.str = mu_2nrealloc(reply->data.ip,
177 					       &reply->maxcount,
178 					       sizeof(reply->data.str[0]));
179 	reply->data.str[reply->count++] = item;
180 }
181 
182 void
dns_reply_push(struct dns_reply * reply,void * item)183 dns_reply_push(struct dns_reply *reply, void *item)
184 {
185 	switch (reply->type) {
186 	case dns_reply_ip:
187 		dns_reply_ip_push(reply, item);
188 		break;
189 	case dns_reply_str:
190 		dns_reply_str_push(reply, item);
191 		break;
192 	default:
193 		abort();
194 	}
195 }
196 
197 void
dns_reply_free(struct dns_reply * reply)198 dns_reply_free(struct dns_reply *reply)
199 {
200 	int i;
201 
202 	switch (reply->type) {
203 	case dns_reply_str:
204 		for (i = 0; i < reply->count; i++)
205 			free(reply->data.str[i]);
206 		free(reply->data.str);
207 		break;
208 	case dns_reply_ip:
209 		free(reply->data.ip);
210 		break;
211 	}
212 }
213 
214 int
dns_str_is_ipv4(const char * addr)215 dns_str_is_ipv4(const char *addr)
216 {
217         int dot_count;
218         int digit_count;
219 
220         dot_count = 0;
221         digit_count = 0;
222         while (*addr != 0) {
223                 if (*addr == '.') {
224                         if (++dot_count > 4)
225                                 return 0;
226                         digit_count = 0;
227                 } else if (!(isdigit(*addr) && ++digit_count <= 3)) {
228                         return 0;
229                 }
230                 addr++;
231         }
232 
233         return dot_count == 3;
234 }
235 
236 static int
errno_to_dns_status(int e)237 errno_to_dns_status(int e)
238 {
239 	switch (e) {
240 	case 0:
241 		return dns_success;
242 	case EAGAIN:
243 #ifdef EINPROGRESS
244 	case EINPROGRESS:
245 #endif
246 #ifdef ETIMEDOUT
247 	case ETIMEDOUT:
248 #endif
249 		return dns_temp_failure;
250 	default:
251 		return dns_failure;
252 	}
253 }
254 
255 /* Table of correspondence between ADNS status codes and dns status.
256    Values are increased by 1 to be able to tell whether the entry is
257    initialized or not. */
258 int adns_to_dns_tab[] = {
259 #define STAT(s) ((s)+1)
260 	[adns_s_ok]                  = STAT(dns_success),
261 
262 	[adns_s_nomemory]            = STAT(dns_failure),
263 	[adns_s_unknownrrtype]       = STAT(dns_failure),
264 	[adns_s_systemfail]          = STAT(dns_failure),
265 
266 	/* remotely induced errors), detected locally */
267 	[adns_s_timeout]             = STAT(dns_temp_failure),
268 	[adns_s_allservfail]         = STAT(dns_temp_failure),
269 	[adns_s_norecurse]           = STAT(dns_temp_failure),
270 	[adns_s_invalidresponse]     = STAT(dns_failure),
271 	[adns_s_unknownformat]       = STAT(dns_failure),
272 
273 	/* remotely induced errors), reported by remote server to us */
274 	[adns_s_rcodeservfail]       = STAT(dns_not_found),
275 	[adns_s_rcodeformaterror]    = STAT(dns_not_found),
276 	[adns_s_rcodenotimplemented] = STAT(dns_not_found),
277 	[adns_s_rcoderefused]        = STAT(dns_not_found),
278 	[adns_s_rcodeunknown]        = STAT(dns_not_found),
279 
280 	/* remote configuration errors */
281 	[adns_s_inconsistent]        = STAT(dns_not_found),
282 	[adns_s_prohibitedcname]     = STAT(dns_not_found),
283 	[adns_s_answerdomaininvalid] = STAT(dns_not_found),
284 	[adns_s_answerdomaintoolong] = STAT(dns_not_found),
285 	[adns_s_invaliddata]         = STAT(dns_not_found),
286 
287 	/* permanent problems with the query */
288 	[adns_s_querydomainwrong]    = STAT(dns_failure),
289 	[adns_s_querydomaininvalid]  = STAT(dns_failure),
290 	[adns_s_querydomaintoolong]  = STAT(dns_failure),
291 
292 	/* permanent errors */
293 	[adns_s_nxdomain]            = STAT(dns_not_found),
294 	[adns_s_nodata]              = STAT(dns_not_found),
295 #undef STAT
296 };
297 
298 /* Convert ADNS status code E to DNS status. */
299 static int
adns_to_dns_status(int e)300 adns_to_dns_status(int e)
301 {
302 	int r;
303 
304 	/* If it is negative, fail right away */
305 	if (e < 0)
306 		return dns_failure;
307 	/* If it is not in table, it still can be a valid, but unhandled
308 	   value */
309 	if (e >= MU_ARRAY_SIZE(adns_to_dns_tab))
310 		return e < adns_s_max_permfail ? dns_not_found : dns_failure;
311 	/* Now, look up in the table */
312 	if ((r = adns_to_dns_tab[e]) > 0)
313 		return r - 1;
314 	/* If not found in table, use adns_s_max_ constants to decide the
315 	   error class.
316 	*/
317 	if (e < adns_s_max_localfail)
318 		return dns_failure;
319 	if (e < adns_s_max_remotefail)
320 		return dns_not_found;
321 	if (e < adns_s_max_tempfail)
322 		return dns_temp_failure;
323 	if (e < adns_s_max_misconfig)
324 		return dns_not_found;
325 	if (e < adns_s_max_misquery)
326 		return dns_not_found;
327 	return dns_not_found;
328 }
329 
330 dns_status
soa_check(const char * name,int ip,struct dns_reply * reply)331 soa_check(const char *name, int ip, struct dns_reply *reply)
332 {
333 	dns_status status = dns_failure;
334 	int rc;
335 	adns_answer *ans;
336 
337 	rc = adns_synchronous(get_state(), name, adns_r_soa_raw,
338 			      DEFAULT_QFLAGS,
339 			      &ans);
340 	if (rc)
341 		return errno_to_dns_status(rc);
342 	status = adns_to_dns_status(ans->status);
343 	if (status == dns_success) {
344 		if (ip) {
345 			status = a_lookup(ans->rrs.soa->mname, reply);
346 		} else {
347 			dns_reply_init(reply, dns_reply_str, 1);
348 			reply->data.str[0] = mu_strdup (ans->rrs.soa->mname);
349 		}
350 		free(ans);
351 	}
352 	return status;
353 }
354 
355 static dns_status
dns_reply_resolve(struct dns_reply * reply)356 dns_reply_resolve(struct dns_reply *reply)
357 {
358 	size_t i;
359 	struct dns_reply res;
360 
361 	dns_reply_init(&res, dns_reply_ip, 0);
362 	for (i = 0; i < reply->count; i++) {
363 		struct dns_reply r;
364 		dns_status stat = a_lookup(reply->data.str[i], &r);
365 		if (stat == dns_success) {
366 			size_t n;
367 			for (n = 0; n < r.count; n++) {
368 				dns_reply_push(&res, &r.data.ip[n]);
369 			}
370 			dns_reply_free(&r);
371 		}
372 	}
373 	dns_reply_free(reply);
374 	*reply = res;
375 	if (res.count == 0)
376 		return dns_not_found;
377 	return dns_success;
378 }
379 
380 /* Return MX records for the given HOST. */
381 dns_status
mx_lookup(const char * host,int resolve,struct dns_reply * reply)382 mx_lookup(const char *host, int resolve, struct dns_reply *reply)
383 {
384 	dns_status status = dns_failure;
385 	int rc;
386 	adns_answer *ans;
387 	int i;
388 
389 	rc = adns_synchronous(get_state(), host, adns_r_mx,
390 			      DEFAULT_QFLAGS,
391 			      &ans);
392 	if (rc)
393 		return errno_to_dns_status(rc);
394 	status = adns_to_dns_status(ans->status);
395 	if (status != dns_success)
396 		return status;
397 
398 	dns_reply_init(reply, dns_reply_str, ans->nrrs);
399 	for (i = 0; i < ans->nrrs; i++)
400 		reply->data.str[i] = mu_strdup(ans->rrs.inthostaddr[i].ha.host);
401 	free(ans);
402 
403 	if (resolve)
404 		status = dns_reply_resolve(reply);
405 
406 	return status;
407 }
408 
409 typedef char IPBUF[3*4+3+1];
410 
411 int
dns_reverse_ipstr(const char * ipstr,char * revipstr)412 dns_reverse_ipstr(const char *ipstr, char *revipstr)
413 {
414 	int i;
415 	const char *p;
416 	char *q;
417 
418 	q = revipstr + strlen(ipstr);
419 	*q = 0;
420 	for (i = 0, p = ipstr; *p && i < 4; i++) {
421 		int len;
422 
423 		for (len = 0; p[len] && p[len] != '.'; len++)
424 			;
425 		q -= len;
426 		memcpy(q, p, len);
427 		if (q > revipstr)
428 			*--q = '.';
429 		p += len;
430 		if (*p == '.')
431 			p++;
432 	}
433 
434 	return *p || i != 4;
435 }
436 
437 dns_status
dns_resolve_ipstr(const char * ipstr,const char * domain,char ** hbuf)438 dns_resolve_ipstr(const char *ipstr, const char *domain, char **hbuf)
439 {
440 	dns_status status = dns_failure;
441 	int rc;
442 	adns_answer *ans;
443 	char *name;
444 	adns_rrtype type;
445 
446 	if (!domain || strcasecmp(domain, "in-addr.arpa") == 0) {
447 		IPBUF ipbuf;
448 		if (!dns_str_is_ipv4(ipstr))
449 			return dns_failure;
450                 if (dns_reverse_ipstr(ipstr, ipbuf))
451                         return dns_failure;
452 		mu_asprintf(&name, "%s.in-addr.arpa", ipbuf);
453 		type = adns_r_ptr_raw;
454         } else {
455 		mu_asprintf(&name, "%s.%s", ipstr, domain);
456 		type = adns_r_a;
457         }
458 
459 	rc = adns_synchronous(get_state(), name, type,
460 			      DEFAULT_QFLAGS,
461 			      &ans);
462 	free(name);
463 	if (rc)
464 		return errno_to_dns_status(rc);
465 	status = adns_to_dns_status(ans->status);
466 	if (status == dns_success) {
467 		if (ans->type == adns_r_ptr_raw) {
468 			*hbuf = mu_strdup(ans->rrs.str[0]);
469 		} else {
470 			*hbuf = mu_strdup(inet_ntoa(ans->rrs.inaddr[0]));
471 		}
472 	}
473 	free(ans);
474 	return status;
475 }
476 
477 dns_status
dns_resolve_hostname(const char * host,char ** ipbuf)478 dns_resolve_hostname(const char *host, char **ipbuf)
479 {
480 	dns_status status = dns_failure;
481 	int rc;
482 	adns_answer *ans;
483 
484 	rc = adns_synchronous(get_state(), host, adns_r_a,
485 			      DEFAULT_QFLAGS,
486 			      &ans);
487 	if (rc)
488 		return errno_to_dns_status(rc);
489 	status = adns_to_dns_status(ans->status);
490 	if (status == dns_success)
491 		*ipbuf = mu_strdup(inet_ntoa(ans->rrs.inaddr[0]));
492 	free(ans);
493 	return status;
494 }
495 
496 
497 dns_status
a_lookup(const char * host,struct dns_reply * reply)498 a_lookup(const char *host, struct dns_reply *reply)
499 {
500 	dns_status status = dns_failure;
501 	int rc;
502 	adns_answer *ans;
503 
504 	rc = adns_synchronous(get_state(), host, adns_r_a,
505 			      DEFAULT_QFLAGS,
506 			      &ans);
507 	if (rc)
508 		return errno_to_dns_status(rc);
509 	status = adns_to_dns_status(ans->status);
510 	if (status == dns_success) {
511 		int i;
512 		dns_reply_init(reply, dns_reply_ip, ans->nrrs);
513 		for (i = 0; i < ans->nrrs; i++)
514 			reply->data.ip[i] = ans->rrs.inaddr[i].s_addr;
515 	}
516 	free(ans);
517 	return status;
518 }
519 
520 dns_status
ptr_lookup(struct in_addr ip,struct dns_reply * reply)521 ptr_lookup(struct in_addr ip, struct dns_reply *reply)
522 {
523 	dns_status status = dns_failure;
524 	int rc;
525 	adns_answer *ans;
526 	char *name;
527 
528 	ip.s_addr = ntohl(ip.s_addr);
529 	mu_asprintf(&name, "%s.in-addr.arpa", inet_ntoa(ip));
530 	rc = adns_synchronous(get_state(), name, adns_r_ptr_raw,
531 			      DEFAULT_QFLAGS,
532 			      &ans);
533 	free(name);
534 	if (rc)
535 		return errno_to_dns_status(rc);
536 	status = adns_to_dns_status(ans->status);
537 	if (status == dns_success) {
538 		int i;
539 		dns_reply_init(reply, dns_reply_str, ans->nrrs);
540 		for (i = 0; i < ans->nrrs; i++)
541 			reply->data.str[i] = mu_strdup(ans->rrs.str[i]);
542 	}
543 	free(ans);
544 	return status;
545 }
546 
547 dns_status
txt_lookup(const char * name,struct dns_reply * reply)548 txt_lookup(const char *name, struct dns_reply *reply)
549 {
550 	dns_status status = dns_failure;
551 	int rc;
552 	adns_answer *ans;
553 
554 	rc = adns_synchronous(get_state(), name, adns_r_txt,
555 			      DEFAULT_QFLAGS,
556 			      &ans);
557 	if (rc)
558 		return errno_to_dns_status(rc);
559 	status = adns_to_dns_status(ans->status);
560 	if (status == dns_success) {
561 		int i;
562 		dns_reply_init(reply, dns_reply_str, ans->nrrs);
563 		for (i = 0; i < ans->nrrs; i++) {
564 			size_t l = 0;
565 			int j;
566 			for (j = 0; ans->rrs.manyistr[i][j].i > 0; j++)
567 				l += ans->rrs.manyistr[i][j].i;
568 			reply->data.str[i] = mu_alloc(l + 1);
569 			reply->data.str[i][0] = 0;
570 			l = 0;
571 			for (j = 0; ans->rrs.manyistr[i][j].i > 0; j++) {
572 				memcpy(reply->data.str[i] + l,
573 				       ans->rrs.manyistr[i][j].str,
574 				       ans->rrs.manyistr[i][j].i);
575 				l += ans->rrs.manyistr[i][j].i;
576 			}
577 			reply->data.str[i][l] = 0;
578 		}
579 	}
580 	free(ans);
581 	return status;
582 }
583 
584 #define VSPF1_STR "v=spf1"
585 #define VSPF1_LEN (sizeof(VSPF1_STR)-1)
586 
587 dns_status
spf_lookup(const char * domain,char ** rec)588 spf_lookup(const char *domain, char **rec)
589 {
590 	dns_status status;
591 	struct dns_reply reply;
592 
593 	status = txt_lookup(domain, &reply);
594 	if (status == dns_success) {
595 		int i;
596 
597 		status = dns_not_found;
598 
599 		for (i = 0; i < reply.count; i++) {
600 			if (mu_c_strncasecmp(reply.data.str[i],
601 					     VSPF1_STR, VSPF1_LEN) == 0
602 			    && (reply.data.str[i][VSPF1_LEN] == 0
603 				|| mu_isspace(reply.data.str[i][VSPF1_LEN]))) {
604 				*rec = mu_strdup(reply.data.str[i]);
605 				status = dns_success;
606 				break;
607 			}
608 		}
609 		dns_reply_free(&reply);
610 	}
611 	return status;
612 }
613 
614 dns_status
dkim_lookup(const char * domain,const char * sel,char *** retval)615 dkim_lookup(const char *domain, const char *sel, char ***retval)
616 {
617 	dns_status status;
618 	struct dns_reply reply;
619 	char *dk;
620 
621 	if (mu_asprintf(&dk, "%s._domainkey.%s", sel, domain))
622 		mu_alloc_die();
623 	status = txt_lookup(dk, &reply);
624 	free(dk);
625 	if (status == dns_success) {
626 		int i;
627 		char **rv = mu_calloc(reply.count + 1, sizeof(*rv));
628 
629 		for (i = 0; i < reply.count; i++) {
630 			rv[i] = mu_strdup(reply.data.str[i]);
631 		}
632 		*retval = rv;
633 		dns_reply_free(&reply);
634 	}
635 	return status;
636 }
637 
638 /* rfc4408, chapter 5.5 */
639 dns_status
ptr_validate(const char * ipstr,struct dns_reply * reply)640 ptr_validate(const char *ipstr, struct dns_reply *reply)
641 {
642 	struct in_addr ip;
643 	size_t i;
644 	dns_status status;
645 	struct dns_reply ptr_reply;
646 	dns_status result = dns_not_found;
647 
648 	if (!inet_aton(ipstr, &ip))
649 		return dns_failure;
650 
651 	status = ptr_lookup(ip, &ptr_reply);
652 
653 	if (status != dns_success)
654 		return status;
655 
656 	if (reply)
657 		dns_reply_init(reply, dns_reply_str, 0);
658 	for (i = 0; i < ptr_reply.count; i++) {
659 		struct dns_reply r;
660 		status = a_lookup(ptr_reply.data.str[i], &r);
661 		if (status == dns_success) {
662 			size_t k;
663 
664 			for (k = 0; k < r.count; k++) {
665 				if (r.data.ip[k] == ip.s_addr) {
666 					result = dns_success;
667 					if (reply)
668 						dns_reply_push(reply,
669 							       mu_strdup(ptr_reply.data.str[i]));
670 					break;
671 				}
672 			}
673 			dns_reply_free(&r);
674 		}
675 	}
676 	dns_reply_free(&ptr_reply);
677 
678 	return result;
679 }
680 
681 
682 mf_status
dns_to_mf_status(dns_status stat)683 dns_to_mf_status(dns_status stat)
684 {
685 	return (mf_status) stat;
686 }
687 
688 dns_status
mf_to_dns_status(mf_status stat)689 mf_to_dns_status(mf_status stat)
690 {
691 	return (dns_status) stat;
692 }
693 
694 mf_status
resolve_ipstr_domain(const char * ipstr,const char * domain,char ** phbuf)695 resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf)
696 {
697 	char *hbuf;
698 	dns_status dstat;
699 
700 	mu_debug(debug_handle, MU_DEBUG_TRACE8,
701 		 ("Getting canonical name for %s", ipstr));
702 
703 	dstat = dns_resolve_ipstr(ipstr, domain, &hbuf);
704 
705 	switch (dstat) {
706 	case dns_success:
707 		mu_debug(debug_handle, MU_DEBUG_TRACE8,
708 			 ("%s resolved to %s", ipstr, hbuf));
709 		*phbuf = hbuf;
710 		break;
711 
712 	default:
713 		mu_debug(debug_handle, MU_DEBUG_TRACE8,
714 			 ("%s not resolved", ipstr));
715 	}
716 	return dns_to_mf_status(dstat);
717 }
718 
719 mf_status
resolve_ipstr(const char * ipstr,char ** phbuf)720 resolve_ipstr(const char *ipstr, char **phbuf)
721 {
722 	return resolve_ipstr_domain(ipstr, NULL, phbuf);
723 }
724 
725 mf_status
resolve_hostname(const char * host,char ** pipbuf)726 resolve_hostname(const char *host, char **pipbuf)
727 {
728 	char *ipbuf;
729 	dns_status dstat;
730 
731 	mu_debug(debug_handle, MU_DEBUG_TRACE8,
732 		 ("Getting IP address for %s", host));
733 
734 	dstat = dns_resolve_hostname(host, &ipbuf);
735 	switch (dstat) {
736 	case dns_success:
737 		mu_debug(debug_handle, MU_DEBUG_TRACE8,
738 			 ("%s resolved to %s", host, ipbuf));
739 		*pipbuf = ipbuf;
740 		break;
741 
742 	default:
743 		mu_debug(debug_handle, MU_DEBUG_TRACE8,
744 			 ("%s not resolved", host));
745 	}
746 	return dns_to_mf_status(dstat);
747 }
748 
749 /* Return NS records for the given DOMAIN. */
750 dns_status
ns_lookup(const char * domain,int resolve,struct dns_reply * reply)751 ns_lookup(const char *domain, int resolve, struct dns_reply *reply)
752 {
753 	dns_status status = dns_failure;
754 	int rc;
755 	adns_answer *ans;
756 	int i;
757 
758 	rc = adns_synchronous(get_state(), domain, adns_r_ns_raw,
759 			      DEFAULT_QFLAGS,
760 			      &ans);
761 	if (rc)
762 		return errno_to_dns_status(rc);
763 	status = adns_to_dns_status(ans->status);
764 	if (status != dns_success)
765 		return status;
766 
767 	dns_reply_init(reply, dns_reply_str, ans->nrrs);
768 	for (i = 0; i < ans->nrrs; i++)
769 		reply->data.str[i] = mu_strdup(ans->rrs.str[i]);
770 	free(ans);
771 
772 	if (resolve)
773 		status = dns_reply_resolve(reply);
774 
775 	return status;
776 }
777