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