xref: /netbsd-src/external/ibm-public/postfix/dist/src/dns/dns_lookup.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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