Copyright updates:
[exim.git] / src / src / lookups / dnsdb.c
CommitLineData
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
15header 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 33static 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
53static 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 80static void *
d447dbd1 81dnsdb_open(const uschar * filename, uschar **errmsg)
0756eb3c
PH
82{
83filename = filename; /* Keep picky compilers happy */
84errmsg = errmsg; /* Ditto */
85return (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
95consist of a number of parts.
96
8e669ac1
PH
97(a) If the first significant character is '>' then the next character is the
98separator character that is used when multiple records are found. The default
7bb56e1f
PH
99separator is newline.
100
0d0c6357
NM
101(b) If the next character is ',' then the next character is the separator
102character used for multiple items of text in "TXT" records. Alternatively,
103if the next character is ';' then these multiple items are concatenated with
104no separator. With neither of these options specified, only the first item
8ee4b30e
PP
105is output. Similarly for "SPF" records, but the default for joining multiple
106items 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
109terminates 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
113causes 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
117require, 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
123default 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 126followed by '=', it is interpreted as the name of the DNS record type. The
ff4dbb19 127default is "TXT".
7bb56e1f 128
fd7f7910 129(e) Then there follows list of domain names. This is a generalized Exim list,
8e669ac1 130which may start with '<' in order to set a specific separator. The default
7bb56e1f 131separator, as always, is colon. */
0756eb3c 132
e6d225ae 133static int
d447dbd1 134dnsdb_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{
138int rc;
7bb56e1f 139int sep = 0;
ff4dbb19 140int defer_mode = PASS;
ab0e957b 141int dnssec_mode = PASS;
fd7f7910
JH
142int save_retrans = dns_retrans;
143int save_retry = dns_retry;
542bc632 144int type;
c38d6da9 145int failrc = FAIL;
55414b25
JH
146const uschar *outsep = CUS"\n";
147const uschar *outsep2 = NULL;
e5a9dba6 148uschar *equals, *domain, *found;
0756eb3c 149
8743d3ac
JH
150dns_answer * dnsa = store_get_dns_answer();
151dns_scan dnss;
152
acec9514 153/* Because we're working in the search pool, we try to reclaim as much
0756eb3c
PH
154store as possible later, so we preallocate the result here */
155
acec9514 156gstring * yield = string_get(256);
0756eb3c 157
0756eb3c
PH
158handle = handle; /* Keep picky compilers happy */
159filename = filename;
160length = length;
161do_cache = do_cache;
162
0d0c6357
NM
163/* If the string starts with '>' we change the output separator.
164If it's followed by ';' or ',' we set the TXT output separator. */
0756eb3c 165
7bb56e1f
PH
166while (isspace(*keystring)) keystring++;
167if (*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 186for (;;)
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.
252If the keystring contains an = this must be preceded by a valid type name. */
7bb56e1f 253
542bc632 254type = T_TXT;
7bb56e1f
PH
255if ((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 283dns_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
286for at least one of them succeeds, we return success. Failure means that none
287of them were found.
0756eb3c 288
8e669ac1
PH
289The original implementation did not support a list of domains. Adding the list
290feature is compatible, except in one case: when PTR records are being looked up
291for a single IPv6 address. Fortunately, we can hack in a compatibility feature
292here: If the type is PTR and no list separator is specified, and the entire
293remaining string is valid as an IP address, set an impossible separator so that
7bb56e1f 294it is treated as one item. */
33397d19 295
7bb56e1f 296if (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
301it the default if not defined (see RFC 4408 section 3.1.3).
302Multiple SPF records are forbidden (section 3.1.2) but are currently
be36e572
JH
303not handled specially, thus they are concatenated with \n by default.
304MX priority and value are space-separated by default.
305SRV and TLSA record parts are space-separated by default. */
542bc632 306
be36e572
JH
307if (!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 315while ((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 550gstring_release_unused(yield);
7bb56e1f 551
acec9514 552/* If yield NULL we have not found anything. Otherwise, insert the terminating
7bb56e1f
PH
553zero and return the result. */
554
fd7f7910
JH
555dns_retrans = save_retrans;
556dns_retry = save_retry;
fd3b6a4a
JH
557dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */
558
acec9514
JH
559if (!yield || !yield->ptr) return failrc;
560
561*result = string_from_gstring(yield);
0756eb3c
PH
562return 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
575void
576dnsdb_version_report(FILE *f)
577{
578#ifdef DYNLOOKUP
579fprintf(f, "Library version: DNSDB: Exim version %s\n", EXIM_VERSION_STR);
580#endif
581}
582
583
e6d225ae 584static 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
600static lookup_info *_lookup_list[] = { &_lookup_info };
601lookup_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 */