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