1 /* $NetBSD: dns_lookup.c,v 1.8 2023/12/23 20:30:43 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* dns_lookup 3
6 /* SUMMARY
7 /* domain name service lookup
8 /* SYNOPSIS
9 /* #include <dns.h>
10 /*
11 /* int dns_lookup(name, type, rflags, list, fqdn, why)
12 /* const char *name;
13 /* unsigned type;
14 /* unsigned rflags;
15 /* DNS_RR **list;
16 /* VSTRING *fqdn;
17 /* VSTRING *why;
18 /*
19 /* int dns_lookup_l(name, rflags, list, fqdn, why, lflags, ltype, ...)
20 /* const char *name;
21 /* unsigned rflags;
22 /* DNS_RR **list;
23 /* VSTRING *fqdn;
24 /* VSTRING *why;
25 /* int lflags;
26 /* unsigned ltype;
27 /*
28 /* int dns_lookup_v(name, rflags, list, fqdn, why, lflags, ltype)
29 /* const char *name;
30 /* unsigned rflags;
31 /* DNS_RR **list;
32 /* VSTRING *fqdn;
33 /* VSTRING *why;
34 /* int lflags;
35 /* unsigned *ltype;
36 /*
37 /* int dns_get_h_errno()
38 /* AUXILIARY FUNCTIONS
39 /* extern int var_dns_ncache_ttl_fix;
40 /*
41 /* int dns_lookup_r(name, type, rflags, list, fqdn, why, rcode)
42 /* const char *name;
43 /* unsigned type;
44 /* unsigned rflags;
45 /* DNS_RR **list;
46 /* VSTRING *fqdn;
47 /* VSTRING *why;
48 /* int *rcode;
49 /*
50 /* int dns_lookup_rl(name, rflags, list, fqdn, why, rcode, lflags,
51 /* ltype, ...)
52 /* const char *name;
53 /* unsigned rflags;
54 /* DNS_RR **list;
55 /* VSTRING *fqdn;
56 /* VSTRING *why;
57 /* int *rcode;
58 /* int lflags;
59 /* unsigned ltype;
60 /*
61 /* int dns_lookup_rv(name, rflags, list, fqdn, why, rcode, lflags,
62 /* ltype)
63 /* const char *name;
64 /* unsigned rflags;
65 /* DNS_RR **list;
66 /* VSTRING *fqdn;
67 /* VSTRING *why;
68 /* int *rcode;
69 /* int lflags;
70 /* unsigned *ltype;
71 /*
72 /* int dns_lookup_x(name, type, rflags, list, fqdn, why, rcode, lflags)
73 /* const char *name;
74 /* unsigned type;
75 /* unsigned rflags;
76 /* DNS_RR **list;
77 /* VSTRING *fqdn;
78 /* VSTRING *why;
79 /* int *rcode;
80 /* unsigned lflags;
81 /* DESCRIPTION
82 /* dns_lookup() looks up DNS resource records. When requested to
83 /* look up data other than type CNAME, it will follow a limited
84 /* number of CNAME indirections. All result names (including
85 /* null terminator) will fit a buffer of size DNS_NAME_LEN.
86 /* All name results are validated by \fIvalid_hostname\fR();
87 /* an invalid name is reported as a DNS_INVAL result, while
88 /* malformed replies are reported as transient errors.
89 /*
90 /* dns_get_h_errno() returns the last error. This deprecates
91 /* usage of the global h_errno variable. We should not rely
92 /* on that being updated.
93 /*
94 /* dns_lookup_l() and dns_lookup_v() allow the user to specify
95 /* a list of resource types.
96 /*
97 /* dns_lookup_x, dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv()
98 /* accept or return additional information.
99 /*
100 /* The var_dns_ncache_ttl_fix variable controls a workaround
101 /* for res_search(3) implementations that break the
102 /* DNS_REQ_FLAG_NCACHE_TTL feature. The workaround does not
103 /* support EDNS0 or DNSSEC, but it should be sufficient for
104 /* DNSBL/DNSWL lookups.
105 /* INPUTS
106 /* .ad
107 /* .fi
108 /* .IP name
109 /* The name to be looked up in the domain name system.
110 /* This name must pass the valid_hostname() test; it
111 /* must not be an IP address.
112 /* .IP type
113 /* The resource record type to be looked up (T_A, T_MX etc.).
114 /* .IP rflags
115 /* Resolver flags. These are a bitwise OR of:
116 /* .RS
117 /* .IP RES_DEBUG
118 /* Print debugging information.
119 /* .IP RES_DNSRCH
120 /* Search local domain and parent domains.
121 /* .IP RES_DEFNAMES
122 /* Append local domain to unqualified names.
123 /* .IP RES_USE_DNSSEC
124 /* Request DNSSEC validation. This flag is silently ignored
125 /* when the system stub resolver API, resolver(3), does not
126 /* implement DNSSEC.
127 /* Automatically turns on the RES_TRUSTAD flag on systems that
128 /* support this flag (this behavior will be more configurable
129 /* in a later release).
130 /* .RE
131 /* .IP lflags
132 /* Flags that control the operation of the dns_lookup*()
133 /* functions. DNS_REQ_FLAG_NONE requests no special processing.
134 /* Otherwise, specify one or more of the following:
135 /* .RS
136 /* .IP DNS_REQ_FLAG_STOP_INVAL
137 /* This flag is used by dns_lookup_l() and dns_lookup_v().
138 /* Invoke dns_lookup() for the resource types in the order as
139 /* specified, and return when dns_lookup() returns DNS_INVAL.
140 /* .IP DNS_REQ_FLAG_STOP_NULLMX
141 /* This flag is used by dns_lookup_l() and dns_lookup_v().
142 /* Invoke dns_lookup() for the resource types in the order as
143 /* specified, and return when dns_lookup() returns DNS_NULLMX.
144 /* .IP DNS_REQ_FLAG_STOP_MX_POLICY
145 /* This flag is used by dns_lookup_l() and dns_lookup_v().
146 /* Invoke dns_lookup() for the resource types in the order as
147 /* specified, and return when dns_lookup() returns DNS_POLICY
148 /* for an MX query.
149 /* .IP DNS_REQ_FLAG_STOP_OK
150 /* This flag is used by dns_lookup_l() and dns_lookup_v().
151 /* Invoke dns_lookup() for the resource types in the order as
152 /* specified, and return when dns_lookup() returns DNS_OK.
153 /* .IP DNS_REQ_FLAG_NCACHE_TTL
154 /* When the lookup result status is DNS_NOTFOUND, return the
155 /* SOA record(s) from the authority section in the reply, if
156 /* available. The per-record reply TTL specifies how long the
157 /* DNS_NOTFOUND answer is valid. The caller should pass the
158 /* record(s) to dns_rr_free().
159 /* Logs a warning if the RES_DNSRCH or RES_DEFNAMES resolver
160 /* flags are set, and disables those flags.
161 /* .RE
162 /* .IP ltype
163 /* The resource record types to be looked up. In the case of
164 /* dns_lookup_l(), this is a null-terminated argument list.
165 /* In the case of dns_lookup_v(), this is a null-terminated
166 /* integer array.
167 /* OUTPUTS
168 /* .ad
169 /* .fi
170 /* .IP list
171 /* A null pointer, or a pointer to a variable that receives a
172 /* list of requested resource records.
173 /* .IP fqdn
174 /* A null pointer, or storage for the fully-qualified domain
175 /* name found for \fIname\fR.
176 /* .IP why
177 /* A null pointer, or storage for the reason for failure.
178 /* .IP rcode
179 /* Pointer to storage for the reply RCODE value. This gives
180 /* more detailed information than DNS_FAIL, DNS_RETRY, etc.
181 /* DIAGNOSTICS
182 /* If DNSSEC validation is requested but the response is not
183 /* DNSSEC validated, dns_lookup() will send a one-time probe
184 /* query as configured with the \fBdnssec_probe\fR configuration
185 /* parameter, and will log a warning when the probe response
186 /* was not DNSSEC validated.
187 /* .PP
188 /* dns_lookup() returns one of the following codes and sets the
189 /* \fIwhy\fR argument accordingly:
190 /* .IP DNS_OK
191 /* The DNS query succeeded.
192 /* .IP DNS_POLICY
193 /* The DNS query succeeded, but the answer did not pass the
194 /* policy filter.
195 /* .IP DNS_NOTFOUND
196 /* The DNS query succeeded; the requested information was not found.
197 /* .IP DNS_NULLMX
198 /* The DNS query succeeded; the requested service is unavailable.
199 /* This is returned when the list argument is not a null
200 /* pointer, and an MX lookup result contains a null server
201 /* name (so-called "nullmx" record).
202 /* .IP DNS_INVAL
203 /* The DNS query succeeded; the result failed the valid_hostname() test.
204 /*
205 /* NOTE: the valid_hostname() test is skipped for results that
206 /* the caller suppresses explicitly. For example, when the
207 /* caller requests MX record lookup but specifies a null
208 /* resource record list argument, no syntax check will be done
209 /* for MX server names.
210 /* .IP DNS_RETRY
211 /* The query failed, or the reply was malformed.
212 /* The problem is considered transient.
213 /* .IP DNS_FAIL
214 /* The query failed.
215 /* BUGS
216 /* dns_lookup() implements a subset of all possible resource types:
217 /* CNAME, MX, A, and some records with similar formatting requirements.
218 /* It is unwise to specify the T_ANY wildcard resource type.
219 /*
220 /* It takes a surprising amount of code to accomplish what appears
221 /* to be a simple task. Later versions of the mail system may implement
222 /* their own DNS client software.
223 /* SEE ALSO
224 /* dns_rr(3) resource record memory and list management
225 /* LICENSE
226 /* .ad
227 /* .fi
228 /* The Secure Mailer license must be distributed with this software.
229 /* AUTHOR(S)
230 /* Wietse Venema
231 /* IBM T.J. Watson Research
232 /* P.O. Box 704
233 /* Yorktown Heights, NY 10598, USA
234 /*
235 /* Wietse Venema
236 /* Google, Inc.
237 /* 111 8th Avenue
238 /* New York, NY 10011, USA
239 /*
240 /* SRV Support by
241 /* Tomas Korbar
242 /* Red Hat, Inc.
243 /*--*/
244
245 /* System library. */
246
247 #include <sys_defs.h>
248 #include <netdb.h>
249 #include <string.h>
250 #include <ctype.h>
251
252 /* Utility library. */
253
254 #include <mymalloc.h>
255 #include <vstring.h>
256 #include <msg.h>
257 #include <valid_hostname.h>
258 #include <stringops.h>
259
260 /* Global library. */
261
262 #include <mail_params.h>
263
264 /* DNS library. */
265
266 #define LIBDNS_INTERNAL
267 #include "dns.h"
268
269 /* Local stuff. */
270
271 /*
272 * Structure to keep track of things while decoding a name server reply.
273 */
274 #define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */
275 #define MAX_DNS_REPLY_SIZE 65536 /* in case we're using TCP */
276 #define MAX_DNS_QUERY_SIZE 2048 /* XXX */
277
278 typedef struct DNS_REPLY {
279 unsigned char *buf; /* raw reply data */
280 size_t buf_len; /* reply buffer length */
281 int rcode; /* unfiltered reply code */
282 int dnssec_ad; /* DNSSEC AD bit */
283 int query_count; /* number of queries */
284 int answer_count; /* number of answers */
285 int auth_count; /* number of authority records */
286 unsigned char *query_start; /* start of query data */
287 unsigned char *answer_start; /* start of answer data */
288 unsigned char *end; /* first byte past reply */
289 } DNS_REPLY;
290
291 /*
292 * Test/set primitives to determine if the reply buffer contains a server
293 * response. We use this when the caller requests DNS_REQ_FLAG_NCACHE_TTL,
294 * and the DNS server replies that the requested record does not exist.
295 */
296 #define TEST_HAVE_DNS_REPLY_PACKET(r) ((r)->end > (r)->buf)
297 #define SET_HAVE_DNS_REPLY_PACKET(r, l) ((r)->end = (r)->buf + (l))
298 #define SET_NO_DNS_REPLY_PACKET(r) ((r)->end = (r)->buf)
299
300 #define INET_ADDR_LEN 4 /* XXX */
301 #define INET6_ADDR_LEN 16 /* XXX */
302
303 /*
304 * Use the threadsafe resolver API if available, not because it is
305 * theadsafe, but because it has more functionality.
306 */
307 #ifdef USE_RES_NCALLS
308 static struct __res_state dns_res_state;
309
310 #define DNS_RES_NINIT res_ninit
311 #define DNS_RES_NMKQUERY res_nmkquery
312 #define DNS_RES_NSEARCH res_nsearch
313 #define DNS_RES_NSEND res_nsend
314 #define DNS_GET_H_ERRNO(statp) ((statp)->res_h_errno)
315
316 /*
317 * Alias new resolver API calls to the legacy resolver API which stores
318 * resolver and error state in global variables.
319 */
320 #else
321 #define dns_res_state _res
322 #define DNS_RES_NINIT(statp) res_init()
323 #define DNS_RES_NMKQUERY(statp, op, dname, class, type, data, datalen, \
324 newrr, buf, buflen) \
325 res_mkquery((op), (dname), (class), (type), (data), (datalen), \
326 (newrr), (buf), (buflen))
327 #define DNS_RES_NSEARCH(statp, dname, class, type, answer, anslen) \
328 res_search((dname), (class), (type), (answer), (anslen))
329 #define DNS_RES_NSEND(statp, msg, msglen, answer, anslen) \
330 res_send((msg), (msglen), (answer), (anslen))
331 #define DNS_GET_H_ERRNO(statp) (h_errno)
332 #endif
333
334 #ifdef USE_SET_H_ERRNO
335 #define DNS_SET_H_ERRNO(statp, err) (set_h_errno(err))
336 #else
337 #define DNS_SET_H_ERRNO(statp, err) (DNS_GET_H_ERRNO(statp) = (err))
338 #endif
339
340 /*
341 * To improve postscreen's allowlisting support, we need to know how long a
342 * DNSBL "not found" answer is valid. The 2010 implementation assumed it was
343 * valid for 3600 seconds. That is too long by 2015 standards.
344 *
345 * Instead of guessing, Postfix 3.1 and later implement RFC 2308 (DNS NCACHE),
346 * where a DNS server provides the TTL of a "not found" response as the TTL
347 * of an SOA record in the authority section.
348 *
349 * Unfortunately, the res_search() and res_query() API gets in the way. These
350 * functions overload their result value, the server reply length, and
351 * return -1 when the requested record does not exist. With libbind-based
352 * implementations, the server response is still available in an application
353 * buffer, thanks to the promise that res_query() and res_search() invoke
354 * res_send(), which returns the full server response even if the requested
355 * record does not exist.
356 *
357 * If this promise is broken (for example, res_search() does not call
358 * res_send(), but some non-libbind implementation that updates the
359 * application buffer only when the requested record exists), then we have a
360 * way out by setting the var_dns_ncache_ttl_fix variable. This enables a
361 * limited res_query() clone that should be sufficient for DNSBL / DNSWL
362 * lookups.
363 *
364 * The libunbound API does not comingle the reply length and reply status
365 * information, but that will have to wait until it is safe to make
366 * libunbound a mandatory dependency for Postfix.
367 */
368 #ifdef HAVE_RES_SEND
369
370 /* dns_neg_query - a res_query() clone that can return negative replies */
371
dns_neg_query(const char * name,int class,int type,unsigned char * answer,int anslen)372 static int dns_neg_query(const char *name, int class, int type,
373 unsigned char *answer, int anslen)
374 {
375 unsigned char msg_buf[MAX_DNS_QUERY_SIZE];
376 HEADER *reply_header = (HEADER *) answer;
377 int len;
378
379 /*
380 * Differences with res_query() from libbind:
381 *
382 * - This function returns a positive server reply length not only in case
383 * of success, but in all cases where a server reply is available that
384 * passes the preliminary checks in res_send().
385 *
386 * - This function clears h_errno in case of success. The caller must use
387 * h_errno instead of the return value to decide if the lookup was
388 * successful.
389 *
390 * - No support for EDNS0 and DNSSEC (including turning off EDNS0 after
391 * error). That should be sufficient for DNS reputation lookups where the
392 * reply contains a small number of IP addresses. TXT records are out of
393 * scope for this workaround.
394 */
395 reply_header->rcode = NOERROR;
396
397 #define NO_MKQUERY_DATA_BUF ((unsigned char *) 0)
398 #define NO_MKQUERY_DATA_LEN ((int) 0)
399 #define NO_MKQUERY_NEWRR ((unsigned char *) 0)
400
401 if ((len = DNS_RES_NMKQUERY(&dns_res_state,
402 QUERY, name, class, type, NO_MKQUERY_DATA_BUF,
403 NO_MKQUERY_DATA_LEN, NO_MKQUERY_NEWRR,
404 msg_buf, sizeof(msg_buf))) < 0) {
405 DNS_SET_H_ERRNO(&dns_res_state, NO_RECOVERY);
406 if (msg_verbose)
407 msg_info("res_nmkquery() failed");
408 return (len);
409 } else if ((len = DNS_RES_NSEND(&dns_res_state,
410 msg_buf, len, answer, anslen)) < 0) {
411 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN);
412 if (msg_verbose)
413 msg_info("res_nsend() failed");
414 return (len);
415 } else {
416 switch (reply_header->rcode) {
417 case NXDOMAIN:
418 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND);
419 break;
420 case NOERROR:
421 if (reply_header->ancount != 0)
422 DNS_SET_H_ERRNO(&dns_res_state, 0);
423 else
424 DNS_SET_H_ERRNO(&dns_res_state, NO_DATA);
425 break;
426 case SERVFAIL:
427 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN);
428 break;
429 default:
430 DNS_SET_H_ERRNO(&dns_res_state, NO_RECOVERY);
431 break;
432 }
433 return (len);
434 }
435 }
436
437 #endif
438
439 /* dns_neg_search - res_search() that can return negative replies */
440
dns_neg_search(const char * name,int class,int type,unsigned char * answer,int anslen,int keep_notfound)441 static int dns_neg_search(const char *name, int class, int type,
442 unsigned char *answer, int anslen, int keep_notfound)
443 {
444 int len;
445
446 /*
447 * Differences with res_search() from libbind:
448 *
449 * - With a non-zero keep_notfound argument, this function returns a
450 * positive server reply length not only in case of success, but also in
451 * case of a "notfound" reply status. The keep_notfound argument is
452 * usually zero, which allows us to avoid an unnecessary memset() call in
453 * the most common use case.
454 *
455 * - This function clears h_errno in case of success. The caller must use
456 * h_errno instead of the return value to decide if a lookup was
457 * successful.
458 */
459 #define NOT_FOUND_H_ERRNO(he) ((he) == HOST_NOT_FOUND || (he) == NO_DATA)
460
461 if (keep_notfound)
462 /* Prepare for returning a null-padded server reply. */
463 memset(answer, 0, anslen);
464 len = DNS_RES_NSEARCH(&dns_res_state, name, class, type, answer, anslen);
465 /* Begin API creep workaround. */
466 if (len < 0 && DNS_GET_H_ERRNO(&dns_res_state) == 0) {
467 DNS_SET_H_ERRNO(&dns_res_state, TRY_AGAIN);
468 msg_warn("res_nsearch(state, \"%s\", %d, %d, %p, %d) returns %d"
469 " with h_errno==0 -- setting h_errno=TRY_AGAIN",
470 name, class, type, answer, anslen, len);
471 }
472 /* End API creep workaround. */
473 if (len > 0) {
474 DNS_SET_H_ERRNO(&dns_res_state, 0);
475 } else if (keep_notfound
476 && NOT_FOUND_H_ERRNO(DNS_GET_H_ERRNO(&dns_res_state))) {
477 /* Expect to return a null-padded server reply. */
478 len = anslen;
479 }
480 return (len);
481 }
482
483 /* dns_query - query name server and pre-parse the reply */
484
dns_query(const char * name,int type,unsigned flags,DNS_REPLY * reply,VSTRING * why,unsigned lflags)485 static int dns_query(const char *name, int type, unsigned flags,
486 DNS_REPLY *reply, VSTRING *why, unsigned lflags)
487 {
488 HEADER *reply_header;
489 int len;
490 unsigned long saved_options;
491 int keep_notfound = (lflags & DNS_REQ_FLAG_NCACHE_TTL);
492
493 /*
494 * Initialize the reply buffer.
495 */
496 if (reply->buf == 0) {
497 reply->buf = (unsigned char *) mymalloc(DEF_DNS_REPLY_SIZE);
498 reply->buf_len = DEF_DNS_REPLY_SIZE;
499 }
500
501 /*
502 * Initialize the name service.
503 */
504 if ((dns_res_state.options & RES_INIT) == 0
505 && DNS_RES_NINIT(&dns_res_state) < 0) {
506 if (why)
507 vstring_strcpy(why, "Name service initialization failure");
508 return (DNS_FAIL);
509 }
510
511 /*
512 * Set search options: debugging, parent domain search, append local
513 * domain. Do not allow the user to control other features.
514 */
515 #define USER_FLAGS (RES_DEBUG | RES_DNSRCH | RES_DEFNAMES | RES_USE_DNSSEC)
516
517 if ((flags & USER_FLAGS) != flags)
518 msg_panic("dns_query: bad flags: %d", flags);
519
520 /*
521 * Set extra options that aren't exposed to the application.
522 */
523 #define XTRA_FLAGS (RES_USE_EDNS0 | RES_TRUSTAD)
524
525 if (DNS_WANT_DNSSEC_VALIDATION(flags))
526 flags |= (RES_USE_EDNS0 | RES_TRUSTAD);
527
528 /*
529 * Can't append domains: we need the right SOA TTL.
530 */
531 #define APPEND_DOMAIN_FLAGS (RES_DNSRCH | RES_DEFNAMES)
532
533 if (keep_notfound && (flags & APPEND_DOMAIN_FLAGS)) {
534 msg_warn("negative caching disables RES_DEFNAMES and RES_DNSRCH");
535 flags &= ~APPEND_DOMAIN_FLAGS;
536 }
537
538 /*
539 * Save and restore resolver options that we overwrite, to avoid
540 * surprising behavior in other code that also invokes the resolver.
541 */
542 #define SAVE_FLAGS (USER_FLAGS | XTRA_FLAGS)
543
544 saved_options = (dns_res_state.options & SAVE_FLAGS);
545
546 /*
547 * Perform the lookup. Claim that the information cannot be found if and
548 * only if the name server told us so.
549 */
550 for (;;) {
551 dns_res_state.options &= ~saved_options;
552 dns_res_state.options |= flags;
553 if (keep_notfound && var_dns_ncache_ttl_fix) {
554 #ifdef HAVE_RES_SEND
555 len = dns_neg_query((char *) name, C_IN, type, reply->buf,
556 reply->buf_len);
557 #else
558 var_dns_ncache_ttl_fix = 0;
559 msg_warn("system library does not support %s=yes"
560 " -- ignoring this setting", VAR_DNS_NCACHE_TTL_FIX);
561 len = dns_neg_search((char *) name, C_IN, type, reply->buf,
562 reply->buf_len, keep_notfound);
563 #endif
564 } else {
565 len = dns_neg_search((char *) name, C_IN, type, reply->buf,
566 reply->buf_len, keep_notfound);
567 }
568 dns_res_state.options &= ~flags;
569 dns_res_state.options |= saved_options;
570 reply_header = (HEADER *) reply->buf;
571 reply->rcode = reply_header->rcode;
572 if ((reply->dnssec_ad = !!reply_header->ad) != 0)
573 DNS_SEC_STATS_SET(DNS_SEC_FLAG_AVAILABLE);
574 if (DNS_GET_H_ERRNO(&dns_res_state) != 0) {
575 if (why)
576 vstring_sprintf(why, "Host or domain name not found. "
577 "Name service error for name=%s type=%s: %s",
578 name, dns_strtype(type),
579 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state)));
580 if (msg_verbose)
581 msg_info("dns_query: %s (%s): %s",
582 name, dns_strtype(type),
583 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state)));
584 switch (DNS_GET_H_ERRNO(&dns_res_state)) {
585 case NO_RECOVERY:
586 return (DNS_FAIL);
587 case HOST_NOT_FOUND:
588 case NO_DATA:
589 if (keep_notfound)
590 break;
591 SET_NO_DNS_REPLY_PACKET(reply);
592 return (DNS_NOTFOUND);
593 default:
594 return (DNS_RETRY);
595 }
596 } else {
597 if (msg_verbose)
598 msg_info("dns_query: %s (%s): OK", name, dns_strtype(type));
599 }
600
601 if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE)
602 break;
603 reply->buf = (unsigned char *)
604 myrealloc((void *) reply->buf, 2 * reply->buf_len);
605 reply->buf_len *= 2;
606 }
607
608 /*
609 * Future proofing. If this reaches the panic call, then some code change
610 * introduced a bug.
611 */
612 if (len < 0)
613 msg_panic("dns_query: bad length %d (h_errno=%s)",
614 len, dns_strerror(DNS_GET_H_ERRNO(&dns_res_state)));
615
616 /*
617 * Paranoia.
618 */
619 if (len > reply->buf_len) {
620 msg_warn("reply length %d > buffer length %d for name=%s type=%s",
621 len, (int) reply->buf_len, name, dns_strtype(type));
622 len = reply->buf_len;
623 }
624
625 /*
626 * Initialize the reply structure. Some structure members are filled on
627 * the fly while the reply is being parsed.
628 */
629 SET_HAVE_DNS_REPLY_PACKET(reply, len);
630 reply->query_start = reply->buf + sizeof(HEADER);
631 reply->answer_start = 0;
632 reply->query_count = ntohs(reply_header->qdcount);
633 reply->answer_count = ntohs(reply_header->ancount);
634 reply->auth_count = ntohs(reply_header->nscount);
635 if (msg_verbose > 1)
636 msg_info("dns_query: reply len=%d ancount=%d nscount=%d",
637 len, reply->answer_count, reply->auth_count);
638
639 /*
640 * Future proofing. If this reaches the panic call, then some code change
641 * introduced a bug.
642 */
643 if (DNS_GET_H_ERRNO(&dns_res_state) == 0) {
644 return (DNS_OK);
645 } else if (keep_notfound) {
646 return (DNS_NOTFOUND);
647 } else {
648 msg_panic("dns_query: unexpected reply status: %s",
649 dns_strerror(DNS_GET_H_ERRNO(&dns_res_state)));
650 }
651 }
652
653 /* dns_skip_query - skip query data in name server reply */
654
dns_skip_query(DNS_REPLY * reply)655 static int dns_skip_query(DNS_REPLY *reply)
656 {
657 int query_count = reply->query_count;
658 unsigned char *pos = reply->query_start;
659 int len;
660
661 /*
662 * For each query, skip over the domain name and over the fixed query
663 * data.
664 */
665 while (query_count-- > 0) {
666 if (pos >= reply->end)
667 return DNS_RETRY;
668 len = dn_skipname(pos, reply->end);
669 if (len < 0)
670 return (DNS_RETRY);
671 pos += len + QFIXEDSZ;
672 }
673 reply->answer_start = pos;
674 return (DNS_OK);
675 }
676
677 /* dns_get_fixed - extract fixed data from resource record */
678
dns_get_fixed(unsigned char * pos,DNS_FIXED * fixed)679 static int dns_get_fixed(unsigned char *pos, DNS_FIXED *fixed)
680 {
681 GETSHORT(fixed->type, pos);
682 GETSHORT(fixed->class, pos);
683 GETLONG(fixed->ttl, pos);
684 GETSHORT(fixed->length, pos);
685
686 if (fixed->class != C_IN) {
687 msg_warn("dns_get_fixed: bad class: %u", fixed->class);
688 return (DNS_RETRY);
689 }
690 return (DNS_OK);
691 }
692
693 /* valid_rr_name - validate hostname in resource record */
694
valid_rr_name(const char * name,const char * location,unsigned type,DNS_REPLY * reply)695 static int valid_rr_name(const char *name, const char *location,
696 unsigned type, DNS_REPLY *reply)
697 {
698 char temp[DNS_NAME_LEN];
699 char *query_name;
700 int len;
701 char *gripe;
702 int result;
703
704 /*
705 * People aren't supposed to specify numeric names where domain names are
706 * required, but it "works" with some mailers anyway, so people complain
707 * when software doesn't bend over backwards.
708 */
709 #define PASS_NAME 1
710 #define REJECT_NAME 0
711
712 if (valid_hostaddr(name, DONT_GRIPE)) {
713 result = PASS_NAME;
714 gripe = "numeric domain name";
715 } else if (!valid_hostname(name, DO_GRIPE | DO_WILDCARD)) {
716 result = REJECT_NAME;
717 gripe = "malformed domain name";
718 } else {
719 result = PASS_NAME;
720 gripe = 0;
721 }
722
723 /*
724 * If we have a gripe, show some context, including the name used in the
725 * query and the type of reply that we're looking at.
726 */
727 if (gripe) {
728 len = dn_expand(reply->buf, reply->end, reply->query_start,
729 temp, DNS_NAME_LEN);
730 query_name = (len < 0 ? "*unparsable*" : temp);
731 msg_warn("%s in %s of %s record for %s: %.100s",
732 gripe, location, dns_strtype(type), query_name, name);
733 }
734 return (result);
735 }
736
737 /* dns_get_rr - extract resource record from name server reply */
738
dns_get_rr(DNS_RR ** list,const char * orig_name,DNS_REPLY * reply,unsigned char * pos,char * rr_name,DNS_FIXED * fixed)739 static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply,
740 unsigned char *pos, char *rr_name,
741 DNS_FIXED *fixed)
742 {
743 char temp[DNS_NAME_LEN];
744 char *tempbuf = temp;
745 UINT32_TYPE soa_buf[5];
746 int comp_len;
747 ssize_t data_len;
748 unsigned pref = 0;
749 unsigned weight = 0;
750 unsigned port = 0;
751 unsigned char *src;
752 unsigned char *dst;
753 int ch;
754
755 #define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b))
756
757 *list = 0;
758
759 switch (fixed->type) {
760 default:
761 msg_panic("dns_get_rr: don't know how to extract resource type %s",
762 dns_strtype(fixed->type));
763 case T_CNAME:
764 case T_DNAME:
765 case T_MB:
766 case T_MG:
767 case T_MR:
768 case T_NS:
769 case T_PTR:
770 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
771 return (DNS_RETRY);
772 if (!valid_rr_name(temp, "resource data", fixed->type, reply))
773 return (DNS_INVAL);
774 data_len = strlen(temp) + 1;
775 break;
776 case T_SRV:
777 GETSHORT(pref, pos);
778 GETSHORT(weight, pos);
779 GETSHORT(port, pos);
780 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
781 return (DNS_RETRY);
782 if (*temp == 0)
783 return (DNS_NULLSRV);
784 if (!valid_rr_name(temp, "resource data", fixed->type, reply))
785 return (DNS_INVAL);
786 data_len = strlen(temp) + 1;
787 break;
788 case T_MX:
789 GETSHORT(pref, pos);
790 if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
791 return (DNS_RETRY);
792 /* Don't even think of returning an invalid hostname to the caller. */
793 if (*temp == 0)
794 return (DNS_NULLMX); /* TODO: descriptive text */
795 if (!valid_rr_name(temp, "resource data", fixed->type, reply))
796 return (DNS_INVAL);
797 data_len = strlen(temp) + 1;
798 break;
799 case T_A:
800 if (fixed->length != INET_ADDR_LEN) {
801 msg_warn("extract_answer: bad address length: %d", fixed->length);
802 return (DNS_RETRY);
803 }
804 if (fixed->length > sizeof(temp))
805 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN",
806 fixed->length);
807 memcpy(temp, pos, fixed->length);
808 data_len = fixed->length;
809 break;
810 #ifdef T_AAAA
811 case T_AAAA:
812 if (fixed->length != INET6_ADDR_LEN) {
813 msg_warn("extract_answer: bad address length: %d", fixed->length);
814 return (DNS_RETRY);
815 }
816 if (fixed->length > sizeof(temp))
817 msg_panic("dns_get_rr: length %d > DNS_NAME_LEN",
818 fixed->length);
819 memcpy(temp, pos, fixed->length);
820 data_len = fixed->length;
821 break;
822 #endif
823
824 /*
825 * We impose the same length limit here as for DNS names. However,
826 * see T_TLSA discussion below.
827 */
828 case T_TXT:
829 data_len = MIN2(pos[0] + 1, MIN2(fixed->length + 1, sizeof(temp)));
830 for (src = pos + 1, dst = (unsigned char *) (temp);
831 dst < (unsigned char *) (temp) + data_len - 1; /* */ ) {
832 ch = *src++;
833 *dst++ = (ISPRINT(ch) ? ch : ' ');
834 }
835 *dst = 0;
836 break;
837
838 /*
839 * For a full certificate, fixed->length may be longer than
840 * sizeof(tmpbuf) == DNS_NAME_LEN. Since we don't need a decode
841 * buffer, just copy the raw data into the rr.
842 *
843 * XXX Reject replies with bogus length < 3.
844 *
845 * XXX What about enforcing a sane upper bound? The RFC 1035 hard
846 * protocol limit is the RRDATA length limit of 65535.
847 */
848 case T_TLSA:
849 data_len = fixed->length;
850 tempbuf = (char *) pos;
851 break;
852
853 /*
854 * We use the SOA record TTL to determine the negative reply TTL. We
855 * save the time fields in the SOA record for debugging, but for now
856 * we don't bother saving the source host and mailbox information, as
857 * that would require changes to the DNS_RR structure and APIs. See
858 * also code in dns_strrecord().
859 */
860 case T_SOA:
861 comp_len = dn_skipname(pos, reply->end);
862 if (comp_len < 0)
863 return (DNS_RETRY);
864 pos += comp_len;
865 comp_len = dn_skipname(pos, reply->end);
866 if (comp_len < 0)
867 return (DNS_RETRY);
868 pos += comp_len;
869 if (reply->end - pos < sizeof(soa_buf)) {
870 msg_warn("extract_answer: bad SOA length: %d", fixed->length);
871 return (DNS_RETRY);
872 }
873 GETLONG(soa_buf[0], pos); /* Serial */
874 GETLONG(soa_buf[1], pos); /* Refresh */
875 GETLONG(soa_buf[2], pos); /* Retry */
876 GETLONG(soa_buf[3], pos); /* Expire */
877 GETLONG(soa_buf[4], pos); /* Ncache TTL */
878 tempbuf = (char *) soa_buf;
879 data_len = sizeof(soa_buf);
880 break;
881 }
882 *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class,
883 fixed->ttl, pref, weight, port, tempbuf, data_len);
884 return (DNS_OK);
885 }
886
887 /* dns_get_alias - extract CNAME from name server reply */
888
dns_get_alias(DNS_REPLY * reply,unsigned char * pos,DNS_FIXED * fixed,char * cname,int c_len)889 static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos,
890 DNS_FIXED *fixed, char *cname, int c_len)
891 {
892 if (fixed->type != T_CNAME)
893 msg_panic("dns_get_alias: bad type %s", dns_strtype(fixed->type));
894 if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0)
895 return (DNS_RETRY);
896 if (!valid_rr_name(cname, "resource data", fixed->type, reply))
897 return (DNS_INVAL);
898 return (DNS_OK);
899 }
900
901 /* dns_get_answer - extract answers from name server reply */
902
dns_get_answer(const char * orig_name,DNS_REPLY * reply,int type,DNS_RR ** rrlist,VSTRING * fqdn,char * cname,int c_len,int * maybe_secure)903 static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
904 DNS_RR **rrlist, VSTRING *fqdn, char *cname, int c_len,
905 int *maybe_secure)
906 {
907 char rr_name[DNS_NAME_LEN];
908 unsigned char *pos;
909 int answer_count = reply->answer_count;
910 int len;
911 DNS_FIXED fixed;
912 DNS_RR *rr;
913 int resource_found = 0;
914 int cname_found = 0;
915 int not_found_status = DNS_NOTFOUND; /* can't happen */
916 int status;
917
918 /*
919 * Initialize. Skip over the name server query if we haven't yet.
920 */
921 if (reply->answer_start == 0)
922 if ((status = dns_skip_query(reply)) < 0)
923 return (status);
924 pos = reply->answer_start;
925
926 /*
927 * Either this, or use a GOTO for emergency exits. The purpose is to
928 * prevent incomplete answers from being passed back to the caller.
929 */
930 #define CORRUPT(status) { \
931 if (rrlist && *rrlist) { \
932 dns_rr_free(*rrlist); \
933 *rrlist = 0; \
934 } \
935 return (status); \
936 }
937
938 /*
939 * Iterate over all answers.
940 */
941 while (answer_count-- > 0) {
942
943 /*
944 * Optionally extract the fully-qualified domain name.
945 */
946 if (pos >= reply->end)
947 CORRUPT(DNS_RETRY);
948 len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN);
949 if (len < 0)
950 CORRUPT(DNS_RETRY);
951 pos += len;
952
953 /*
954 * Extract the fixed reply data: type, class, ttl, length.
955 */
956 if (pos + RRFIXEDSZ > reply->end)
957 CORRUPT(DNS_RETRY);
958 if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK)
959 CORRUPT(status);
960 if (strcmp(orig_name, ".") == 0 && *rr_name == 0)
961 /* Allow empty response name for root queries. */ ;
962 else if (!valid_rr_name(rr_name, "resource name", fixed.type, reply))
963 CORRUPT(DNS_INVAL);
964 if (fqdn)
965 vstring_strcpy(fqdn, rr_name);
966 if (msg_verbose)
967 msg_info("dns_get_answer: type %s for %s",
968 dns_strtype(fixed.type), rr_name);
969 pos += RRFIXEDSZ;
970
971 /*
972 * Optionally extract the requested resource or CNAME data.
973 */
974 if (pos + fixed.length > reply->end)
975 CORRUPT(DNS_RETRY);
976 if (type == fixed.type || type == T_ANY) { /* requested type */
977 if (rrlist) {
978 if ((status = dns_get_rr(&rr, orig_name, reply, pos, rr_name,
979 &fixed)) == DNS_OK) {
980 resource_found++;
981 rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
982 *rrlist = dns_rr_append(*rrlist, rr);
983 } else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
984 CORRUPT(status); /* TODO: use better name */
985 } else if (not_found_status != DNS_RETRY)
986 not_found_status = status;
987 } else
988 resource_found++;
989 } else if (fixed.type == T_CNAME) { /* cname resource */
990 cname_found++;
991 if (cname && c_len > 0)
992 if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK)
993 CORRUPT(status);
994 if (!reply->dnssec_ad)
995 *maybe_secure = 0;
996 }
997 pos += fixed.length;
998 }
999
1000 /*
1001 * See what answer we came up with. Report success when the requested
1002 * information was found. Otherwise, when a CNAME was found, report that
1003 * more recursion is needed. Otherwise report failure.
1004 */
1005 if (resource_found)
1006 return (DNS_OK);
1007 if (cname_found)
1008 return (DNS_RECURSE);
1009 return (not_found_status);
1010 }
1011
1012 /* dns_lookup_x - DNS lookup user interface */
1013
dns_lookup_x(const char * name,unsigned type,unsigned flags,DNS_RR ** rrlist,VSTRING * fqdn,VSTRING * why,int * rcode,unsigned lflags)1014 int dns_lookup_x(const char *name, unsigned type, unsigned flags,
1015 DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why,
1016 int *rcode, unsigned lflags)
1017 {
1018 char cname[DNS_NAME_LEN];
1019 int c_len = sizeof(cname);
1020 static DNS_REPLY reply;
1021 int count;
1022 int status;
1023 int maybe_secure = 1; /* Query name presumed secure */
1024 const char *orig_name = name;
1025
1026 /*
1027 * Reset results early. DNS_OK is not the only status that returns
1028 * resource records; DNS_NOTFOUND will do that too, if requested.
1029 */
1030 if (rrlist)
1031 *rrlist = 0;
1032
1033 /*
1034 * DJBDNS produces a bogus A record when given a numerical hostname.
1035 */
1036 if (valid_hostaddr(name, DONT_GRIPE)) {
1037 if (why)
1038 vstring_sprintf(why,
1039 "Name service error for %s: invalid host or domain name",
1040 name);
1041 if (rcode)
1042 *rcode = NXDOMAIN;
1043 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND);
1044 return (DNS_NOTFOUND);
1045 }
1046
1047 /*
1048 * The Linux resolver misbehaves when given an invalid domain name.
1049 */
1050 if (strcmp(name, ".") && !valid_hostname(name, DONT_GRIPE | DO_WILDCARD)) {
1051 if (why)
1052 vstring_sprintf(why,
1053 "Name service error for %s: invalid host or domain name",
1054 name);
1055 if (rcode)
1056 *rcode = NXDOMAIN;
1057 DNS_SET_H_ERRNO(&dns_res_state, HOST_NOT_FOUND);
1058 return (DNS_NOTFOUND);
1059 }
1060
1061 /*
1062 * Perform the lookup. Follow CNAME chains, but only up to a
1063 * pre-determined maximum.
1064 */
1065 for (count = 0; count < 10; count++) {
1066
1067 /*
1068 * Perform the DNS lookup, and pre-parse the name server reply.
1069 */
1070 status = dns_query(name, type, flags, &reply, why, lflags);
1071 if (rcode)
1072 *rcode = reply.rcode;
1073 if (status != DNS_OK) {
1074
1075 /*
1076 * If the record does not exist, and we have a copy of the server
1077 * response, try to extract the negative caching TTL for the SOA
1078 * record in the authority section. DO NOT return an error if an
1079 * SOA record is malformed.
1080 */
1081 if (status == DNS_NOTFOUND && TEST_HAVE_DNS_REPLY_PACKET(&reply)
1082 && reply.auth_count > 0) {
1083 reply.answer_count = reply.auth_count; /* XXX TODO: Fix API */
1084 (void) dns_get_answer(orig_name, &reply, T_SOA, rrlist, fqdn,
1085 cname, c_len, &maybe_secure);
1086 }
1087 if (DNS_WANT_DNSSEC_VALIDATION(flags)
1088 && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \
1089 DNS_SEC_FLAG_DONT_PROBE))
1090 dns_sec_probe(flags); /* XXX Clobbers 'reply' */
1091 return (status);
1092 }
1093
1094 /*
1095 * Extract resource records of the requested type. Pick up CNAME
1096 * information just in case the requested data is not found.
1097 */
1098 status = dns_get_answer(orig_name, &reply, type, rrlist, fqdn,
1099 cname, c_len, &maybe_secure);
1100 if (DNS_WANT_DNSSEC_VALIDATION(flags)
1101 && !DNS_SEC_STATS_TEST(DNS_SEC_FLAG_AVAILABLE | \
1102 DNS_SEC_FLAG_DONT_PROBE))
1103 dns_sec_probe(flags); /* XXX Clobbers 'reply' */
1104 switch (status) {
1105 default:
1106 if (why)
1107 vstring_sprintf(why, "Name service error for name=%s type=%s: "
1108 "Malformed or unexpected name server reply",
1109 name, dns_strtype(type));
1110 return (status);
1111 case DNS_NULLMX:
1112 if (why)
1113 vstring_sprintf(why, "Domain %s does not accept mail (nullMX)",
1114 name);
1115 DNS_SET_H_ERRNO(&dns_res_state, NO_DATA);
1116 return (status);
1117 case DNS_NULLSRV:
1118 if (why)
1119 vstring_sprintf(why, "Domain %s does not support SRV requests",
1120 name);
1121 DNS_SET_H_ERRNO(&dns_res_state, NO_DATA);
1122 return (status);
1123 case DNS_OK:
1124 if (rrlist && dns_rr_filter_maps) {
1125 if (dns_rr_filter_execute(rrlist) < 0) {
1126 if (why)
1127 vstring_sprintf(why,
1128 "Error looking up name=%s type=%s: "
1129 "Invalid DNS reply filter syntax",
1130 name, dns_strtype(type));
1131 dns_rr_free(*rrlist);
1132 *rrlist = 0;
1133 status = DNS_RETRY;
1134 } else if (*rrlist == 0) {
1135 if (why)
1136 vstring_sprintf(why,
1137 "Error looking up name=%s type=%s: "
1138 "DNS reply filter drops all results",
1139 name, dns_strtype(type));
1140 status = DNS_POLICY;
1141 }
1142 }
1143 return (status);
1144 case DNS_RECURSE:
1145 if (msg_verbose)
1146 msg_info("dns_lookup: %s aliased to %s", name, cname);
1147 #if RES_USE_DNSSEC
1148
1149 /*
1150 * Once an intermediate CNAME reply is not validated, all
1151 * consequent RRs are deemed not validated, so we don't ask for
1152 * further DNSSEC replies.
1153 */
1154 if (maybe_secure == 0)
1155 flags &= ~RES_USE_DNSSEC;
1156 #endif
1157 name = cname;
1158 }
1159 }
1160 if (why)
1161 vstring_sprintf(why, "Name server loop for %s", name);
1162 msg_warn("dns_lookup: Name server loop for %s", name);
1163 return (DNS_NOTFOUND);
1164 }
1165
1166 /* dns_lookup_rl - DNS lookup interface with types list */
1167
dns_lookup_rl(const char * name,unsigned flags,DNS_RR ** rrlist,VSTRING * fqdn,VSTRING * why,int * rcode,int lflags,...)1168 int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
1169 VSTRING *fqdn, VSTRING *why, int *rcode,
1170 int lflags,...)
1171 {
1172 va_list ap;
1173 unsigned type, next;
1174 int status = DNS_NOTFOUND;
1175 int hpref_status = INT_MIN;
1176 VSTRING *hpref_rtext = 0;
1177 int hpref_rcode;
1178 int hpref_h_errno;
1179 DNS_RR *rr;
1180
1181 /* Save intermediate highest-priority result. */
1182 #define SAVE_HPREF_STATUS() do { \
1183 hpref_status = status; \
1184 if (rcode) \
1185 hpref_rcode = *rcode; \
1186 if (why && status != DNS_OK) \
1187 vstring_strcpy(hpref_rtext ? hpref_rtext : \
1188 (hpref_rtext = vstring_alloc(VSTRING_LEN(why))), \
1189 vstring_str(why)); \
1190 hpref_h_errno = DNS_GET_H_ERRNO(&dns_res_state); \
1191 } while (0)
1192
1193 /* Restore intermediate highest-priority result. */
1194 #define RESTORE_HPREF_STATUS() do { \
1195 status = hpref_status; \
1196 if (rcode) \
1197 *rcode = hpref_rcode; \
1198 if (why && status != DNS_OK) \
1199 vstring_strcpy(why, vstring_str(hpref_rtext)); \
1200 DNS_SET_H_ERRNO(&dns_res_state, hpref_h_errno); \
1201 } while (0)
1202
1203 if (rrlist)
1204 *rrlist = 0;
1205 va_start(ap, lflags);
1206 for (type = va_arg(ap, unsigned); type != 0; type = next) {
1207 next = va_arg(ap, unsigned);
1208 if (msg_verbose)
1209 msg_info("lookup %s type %s flags %s",
1210 name, dns_strtype(type), dns_str_resflags(flags));
1211 status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
1212 fqdn, why, rcode, lflags);
1213 if (rrlist && rr)
1214 *rrlist = dns_rr_append(*rrlist, rr);
1215 if (status == DNS_OK) {
1216 if (lflags & DNS_REQ_FLAG_STOP_OK)
1217 break;
1218 } else if (status == DNS_INVAL) {
1219 if (lflags & DNS_REQ_FLAG_STOP_INVAL)
1220 break;
1221 } else if (status == DNS_POLICY) {
1222 if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY))
1223 break;
1224 } else if (status == DNS_NULLMX) {
1225 if (lflags & DNS_REQ_FLAG_STOP_NULLMX)
1226 break;
1227 }
1228 /* XXX Stop after NXDOMAIN error. */
1229 if (next == 0)
1230 break;
1231 if (status >= hpref_status)
1232 SAVE_HPREF_STATUS(); /* save last info */
1233 }
1234 va_end(ap);
1235 if (status < hpref_status)
1236 RESTORE_HPREF_STATUS(); /* else report last info */
1237 if (hpref_rtext)
1238 vstring_free(hpref_rtext);
1239 return (status);
1240 }
1241
1242 /* dns_lookup_rv - DNS lookup interface with types vector */
1243
dns_lookup_rv(const char * name,unsigned flags,DNS_RR ** rrlist,VSTRING * fqdn,VSTRING * why,int * rcode,int lflags,unsigned * types)1244 int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
1245 VSTRING *fqdn, VSTRING *why, int *rcode,
1246 int lflags, unsigned *types)
1247 {
1248 unsigned type, next;
1249 int status = DNS_NOTFOUND;
1250 int hpref_status = INT_MIN;
1251 VSTRING *hpref_rtext = 0;
1252 int hpref_rcode;
1253 int hpref_h_errno;
1254 DNS_RR *rr;
1255
1256 if (rrlist)
1257 *rrlist = 0;
1258 for (type = *types++; type != 0; type = next) {
1259 next = *types++;
1260 if (msg_verbose)
1261 msg_info("lookup %s type %s flags %s",
1262 name, dns_strtype(type), dns_str_resflags(flags));
1263 status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
1264 fqdn, why, rcode, lflags);
1265 if (rrlist && rr)
1266 *rrlist = dns_rr_append(*rrlist, rr);
1267 if (status == DNS_OK) {
1268 if (lflags & DNS_REQ_FLAG_STOP_OK)
1269 break;
1270 } else if (status == DNS_INVAL) {
1271 if (lflags & DNS_REQ_FLAG_STOP_INVAL)
1272 break;
1273 } else if (status == DNS_POLICY) {
1274 if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY))
1275 break;
1276 } else if (status == DNS_NULLMX) {
1277 if (lflags & DNS_REQ_FLAG_STOP_NULLMX)
1278 break;
1279 }
1280 /* XXX Stop after NXDOMAIN error. */
1281 if (next == 0)
1282 break;
1283 if (status >= hpref_status)
1284 SAVE_HPREF_STATUS(); /* save last info */
1285 }
1286 if (status < hpref_status)
1287 RESTORE_HPREF_STATUS(); /* else report last info */
1288 if (hpref_rtext)
1289 vstring_free(hpref_rtext);
1290 return (status);
1291 }
1292
1293 /* dns_get_h_errno - get the last lookup status */
1294
dns_get_h_errno(void)1295 int dns_get_h_errno(void)
1296 {
1297 return (DNS_GET_H_ERRNO(&dns_res_state));
1298 }
1299