X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Flookups%2Fdnsdb.c;h=1985964da325140399cda146554b10ae30177236;hp=5c9f96b79873b890a3957d53b984c3bb0b55879d;hb=1e1ddfac79fbcd052f199500a6493c7f79cb8462;hpb=e6d225ae6e6811d3c88dc201642a2127ff6c11bd diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 5c9f96b79..1985964da 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -1,10 +1,9 @@ -/* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.20 2010/05/29 19:23:26 nm4 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -16,18 +15,26 @@ header files. */ #ifndef T_TXT -#define T_TXT 16 +# define T_TXT 16 +#endif + +/* Many systems do not have T_SPF. */ +#ifndef T_SPF +# define T_SPF 99 +#endif + +/* New TLSA record for DANE */ +#ifndef T_TLSA +# define T_TLSA 52 #endif /* Table of recognized DNS record types and their integer values. */ -static char *type_names[] = { +static const char *type_names[] = { "a", #if HAVE_IPV6 + "a+", "aaaa", - #ifdef SUPPORT_A6 - "a6", - #endif #endif "cname", "csa", @@ -35,7 +42,10 @@ static char *type_names[] = { "mxh", "ns", "ptr", + "soa", + "spf", "srv", + "tlsa", "txt", "zns" }; @@ -43,10 +53,8 @@ static char *type_names[] = { static int type_values[] = { T_A, #if HAVE_IPV6 + T_ADDRESSES, /* Private type for AAAA + A */ T_AAAA, - #ifdef SUPPORT_A6 - T_A6, - #endif #endif T_CNAME, T_CSA, /* Private type for "Client SMTP Authorization". */ @@ -54,7 +62,10 @@ static int type_values[] = { T_MXH, /* Private type for "MX hostnames" */ T_NS, T_PTR, + T_SOA, + T_SPF, T_SRV, + T_TLSA, T_TXT, T_ZNS /* Private type for "zone nameservers" */ }; @@ -67,7 +78,7 @@ static int type_values[] = { /* See local README for interface description. */ static void * -dnsdb_open(uschar *filename, uschar **errmsg) +dnsdb_open(const uschar * filename, uschar **errmsg) { filename = filename; /* Keep picky compilers happy */ errmsg = errmsg; /* Ditto */ @@ -91,13 +102,25 @@ separator is newline. character used for multiple items of text in "TXT" records. Alternatively, if the next character is ';' then these multiple items are concatenated with no separator. With neither of these options specified, only the first item -is output. +is output. Similarly for "SPF" records, but the default for joining multiple +items in one SPF record is the empty string, for direct concatenation. + +(c) Options, all comma-terminated, in any order. Any unrecognised option +terminates option processing. Recognised options are: -(c) If the next sequence of characters is 'defer_FOO' followed by a comma, -the defer behaviour is set to FOO. The possible behaviours are: 'strict', where -any defer causes the whole lookup to defer; 'lax', where a defer causes the -whole lookup to defer only if none of the DNS queries succeeds; and 'never', -where all defers are as if the lookup failed. The default is 'lax'. +- 'defer_FOO': set the defer behaviour to FOO. The possible behaviours are: +'strict', where any defer causes the whole lookup to defer; 'lax', where a defer +causes the whole lookup to defer only if none of the DNS queries succeeds; and +'never', where all defers are as if the lookup failed. The default is 'lax'. + +- 'dnssec_FOO', with 'strict', 'lax' (default), and 'never'. The meanings are +require, try and don't-try dnssec respectively. + +- 'retrans_VAL', set the timeout value. VAL is an Exim time specification +(eg "5s"). The default is set by the main configuration option 'dns_retrans'. + +- 'retry_VAL', set the retry count on timeouts. VAL is an integer. The +default is set by the main configuration option "dns_retry". (d) If the next sequence of characters is a sequence of letters and digits followed by '=', it is interpreted as the name of the DNS record type. The @@ -108,29 +131,29 @@ which may start with '<' in order to set a specific separator. The default separator, as always, is colon. */ static int -dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +dnsdb_find(void * handle, const uschar * filename, const uschar * keystring, + int length, uschar ** result, uschar ** errmsg, uint * do_cache, + const uschar * opts) { int rc; -int size = 256; -int ptr = 0; int sep = 0; int defer_mode = PASS; -int type = T_TXT; +int dnssec_mode = PASS; +int save_retrans = dns_retrans; +int save_retry = dns_retry; +int type; int failrc = FAIL; -uschar *outsep = US"\n"; -uschar *outsep2 = NULL; +const uschar *outsep = CUS"\n"; +const uschar *outsep2 = NULL; uschar *equals, *domain, *found; -uschar buffer[256]; -/* Because we're the working in the search pool, we try to reclaim as much -store as possible later, so we preallocate the result here */ +dns_answer * dnsa = store_get_dns_answer(); +dns_scan dnss; -uschar *yield = store_get(size); +/* Because we're working in the search pool, we try to reclaim as much +store as possible later, so we preallocate the result here */ -dns_record *rr; -dns_answer dnsa; -dns_scan dnss; +gstring * yield = string_get(256); handle = handle; /* Keep picky compilers happy */ filename = filename; @@ -158,42 +181,77 @@ if (*keystring == '>') while (isspace(*keystring)) keystring++; } -/* Check for a defer behaviour keyword. */ +/* Check for a modifier keyword. */ -if (strncmpic(keystring, US"defer_", 6) == 0) +for (;;) { - keystring += 6; - if (strncmpic(keystring, US"strict", 6) == 0) + if (strncmpic(keystring, US"defer_", 6) == 0) { - defer_mode = DEFER; keystring += 6; + if (strncmpic(keystring, US"strict", 6) == 0) + { defer_mode = DEFER; keystring += 6; } + else if (strncmpic(keystring, US"lax", 3) == 0) + { defer_mode = PASS; keystring += 3; } + else if (strncmpic(keystring, US"never", 5) == 0) + { defer_mode = OK; keystring += 5; } + else + { + *errmsg = US"unsupported dnsdb defer behaviour"; + return DEFER; + } } - else if (strncmpic(keystring, US"lax", 3) == 0) + else if (strncmpic(keystring, US"dnssec_", 7) == 0) { - defer_mode = PASS; - keystring += 3; + keystring += 7; + if (strncmpic(keystring, US"strict", 6) == 0) + { dnssec_mode = DEFER; keystring += 6; } + else if (strncmpic(keystring, US"lax", 3) == 0) + { dnssec_mode = PASS; keystring += 3; } + else if (strncmpic(keystring, US"never", 5) == 0) + { dnssec_mode = OK; keystring += 5; } + else + { + *errmsg = US"unsupported dnsdb dnssec behaviour"; + return DEFER; + } } - else if (strncmpic(keystring, US"never", 5) == 0) + else if (strncmpic(keystring, US"retrans_", 8) == 0) { - defer_mode = OK; - keystring += 5; + int timeout_sec; + if ((timeout_sec = readconf_readtime(keystring += 8, ',', FALSE)) <= 0) + { + *errmsg = US"unsupported dnsdb timeout value"; + return DEFER; + } + dns_retrans = timeout_sec; + while (*keystring != ',') keystring++; } - else + else if (strncmpic(keystring, US"retry_", 6) == 0) { - *errmsg = US"unsupported dnsdb defer behaviour"; - return DEFER; + int retries; + if ((retries = (int)strtol(CCS keystring + 6, CSS &keystring, 0)) < 0) + { + *errmsg = US"unsupported dnsdb retry count"; + return DEFER; + } + dns_retry = retries; } + else + break; + while (isspace(*keystring)) keystring++; if (*keystring++ != ',') { - *errmsg = US"dnsdb defer behaviour syntax error"; + *errmsg = US"dnsdb modifier syntax error"; return DEFER; } while (isspace(*keystring)) keystring++; } -/* If the keystring contains an = this must be preceded by a valid type name. */ +/* Figure out the "type" value if it is not T_TXT. +If the keystring contains an = this must be preceded by a valid type name. */ +type = T_TXT; if ((equals = Ustrchr(keystring, '=')) != NULL) { int i, len; @@ -202,17 +260,15 @@ if ((equals = Ustrchr(keystring, '=')) != NULL) while (tend > keystring && isspace(tend[-1])) tend--; len = tend - keystring; - for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++) - { + for (i = 0; i < nelem(type_names); i++) if (len == Ustrlen(type_names[i]) && strncmpic(keystring, US type_names[i], len) == 0) { type = type_values[i]; break; } - } - if (i >= sizeof(type_names)/sizeof(uschar *)) + if (i >= nelem(type_names)) { *errmsg = US"unsupported DNS record type"; return DEFER; @@ -224,7 +280,7 @@ if ((equals = Ustrchr(keystring, '=')) != NULL) /* Initialize the resolver in case this is the first time it has been used. */ -dns_init(FALSE, FALSE); +dns_init(FALSE, FALSE, dnssec_mode != OK); /* The remainder of the string must be a list of domains. As long as the lookup for at least one of them succeeds, we return success. Failure means that none @@ -241,15 +297,26 @@ if (type == T_PTR && keystring[0] != '<' && string_is_ip_address(keystring, NULL) != 0) sep = -1; +/* SPF strings should be concatenated without a separator, thus make +it the default if not defined (see RFC 4408 section 3.1.3). +Multiple SPF records are forbidden (section 3.1.2) but are currently +not handled specially, thus they are concatenated with \n by default. +MX priority and value are space-separated by default. +SRV and TLSA record parts are space-separated by default. */ + +if (!outsep2) switch(type) + { + case T_SPF: outsep2 = US""; break; + case T_SRV: case T_MX: case T_TLSA: outsep2 = US" "; break; + } + /* Now scan the list and do a lookup for each item */ -while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) - != NULL) +while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) { - uschar rbuffer[256]; - int searchtype = (type == T_CSA)? T_SRV : /* record type we want */ - (type == T_MXH)? T_MX : - (type == T_ZNS)? T_NS : type; + int searchtype = type == T_CSA ? T_SRV : /* record type we want */ + type == T_MXH ? T_MX : + type == T_ZNS ? T_NS : type; /* If the type is PTR or CSA, we have to construct the relevant magic lookup key if the original is an IP address (some experimental protocols are using @@ -259,185 +326,271 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) if ((type == T_PTR || type == T_CSA) && string_is_ip_address(domain, NULL) != 0) - { - dns_build_reverse(domain, rbuffer); - domain = rbuffer; - } - - DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", domain); - - /* Do the lookup and sort out the result. There are three special types that - are handled specially: T_CSA, T_ZNS and T_MXH. The former two are handled in - a special lookup function so that the facility could be used from other - parts of the Exim code. The latter affects only what happens later on in - this function, but for tidiness it is handled in a similar way. If the - lookup fails, continue with the next domain. In the case of DEFER, adjust - the final "nothing found" result, but carry on to the next domain. */ - - found = domain; - rc = dns_special_lookup(&dnsa, domain, type, &found); - - if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue; - if (rc != DNS_SUCCEED) - { - if (defer_mode == DEFER) return DEFER; /* always defer */ - else if (defer_mode == PASS) failrc = DEFER; /* defer only if all do */ - continue; /* treat defer as fail */ - } - - /* Search the returned records */ + domain = dns_build_reverse(domain); - for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; - rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + do { - if (rr->type != searchtype) continue; + DEBUG(D_lookup) debug_printf_indent("dnsdb key: %s\n", domain); + + /* Do the lookup and sort out the result. There are four special types that + are handled specially: T_CSA, T_ZNS, T_ADDRESSES and T_MXH. + The first two are handled in a special lookup function so that the facility + could be used from other parts of the Exim code. T_ADDRESSES is handled by looping + over the types of A lookup. T_MXH affects only what happens later on in + this function, but for tidiness it is handled by the "special". If the + lookup fails, continue with the next domain. In the case of DEFER, adjust + the final "nothing found" result, but carry on to the next domain. */ + + found = domain; +#if HAVE_IPV6 + if (type == T_ADDRESSES) /* NB cannot happen unless HAVE_IPV6 */ + { + if (searchtype == T_ADDRESSES) searchtype = T_AAAA; + else if (searchtype == T_AAAA) searchtype = T_A; + rc = dns_special_lookup(dnsa, domain, searchtype, CUSS &found); + } + else +#endif + rc = dns_special_lookup(dnsa, domain, type, CUSS &found); - /* There may be several addresses from an A6 record. Put the configured - separator between them, just as for between several records. However, A6 - support is not normally configured these days. */ + lookup_dnssec_authenticated = dnssec_mode==OK ? NULL + : dns_is_secure(dnsa) ? US"yes" : US"no"; - if (type == T_A || - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_AAAA) + if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue; + if ( rc != DNS_SUCCEED + || (dnssec_mode == DEFER && !dns_is_secure(dnsa)) + ) { - dns_address *da; - for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next) - { - if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1); - yield = string_cat(yield, &size, &ptr, da->address, - Ustrlen(da->address)); - } - continue; + if (defer_mode == DEFER) + { + dns_retrans = save_retrans; + dns_retry = save_retry; + dns_init(FALSE, FALSE, FALSE); /* clr dnssec bit */ + return DEFER; /* always defer */ + } + if (defer_mode == PASS) failrc = DEFER; /* defer only if all do */ + continue; /* treat defer as fail */ } - /* Other kinds of record just have one piece of data each, but there may be - several of them, of course. */ - if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1); + /* Search the returned records */ - if (type == T_TXT) + for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == searchtype) { - if (outsep2 == NULL) - { - /* output only the first item of data */ - yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1), - (rr->data)[0]); - } - else + if (*do_cache > rr->ttl) + *do_cache = rr->ttl; + + if (type == T_A || type == T_AAAA || type == T_ADDRESSES) { - /* output all items */ - int data_offset = 0; - while (data_offset < rr->size) + for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next) { - uschar chunk_len = (rr->data)[data_offset++]; - if (outsep2[0] != '\0' && data_offset != 1) - yield = string_cat(yield, &size, &ptr, outsep2, 1); - yield = string_cat(yield, &size, &ptr, - (uschar *)((rr->data)+data_offset), chunk_len); - data_offset += chunk_len; + if (yield->ptr) yield = string_catn(yield, outsep, 1); + yield = string_cat(yield, da->address); } + continue; } - } - else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */ - { - int priority, weight, port; - uschar s[264]; - uschar *p = (uschar *)(rr->data); - if (type == T_MXH) + /* Other kinds of record just have one piece of data each, but there may be + several of them, of course. */ + + if (yield->ptr) yield = string_catn(yield, outsep, 1); + + if (type == T_TXT || type == T_SPF) { - /* mxh ignores the priority number and includes only the hostnames */ - GETSHORT(priority, p); + if (outsep2 == NULL) /* output only the first item of data */ + yield = string_catn(yield, US (rr->data+1), (rr->data)[0]); + else + { + /* output all items */ + int data_offset = 0; + while (data_offset < rr->size) + { + uschar chunk_len = (rr->data)[data_offset++]; + if (outsep2[0] != '\0' && data_offset != 1) + yield = string_catn(yield, outsep2, 1); + yield = string_catn(yield, US ((rr->data)+data_offset), chunk_len); + data_offset += chunk_len; + } + } } - else if (type == T_MX) + else if (type == T_TLSA) { - GETSHORT(priority, p); - sprintf(CS s, "%d ", priority); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + uint8_t usage, selector, matching_type; + uint16_t payload_length; + uschar s[MAX_TLSA_EXPANDED_SIZE]; + uschar * sp = s; + uschar * p = US rr->data; + + usage = *p++; + selector = *p++; + matching_type = *p++; + /* What's left after removing the first 3 bytes above */ + payload_length = rr->size - 3; + sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2, + selector, *outsep2, matching_type, *outsep2); + /* Now append the cert/identifier, one hex char at a time */ + while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4)) + sp += sprintf(CS sp, "%02x", *p++); + + yield = string_cat(yield, s); } - else if (type == T_SRV) + else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */ { - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - sprintf(CS s, "%d %d %d ", priority, weight, port); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + int priority, weight, port; + uschar s[264]; + uschar * p = US rr->data; + + switch (type) + { + case T_MXH: + /* mxh ignores the priority number and includes only the hostnames */ + GETSHORT(priority, p); + break; + + case T_MX: + GETSHORT(priority, p); + sprintf(CS s, "%d%c", priority, *outsep2); + yield = string_cat(yield, s); + break; + + case T_SRV: + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2, + weight, *outsep2, port, *outsep2); + yield = string_cat(yield, s); + break; + + case T_CSA: + /* See acl_verify_csa() for more comments about CSA. */ + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + + if (priority != 1) continue; /* CSA version must be 1 */ + + /* If the CSA record we found is not the one we asked for, analyse + the subdomain assertions in the port field, else analyse the direct + authorization status in the weight field. */ + + if (Ustrcmp(found, domain) != 0) + { + if (port & 1) *s = 'X'; /* explicit authorization required */ + else *s = '?'; /* no subdomain assertions here */ + } + else + { + if (weight < 2) *s = 'N'; /* not authorized */ + else if (weight == 2) *s = 'Y'; /* authorized */ + else if (weight == 3) *s = '?'; /* unauthorizable */ + else continue; /* invalid */ + } + + s[1] = ' '; + yield = string_catn(yield, s, 2); + break; + + default: + break; + } + + /* GETSHORT() has advanced the pointer to the target domain. */ + + rc = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p, + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); + + /* If an overlong response was received, the data will have been + truncated and dn_expand may fail. */ + + if (rc < 0) + { + log_write(0, LOG_MAIN, "host name alias list truncated: type=%s " + "domain=%s", dns_text_type(type), domain); + break; + } + else yield = string_cat(yield, s); + + if (type == T_SOA && outsep2 != NULL) + { + unsigned long serial, refresh, retry, expire, minimum; + + p += rc; + yield = string_catn(yield, outsep2, 1); + + rc = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p, + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); + if (rc < 0) + { + log_write(0, LOG_MAIN, "responsible-mailbox truncated: type=%s " + "domain=%s", dns_text_type(type), domain); + break; + } + else yield = string_cat(yield, s); + + p += rc; + GETLONG(serial, p); GETLONG(refresh, p); + GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); + sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu", + *outsep2, serial, *outsep2, refresh, + *outsep2, retry, *outsep2, expire, *outsep2, minimum); + yield = string_cat(yield, s); + } } - else if (type == T_CSA) - { - /* See acl_verify_csa() for more comments about CSA. */ + } /* Loop for list of returned records */ - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); + /* Loop for set of A-lookup types */ + } while (type == T_ADDRESSES && searchtype != T_A); - if (priority != 1) continue; /* CSA version must be 1 */ + } /* Loop for list of domains */ - /* If the CSA record we found is not the one we asked for, analyse - the subdomain assertions in the port field, else analyse the direct - authorization status in the weight field. */ +/* Reclaim unused memory */ - if (found != domain) - { - if (port & 1) *s = 'X'; /* explicit authorization required */ - else *s = '?'; /* no subdomain assertions here */ - } - else - { - if (weight < 2) *s = 'N'; /* not authorized */ - else if (weight == 2) *s = 'Y'; /* authorized */ - else if (weight == 3) *s = '?'; /* unauthorizable */ - else continue; /* invalid */ - } +gstring_release_unused(yield); - s[1] = ' '; - yield = string_cat(yield, &size, &ptr, s, 2); - } +/* If yield NULL we have not found anything. Otherwise, insert the terminating +zero and return the result. */ - /* GETSHORT() has advanced the pointer to the target domain. */ +dns_retrans = save_retrans; +dns_retry = save_retry; +dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ - rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, - (DN_EXPAND_ARG4_TYPE)(s), sizeof(s)); +if (!yield || !yield->ptr) return failrc; - /* If an overlong response was received, the data will have been - truncated and dn_expand may fail. */ +*result = string_from_gstring(yield); +return OK; +} - if (rc < 0) - { - log_write(0, LOG_MAIN, "host name alias list truncated: type=%s " - "domain=%s", dns_text_type(type), domain); - break; - } - else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); - } - } /* Loop for list of returned records */ - } /* Loop for list of domains */ -/* Reclaim unused memory */ -store_reset(yield + ptr + 1); +/************************************************* +* Version reporting entry point * +*************************************************/ -/* If ptr == 0 we have not found anything. Otherwise, insert the terminating -zero and return the result. */ +/* See local README for interface description. */ -if (ptr == 0) return failrc; -yield[ptr] = 0; -*result = yield; -return OK; +#include "../version.h" + +void +dnsdb_version_report(FILE *f) +{ +#ifdef DYNLOOKUP +fprintf(f, "Library version: DNSDB: Exim version %s\n", EXIM_VERSION_STR); +#endif } + static lookup_info _lookup_info = { - US"dnsdb", /* lookup name */ - lookup_querystyle, /* query style */ - dnsdb_open, /* open function */ - NULL, /* check function */ - dnsdb_find, /* find function */ - NULL, /* no close function */ - NULL, /* no tidy function */ - NULL /* no quoting function */ + .name = US"dnsdb", /* lookup name */ + .type = lookup_querystyle, /* query style */ + .open = dnsdb_open, /* open function */ + .check = NULL, /* check function */ + .find = dnsdb_find, /* find function */ + .close = NULL, /* no close function */ + .tidy = NULL, /* no tidy function */ + .quote = NULL, /* no quoting function */ + .version_report = dnsdb_version_report /* version reporting */ }; #ifdef DYNLOOKUP @@ -447,4 +600,6 @@ static lookup_info _lookup_info = { static lookup_info *_lookup_list[] = { &_lookup_info }; lookup_module_info dnsdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; +/* vi: aw ai sw=2 +*/ /* End of lookups/dnsdb.c */