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