Commit | Line | Data |
---|---|---|
0756eb3c PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
1e1ddfac | 6 | /* Copyright (c) The Exim Maintainers 2020 */ |
0756eb3c PH |
7 | /* See the file NOTICE for conditions of use and distribution. */ |
8 | ||
9 | #include "../exim.h" | |
10 | #include "lf_functions.h" | |
0756eb3c PH |
11 | |
12 | ||
13 | ||
14 | /* Ancient systems (e.g. SunOS4) don't appear to have T_TXT defined in their | |
15 | header files. */ | |
16 | ||
17 | #ifndef T_TXT | |
acec9514 | 18 | # define T_TXT 16 |
0756eb3c PH |
19 | #endif |
20 | ||
eae0036b PP |
21 | /* Many systems do not have T_SPF. */ |
22 | #ifndef T_SPF | |
acec9514 | 23 | # define T_SPF 99 |
eae0036b PP |
24 | #endif |
25 | ||
1e06383a TL |
26 | /* New TLSA record for DANE */ |
27 | #ifndef T_TLSA | |
acec9514 | 28 | # define T_TLSA 52 |
1e06383a TL |
29 | #endif |
30 | ||
0756eb3c PH |
31 | /* Table of recognized DNS record types and their integer values. */ |
32 | ||
1ba28e2b | 33 | static const char *type_names[] = { |
0756eb3c PH |
34 | "a", |
35 | #if HAVE_IPV6 | |
3a796370 | 36 | "a+", |
0756eb3c | 37 | "aaaa", |
0756eb3c PH |
38 | #endif |
39 | "cname", | |
e5a9dba6 | 40 | "csa", |
0756eb3c | 41 | "mx", |
ea3bc19b | 42 | "mxh", |
0756eb3c PH |
43 | "ns", |
44 | "ptr", | |
d2a2c69b | 45 | "soa", |
eae0036b | 46 | "spf", |
0756eb3c | 47 | "srv", |
1e06383a | 48 | "tlsa", |
33397d19 | 49 | "txt", |
8e669ac1 | 50 | "zns" |
33397d19 | 51 | }; |
0756eb3c PH |
52 | |
53 | static int type_values[] = { | |
54 | T_A, | |
55 | #if HAVE_IPV6 | |
66be95e0 | 56 | T_ADDRESSES, /* Private type for AAAA + A */ |
0756eb3c | 57 | T_AAAA, |
0756eb3c PH |
58 | #endif |
59 | T_CNAME, | |
e5a9dba6 | 60 | T_CSA, /* Private type for "Client SMTP Authorization". */ |
0756eb3c | 61 | T_MX, |
ea3bc19b | 62 | T_MXH, /* Private type for "MX hostnames" */ |
0756eb3c PH |
63 | T_NS, |
64 | T_PTR, | |
d2a2c69b | 65 | T_SOA, |
eae0036b | 66 | T_SPF, |
0756eb3c | 67 | T_SRV, |
1e06383a | 68 | T_TLSA, |
33397d19 PH |
69 | T_TXT, |
70 | T_ZNS /* Private type for "zone nameservers" */ | |
71 | }; | |
0756eb3c PH |
72 | |
73 | ||
74 | /************************************************* | |
75 | * Open entry point * | |
76 | *************************************************/ | |
77 | ||
78 | /* See local README for interface description. */ | |
79 | ||
e6d225ae | 80 | static void * |
d447dbd1 | 81 | dnsdb_open(const uschar * filename, uschar **errmsg) |
0756eb3c PH |
82 | { |
83 | filename = filename; /* Keep picky compilers happy */ | |
84 | errmsg = errmsg; /* Ditto */ | |
85 | return (void *)(-1); /* Any non-0 value */ | |
86 | } | |
87 | ||
88 | ||
89 | ||
90 | /************************************************* | |
91 | * Find entry point for dnsdb * | |
92 | *************************************************/ | |
93 | ||
7bb56e1f PH |
94 | /* See local README for interface description. The query in the "keystring" may |
95 | consist of a number of parts. | |
96 | ||
8e669ac1 PH |
97 | (a) If the first significant character is '>' then the next character is the |
98 | separator character that is used when multiple records are found. The default | |
7bb56e1f PH |
99 | separator is newline. |
100 | ||
0d0c6357 NM |
101 | (b) If the next character is ',' then the next character is the separator |
102 | character used for multiple items of text in "TXT" records. Alternatively, | |
103 | if the next character is ';' then these multiple items are concatenated with | |
104 | no separator. With neither of these options specified, only the first item | |
8ee4b30e PP |
105 | is output. Similarly for "SPF" records, but the default for joining multiple |
106 | items in one SPF record is the empty string, for direct concatenation. | |
0d0c6357 | 107 | |
fd7f7910 JH |
108 | (c) Options, all comma-terminated, in any order. Any unrecognised option |
109 | terminates option processing. Recognised options are: | |
110 | ||
111 | - 'defer_FOO': set the defer behaviour to FOO. The possible behaviours are: | |
112 | 'strict', where any defer causes the whole lookup to defer; 'lax', where a defer | |
113 | causes the whole lookup to defer only if none of the DNS queries succeeds; and | |
114 | 'never', where all defers are as if the lookup failed. The default is 'lax'. | |
115 | ||
ab0e957b | 116 | - 'dnssec_FOO', with 'strict', 'lax' (default), and 'never'. The meanings are |
fd3b6a4a JH |
117 | require, try and don't-try dnssec respectively. |
118 | ||
fd7f7910 JH |
119 | - 'retrans_VAL', set the timeout value. VAL is an Exim time specification |
120 | (eg "5s"). The default is set by the main configuration option 'dns_retrans'. | |
121 | ||
122 | - 'retry_VAL', set the retry count on timeouts. VAL is an integer. The | |
123 | default is set by the main configuration option "dns_retry". | |
124 | ||
125 | (d) If the next sequence of characters is a sequence of letters and digits | |
8e669ac1 | 126 | followed by '=', it is interpreted as the name of the DNS record type. The |
ff4dbb19 | 127 | default is "TXT". |
7bb56e1f | 128 | |
fd7f7910 | 129 | (e) Then there follows list of domain names. This is a generalized Exim list, |
8e669ac1 | 130 | which may start with '<' in order to set a specific separator. The default |
7bb56e1f | 131 | separator, as always, is colon. */ |
0756eb3c | 132 | |
e6d225ae | 133 | static int |
d447dbd1 | 134 | dnsdb_find(void * handle, const uschar * filename, const uschar * keystring, |
67a57a5a JH |
135 | int length, uschar ** result, uschar ** errmsg, uint * do_cache, |
136 | const uschar * opts) | |
0756eb3c PH |
137 | { |
138 | int rc; | |
7bb56e1f | 139 | int sep = 0; |
ff4dbb19 | 140 | int defer_mode = PASS; |
ab0e957b | 141 | int dnssec_mode = PASS; |
fd7f7910 JH |
142 | int save_retrans = dns_retrans; |
143 | int save_retry = dns_retry; | |
542bc632 | 144 | int type; |
c38d6da9 | 145 | int failrc = FAIL; |
55414b25 JH |
146 | const uschar *outsep = CUS"\n"; |
147 | const uschar *outsep2 = NULL; | |
e5a9dba6 | 148 | uschar *equals, *domain, *found; |
0756eb3c | 149 | |
8743d3ac JH |
150 | dns_answer * dnsa = store_get_dns_answer(); |
151 | dns_scan dnss; | |
152 | ||
acec9514 | 153 | /* Because we're working in the search pool, we try to reclaim as much |
0756eb3c PH |
154 | store as possible later, so we preallocate the result here */ |
155 | ||
acec9514 | 156 | gstring * yield = string_get(256); |
0756eb3c | 157 | |
0756eb3c PH |
158 | handle = handle; /* Keep picky compilers happy */ |
159 | filename = filename; | |
160 | length = length; | |
161 | do_cache = do_cache; | |
162 | ||
0d0c6357 NM |
163 | /* If the string starts with '>' we change the output separator. |
164 | If it's followed by ';' or ',' we set the TXT output separator. */ | |
0756eb3c | 165 | |
7bb56e1f PH |
166 | while (isspace(*keystring)) keystring++; |
167 | if (*keystring == '>') | |
0756eb3c | 168 | { |
7bb56e1f | 169 | outsep = keystring + 1; |
8e669ac1 | 170 | keystring += 2; |
0d0c6357 NM |
171 | if (*keystring == ',') |
172 | { | |
173 | outsep2 = keystring + 1; | |
174 | keystring += 2; | |
175 | } | |
176 | else if (*keystring == ';') | |
177 | { | |
178 | outsep2 = US""; | |
179 | keystring++; | |
180 | } | |
7bb56e1f | 181 | while (isspace(*keystring)) keystring++; |
8e669ac1 | 182 | } |
7bb56e1f | 183 | |
fd3b6a4a | 184 | /* Check for a modifier keyword. */ |
ff4dbb19 | 185 | |
fd7f7910 | 186 | for (;;) |
ff4dbb19 | 187 | { |
fd3b6a4a | 188 | if (strncmpic(keystring, US"defer_", 6) == 0) |
ff4dbb19 | 189 | { |
ff4dbb19 | 190 | keystring += 6; |
fd3b6a4a | 191 | if (strncmpic(keystring, US"strict", 6) == 0) |
fd7f7910 | 192 | { defer_mode = DEFER; keystring += 6; } |
fd3b6a4a | 193 | else if (strncmpic(keystring, US"lax", 3) == 0) |
fd7f7910 | 194 | { defer_mode = PASS; keystring += 3; } |
fd3b6a4a | 195 | else if (strncmpic(keystring, US"never", 5) == 0) |
fd7f7910 | 196 | { defer_mode = OK; keystring += 5; } |
fd3b6a4a JH |
197 | else |
198 | { | |
199 | *errmsg = US"unsupported dnsdb defer behaviour"; | |
200 | return DEFER; | |
201 | } | |
ff4dbb19 | 202 | } |
fd7f7910 | 203 | else if (strncmpic(keystring, US"dnssec_", 7) == 0) |
ff4dbb19 | 204 | { |
fd3b6a4a JH |
205 | keystring += 7; |
206 | if (strncmpic(keystring, US"strict", 6) == 0) | |
fd7f7910 | 207 | { dnssec_mode = DEFER; keystring += 6; } |
fd3b6a4a | 208 | else if (strncmpic(keystring, US"lax", 3) == 0) |
fd7f7910 JH |
209 | { dnssec_mode = PASS; keystring += 3; } |
210 | else if (strncmpic(keystring, US"never", 5) == 0) | |
211 | { dnssec_mode = OK; keystring += 5; } | |
212 | else | |
fd3b6a4a | 213 | { |
fd7f7910 JH |
214 | *errmsg = US"unsupported dnsdb dnssec behaviour"; |
215 | return DEFER; | |
fd3b6a4a | 216 | } |
fd7f7910 JH |
217 | } |
218 | else if (strncmpic(keystring, US"retrans_", 8) == 0) | |
219 | { | |
220 | int timeout_sec; | |
221 | if ((timeout_sec = readconf_readtime(keystring += 8, ',', FALSE)) <= 0) | |
fd3b6a4a | 222 | { |
fd7f7910 JH |
223 | *errmsg = US"unsupported dnsdb timeout value"; |
224 | return DEFER; | |
fd3b6a4a | 225 | } |
fd7f7910 JH |
226 | dns_retrans = timeout_sec; |
227 | while (*keystring != ',') keystring++; | |
228 | } | |
229 | else if (strncmpic(keystring, US"retry_", 6) == 0) | |
230 | { | |
231 | int retries; | |
fc362fc5 | 232 | if ((retries = (int)strtol(CCS keystring + 6, CSS &keystring, 0)) < 0) |
fd3b6a4a | 233 | { |
fd7f7910 | 234 | *errmsg = US"unsupported dnsdb retry count"; |
fd3b6a4a JH |
235 | return DEFER; |
236 | } | |
fd7f7910 | 237 | dns_retry = retries; |
ff4dbb19 | 238 | } |
fd7f7910 JH |
239 | else |
240 | break; | |
241 | ||
ff4dbb19 PH |
242 | while (isspace(*keystring)) keystring++; |
243 | if (*keystring++ != ',') | |
244 | { | |
fd3b6a4a | 245 | *errmsg = US"dnsdb modifier syntax error"; |
ff4dbb19 PH |
246 | return DEFER; |
247 | } | |
248 | while (isspace(*keystring)) keystring++; | |
249 | } | |
250 | ||
542bc632 PP |
251 | /* Figure out the "type" value if it is not T_TXT. |
252 | If the keystring contains an = this must be preceded by a valid type name. */ | |
7bb56e1f | 253 | |
542bc632 | 254 | type = T_TXT; |
7bb56e1f PH |
255 | if ((equals = Ustrchr(keystring, '=')) != NULL) |
256 | { | |
257 | int i, len; | |
258 | uschar *tend = equals; | |
8e669ac1 PH |
259 | |
260 | while (tend > keystring && isspace(tend[-1])) tend--; | |
261 | len = tend - keystring; | |
262 | ||
9be4572b | 263 | for (i = 0; i < nelem(type_names); i++) |
0756eb3c PH |
264 | if (len == Ustrlen(type_names[i]) && |
265 | strncmpic(keystring, US type_names[i], len) == 0) | |
266 | { | |
267 | type = type_values[i]; | |
268 | break; | |
269 | } | |
8e669ac1 | 270 | |
9be4572b | 271 | if (i >= nelem(type_names)) |
0756eb3c PH |
272 | { |
273 | *errmsg = US"unsupported DNS record type"; | |
274 | return DEFER; | |
275 | } | |
8e669ac1 | 276 | |
7bb56e1f PH |
277 | keystring = equals + 1; |
278 | while (isspace(*keystring)) keystring++; | |
0756eb3c | 279 | } |
8e669ac1 | 280 | |
7bb56e1f | 281 | /* Initialize the resolver in case this is the first time it has been used. */ |
0756eb3c | 282 | |
fd3b6a4a | 283 | dns_init(FALSE, FALSE, dnssec_mode != OK); |
0756eb3c | 284 | |
8e669ac1 PH |
285 | /* The remainder of the string must be a list of domains. As long as the lookup |
286 | for at least one of them succeeds, we return success. Failure means that none | |
287 | of them were found. | |
0756eb3c | 288 | |
8e669ac1 PH |
289 | The original implementation did not support a list of domains. Adding the list |
290 | feature is compatible, except in one case: when PTR records are being looked up | |
291 | for a single IPv6 address. Fortunately, we can hack in a compatibility feature | |
292 | here: If the type is PTR and no list separator is specified, and the entire | |
293 | remaining string is valid as an IP address, set an impossible separator so that | |
7bb56e1f | 294 | it is treated as one item. */ |
33397d19 | 295 | |
7bb56e1f | 296 | if (type == T_PTR && keystring[0] != '<' && |
7e66e54d | 297 | string_is_ip_address(keystring, NULL) != 0) |
7bb56e1f | 298 | sep = -1; |
0756eb3c | 299 | |
542bc632 PP |
300 | /* SPF strings should be concatenated without a separator, thus make |
301 | it the default if not defined (see RFC 4408 section 3.1.3). | |
302 | Multiple SPF records are forbidden (section 3.1.2) but are currently | |
be36e572 JH |
303 | not handled specially, thus they are concatenated with \n by default. |
304 | MX priority and value are space-separated by default. | |
305 | SRV and TLSA record parts are space-separated by default. */ | |
542bc632 | 306 | |
be36e572 JH |
307 | if (!outsep2) switch(type) |
308 | { | |
309 | case T_SPF: outsep2 = US""; break; | |
310 | case T_SRV: case T_MX: case T_TLSA: outsep2 = US" "; break; | |
311 | } | |
542bc632 | 312 | |
7bb56e1f | 313 | /* Now scan the list and do a lookup for each item */ |
0756eb3c | 314 | |
fd7f7910 | 315 | while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) |
8e669ac1 | 316 | { |
b09c1793 JH |
317 | int searchtype = type == T_CSA ? T_SRV : /* record type we want */ |
318 | type == T_MXH ? T_MX : | |
319 | type == T_ZNS ? T_NS : type; | |
e5a9dba6 PH |
320 | |
321 | /* If the type is PTR or CSA, we have to construct the relevant magic lookup | |
322 | key if the original is an IP address (some experimental protocols are using | |
323 | PTR records for different purposes where the key string is a host name, and | |
324 | Exim's extended CSA can be keyed by domains or IP addresses). This code for | |
325 | doing the reversal is now in a separate function. */ | |
326 | ||
327 | if ((type == T_PTR || type == T_CSA) && | |
7e66e54d | 328 | string_is_ip_address(domain, NULL) != 0) |
152481a0 | 329 | domain = dns_build_reverse(domain); |
8e669ac1 | 330 | |
3a796370 | 331 | do |
c38d6da9 | 332 | { |
42c7f0b4 | 333 | DEBUG(D_lookup) debug_printf_indent("dnsdb key: %s\n", domain); |
3a796370 JH |
334 | |
335 | /* Do the lookup and sort out the result. There are four special types that | |
66be95e0 | 336 | are handled specially: T_CSA, T_ZNS, T_ADDRESSES and T_MXH. |
3a796370 | 337 | The first two are handled in a special lookup function so that the facility |
66be95e0 | 338 | could be used from other parts of the Exim code. T_ADDRESSES is handled by looping |
3a796370 JH |
339 | over the types of A lookup. T_MXH affects only what happens later on in |
340 | this function, but for tidiness it is handled by the "special". If the | |
341 | lookup fails, continue with the next domain. In the case of DEFER, adjust | |
342 | the final "nothing found" result, but carry on to the next domain. */ | |
343 | ||
344 | found = domain; | |
6abc190a | 345 | #if HAVE_IPV6 |
66be95e0 | 346 | if (type == T_ADDRESSES) /* NB cannot happen unless HAVE_IPV6 */ |
3a796370 | 347 | { |
cc00f4af | 348 | if (searchtype == T_ADDRESSES) searchtype = T_AAAA; |
3a796370 | 349 | else if (searchtype == T_AAAA) searchtype = T_A; |
8743d3ac | 350 | rc = dns_special_lookup(dnsa, domain, searchtype, CUSS &found); |
3a796370 JH |
351 | } |
352 | else | |
6abc190a | 353 | #endif |
8743d3ac | 354 | rc = dns_special_lookup(dnsa, domain, type, CUSS &found); |
ea3bc19b | 355 | |
4e0983dc | 356 | lookup_dnssec_authenticated = dnssec_mode==OK ? NULL |
8743d3ac | 357 | : dns_is_secure(dnsa) ? US"yes" : US"no"; |
4e0983dc | 358 | |
3a796370 | 359 | if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue; |
91f40ccd | 360 | if ( rc != DNS_SUCCEED |
8743d3ac | 361 | || (dnssec_mode == DEFER && !dns_is_secure(dnsa)) |
91f40ccd | 362 | ) |
3a796370 | 363 | { |
fd3b6a4a JH |
364 | if (defer_mode == DEFER) |
365 | { | |
fd7f7910 JH |
366 | dns_retrans = save_retrans; |
367 | dns_retry = save_retry; | |
9d9c3746 | 368 | dns_init(FALSE, FALSE, FALSE); /* clr dnssec bit */ |
fd3b6a4a JH |
369 | return DEFER; /* always defer */ |
370 | } | |
3a796370 JH |
371 | if (defer_mode == PASS) failrc = DEFER; /* defer only if all do */ |
372 | continue; /* treat defer as fail */ | |
373 | } | |
fd3b6a4a | 374 | |
8e669ac1 | 375 | |
3a796370 | 376 | /* Search the returned records */ |
8e669ac1 | 377 | |
8743d3ac JH |
378 | for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; |
379 | rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == searchtype) | |
0756eb3c | 380 | { |
14b3c5bc JH |
381 | if (*do_cache > rr->ttl) |
382 | *do_cache = rr->ttl; | |
383 | ||
cc00f4af | 384 | if (type == T_A || type == T_AAAA || type == T_ADDRESSES) |
7bb56e1f | 385 | { |
8743d3ac | 386 | for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next) |
3a796370 | 387 | { |
acec9514 JH |
388 | if (yield->ptr) yield = string_catn(yield, outsep, 1); |
389 | yield = string_cat(yield, da->address); | |
3a796370 JH |
390 | } |
391 | continue; | |
7bb56e1f | 392 | } |
8e669ac1 | 393 | |
3a796370 JH |
394 | /* Other kinds of record just have one piece of data each, but there may be |
395 | several of them, of course. */ | |
8e669ac1 | 396 | |
acec9514 | 397 | if (yield->ptr) yield = string_catn(yield, outsep, 1); |
8e669ac1 | 398 | |
3a796370 | 399 | if (type == T_TXT || type == T_SPF) |
80a47a2c | 400 | { |
acec9514 JH |
401 | if (outsep2 == NULL) /* output only the first item of data */ |
402 | yield = string_catn(yield, US (rr->data+1), (rr->data)[0]); | |
3a796370 JH |
403 | else |
404 | { | |
405 | /* output all items */ | |
406 | int data_offset = 0; | |
407 | while (data_offset < rr->size) | |
408 | { | |
409 | uschar chunk_len = (rr->data)[data_offset++]; | |
410 | if (outsep2[0] != '\0' && data_offset != 1) | |
acec9514 JH |
411 | yield = string_catn(yield, outsep2, 1); |
412 | yield = string_catn(yield, US ((rr->data)+data_offset), chunk_len); | |
3a796370 JH |
413 | data_offset += chunk_len; |
414 | } | |
0d0c6357 | 415 | } |
80a47a2c | 416 | } |
1e06383a TL |
417 | else if (type == T_TLSA) |
418 | { | |
419 | uint8_t usage, selector, matching_type; | |
624f33df | 420 | uint16_t payload_length; |
1e06383a TL |
421 | uschar s[MAX_TLSA_EXPANDED_SIZE]; |
422 | uschar * sp = s; | |
d2a2c69b | 423 | uschar * p = US rr->data; |
1e06383a TL |
424 | |
425 | usage = *p++; | |
426 | selector = *p++; | |
427 | matching_type = *p++; | |
428 | /* What's left after removing the first 3 bytes above */ | |
429 | payload_length = rr->size - 3; | |
be36e572 JH |
430 | sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2, |
431 | selector, *outsep2, matching_type, *outsep2); | |
1e06383a | 432 | /* Now append the cert/identifier, one hex char at a time */ |
624f33df JH |
433 | while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4)) |
434 | sp += sprintf(CS sp, "%02x", *p++); | |
d2a2c69b | 435 | |
acec9514 | 436 | yield = string_cat(yield, s); |
1e06383a | 437 | } |
d2a2c69b | 438 | else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */ |
e5a9dba6 | 439 | { |
3a796370 JH |
440 | int priority, weight, port; |
441 | uschar s[264]; | |
d2a2c69b JH |
442 | uschar * p = US rr->data; |
443 | ||
444 | switch (type) | |
445 | { | |
446 | case T_MXH: | |
447 | /* mxh ignores the priority number and includes only the hostnames */ | |
448 | GETSHORT(priority, p); | |
449 | break; | |
450 | ||
451 | case T_MX: | |
452 | GETSHORT(priority, p); | |
453 | sprintf(CS s, "%d%c", priority, *outsep2); | |
acec9514 | 454 | yield = string_cat(yield, s); |
d2a2c69b JH |
455 | break; |
456 | ||
457 | case T_SRV: | |
458 | GETSHORT(priority, p); | |
459 | GETSHORT(weight, p); | |
460 | GETSHORT(port, p); | |
461 | sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2, | |
462 | weight, *outsep2, port, *outsep2); | |
acec9514 | 463 | yield = string_cat(yield, s); |
d2a2c69b JH |
464 | break; |
465 | ||
466 | case T_CSA: | |
467 | /* See acl_verify_csa() for more comments about CSA. */ | |
468 | GETSHORT(priority, p); | |
469 | GETSHORT(weight, p); | |
470 | GETSHORT(port, p); | |
471 | ||
472 | if (priority != 1) continue; /* CSA version must be 1 */ | |
473 | ||
474 | /* If the CSA record we found is not the one we asked for, analyse | |
475 | the subdomain assertions in the port field, else analyse the direct | |
476 | authorization status in the weight field. */ | |
477 | ||
478 | if (Ustrcmp(found, domain) != 0) | |
479 | { | |
480 | if (port & 1) *s = 'X'; /* explicit authorization required */ | |
481 | else *s = '?'; /* no subdomain assertions here */ | |
482 | } | |
483 | else | |
484 | { | |
485 | if (weight < 2) *s = 'N'; /* not authorized */ | |
486 | else if (weight == 2) *s = 'Y'; /* authorized */ | |
487 | else if (weight == 3) *s = '?'; /* unauthorizable */ | |
488 | else continue; /* invalid */ | |
489 | } | |
490 | ||
491 | s[1] = ' '; | |
acec9514 | 492 | yield = string_catn(yield, s, 2); |
d2a2c69b JH |
493 | break; |
494 | ||
495 | default: | |
496 | break; | |
497 | } | |
e5a9dba6 | 498 | |
3a796370 | 499 | /* GETSHORT() has advanced the pointer to the target domain. */ |
8e669ac1 | 500 | |
8743d3ac | 501 | rc = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p, |
d2a2c69b | 502 | (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); |
8e669ac1 | 503 | |
3a796370 JH |
504 | /* If an overlong response was received, the data will have been |
505 | truncated and dn_expand may fail. */ | |
8e669ac1 | 506 | |
3a796370 JH |
507 | if (rc < 0) |
508 | { | |
509 | log_write(0, LOG_MAIN, "host name alias list truncated: type=%s " | |
510 | "domain=%s", dns_text_type(type), domain); | |
511 | break; | |
512 | } | |
acec9514 | 513 | else yield = string_cat(yield, s); |
d2a2c69b JH |
514 | |
515 | if (type == T_SOA && outsep2 != NULL) | |
516 | { | |
517 | unsigned long serial, refresh, retry, expire, minimum; | |
518 | ||
519 | p += rc; | |
acec9514 | 520 | yield = string_catn(yield, outsep2, 1); |
d2a2c69b | 521 | |
8743d3ac | 522 | rc = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p, |
d2a2c69b JH |
523 | (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); |
524 | if (rc < 0) | |
525 | { | |
526 | log_write(0, LOG_MAIN, "responsible-mailbox truncated: type=%s " | |
527 | "domain=%s", dns_text_type(type), domain); | |
528 | break; | |
529 | } | |
acec9514 | 530 | else yield = string_cat(yield, s); |
d2a2c69b JH |
531 | |
532 | p += rc; | |
533 | GETLONG(serial, p); GETLONG(refresh, p); | |
534 | GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); | |
fc362fc5 | 535 | sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu", |
d2a2c69b JH |
536 | *outsep2, serial, *outsep2, refresh, |
537 | *outsep2, retry, *outsep2, expire, *outsep2, minimum); | |
acec9514 | 538 | yield = string_cat(yield, s); |
d2a2c69b | 539 | } |
7bb56e1f | 540 | } |
3a796370 JH |
541 | } /* Loop for list of returned records */ |
542 | ||
4c04137d | 543 | /* Loop for set of A-lookup types */ |
66be95e0 | 544 | } while (type == T_ADDRESSES && searchtype != T_A); |
3a796370 JH |
545 | |
546 | } /* Loop for list of domains */ | |
7bb56e1f PH |
547 | |
548 | /* Reclaim unused memory */ | |
0756eb3c | 549 | |
f3ebb786 | 550 | gstring_release_unused(yield); |
7bb56e1f | 551 | |
acec9514 | 552 | /* If yield NULL we have not found anything. Otherwise, insert the terminating |
7bb56e1f PH |
553 | zero and return the result. */ |
554 | ||
fd7f7910 JH |
555 | dns_retrans = save_retrans; |
556 | dns_retry = save_retry; | |
fd3b6a4a JH |
557 | dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ |
558 | ||
acec9514 JH |
559 | if (!yield || !yield->ptr) return failrc; |
560 | ||
561 | *result = string_from_gstring(yield); | |
0756eb3c PH |
562 | return OK; |
563 | } | |
564 | ||
6545de78 PP |
565 | |
566 | ||
567 | /************************************************* | |
568 | * Version reporting entry point * | |
569 | *************************************************/ | |
570 | ||
571 | /* See local README for interface description. */ | |
572 | ||
573 | #include "../version.h" | |
574 | ||
575 | void | |
576 | dnsdb_version_report(FILE *f) | |
577 | { | |
578 | #ifdef DYNLOOKUP | |
579 | fprintf(f, "Library version: DNSDB: Exim version %s\n", EXIM_VERSION_STR); | |
580 | #endif | |
581 | } | |
582 | ||
583 | ||
e6d225ae | 584 | static lookup_info _lookup_info = { |
9f400174 JH |
585 | .name = US"dnsdb", /* lookup name */ |
586 | .type = lookup_querystyle, /* query style */ | |
587 | .open = dnsdb_open, /* open function */ | |
588 | .check = NULL, /* check function */ | |
589 | .find = dnsdb_find, /* find function */ | |
590 | .close = NULL, /* no close function */ | |
591 | .tidy = NULL, /* no tidy function */ | |
592 | .quote = NULL, /* no quoting function */ | |
593 | .version_report = dnsdb_version_report /* version reporting */ | |
e6d225ae DW |
594 | }; |
595 | ||
596 | #ifdef DYNLOOKUP | |
597 | #define dnsdb_lookup_module_info _lookup_module_info | |
598 | #endif | |
599 | ||
600 | static lookup_info *_lookup_list[] = { &_lookup_info }; | |
601 | lookup_module_info dnsdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; | |
602 | ||
fd3b6a4a JH |
603 | /* vi: aw ai sw=2 |
604 | */ | |
0756eb3c | 605 | /* End of lookups/dnsdb.c */ |