1 /* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.4 2004/11/24 15:43:36 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2004 */
8 /* See the file NOTICE for conditions of use and distribution. */
11 #include "lf_functions.h"
16 /* Ancient systems (e.g. SunOS4) don't appear to have T_TXT defined in their
23 /* Table of recognized DNS record types and their integer values. */
25 static char *type_names[] = {
43 static int type_values[] = {
53 T_MXH, /* Private type for "MX hostnames" */
58 T_ZNS /* Private type for "zone nameservers" */
62 /*************************************************
64 *************************************************/
66 /* See local README for interface description. */
69 dnsdb_open(uschar *filename, uschar **errmsg)
71 filename = filename; /* Keep picky compilers happy */
72 errmsg = errmsg; /* Ditto */
73 return (void *)(-1); /* Any non-0 value */
78 /*************************************************
79 * Find entry point for dnsdb *
80 *************************************************/
82 /* See local README for interface description. The query in the "keystring" may
83 consist of a number of parts.
85 (a) If the first significant character is '>' then the next character is the
86 separator character that is used when multiple records are found. The default
89 (b) If the next sequence of characters is a sequence of letters and digits
90 followed by '=', it is interpreted as the name of the DNS record type. The
93 (c) Then there follows list of domain names. This is a generalized Exim list,
94 which may start with '<' in order to set a specific separator. The default
95 separator, as always, is colon. */
98 dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length,
99 uschar **result, uschar **errmsg, BOOL *do_cache)
106 uschar *outsep = US"\n";
107 uschar *equals, *domain;
110 /* Because we're the working in the search pool, we try to reclaim as much
111 store as possible later, so we preallocate the result here */
113 uschar *yield = store_get(size);
119 handle = handle; /* Keep picky compilers happy */
124 /* If the string starts with '>' we change the output separator */
126 while (isspace(*keystring)) keystring++;
127 if (*keystring == '>')
129 outsep = keystring + 1;
131 while (isspace(*keystring)) keystring++;
134 /* If the keystring contains an = this must be preceded by a valid type name. */
136 if ((equals = Ustrchr(keystring, '=')) != NULL)
139 uschar *tend = equals;
141 while (tend > keystring && isspace(tend[-1])) tend--;
142 len = tend - keystring;
144 for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++)
146 if (len == Ustrlen(type_names[i]) &&
147 strncmpic(keystring, US type_names[i], len) == 0)
149 type = type_values[i];
154 if (i >= sizeof(type_names)/sizeof(uschar *))
156 *errmsg = US"unsupported DNS record type";
160 keystring = equals + 1;
161 while (isspace(*keystring)) keystring++;
164 /* Initialize the resolver in case this is the first time it has been used. */
166 dns_init(FALSE, FALSE);
168 /* The remainder of the string must be a list of domains. As long as the lookup
169 for at least one of them succeeds, we return success. Failure means that none
172 The original implementation did not support a list of domains. Adding the list
173 feature is compatible, except in one case: when PTR records are being looked up
174 for a single IPv6 address. Fortunately, we can hack in a compatibility feature
175 here: If the type is PTR and no list separator is specified, and the entire
176 remaining string is valid as an IP address, set an impossible separator so that
177 it is treated as one item. */
179 if (type == T_PTR && keystring[0] != '<' &&
180 string_is_ip_address(keystring, NULL) > 0)
183 /* Now scan the list and do a lookup for each item */
185 while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer)))
189 int searchtype = (type == T_ZNS)? T_NS : /* record type we want */
190 (type == T_MXH)? T_MX : type;
192 /* If the type is PTR, we have to construct the relevant magic lookup
193 key. This code is now in a separate function. */
197 dns_build_reverse(domain, rbuffer);
201 DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", domain);
203 /* Do the lookup and sort out the result. There are two special types that
204 are handled specially: T_ZNS and T_MXH. The former is handled in a special
205 lookup function so that the facility could be used from other parts of the
206 Exim code. The latter affects only what happens later on in this function,
207 but for tidiness it is handled in a similar way. If the lookup fails,
208 continue with the next domain. */
210 rc = dns_special_lookup(&dnsa, domain, type, NULL);
212 if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue;
213 if (rc != DNS_SUCCEED) return DEFER;
215 /* Search the returned records */
217 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
219 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
221 if (rr->type != searchtype) continue;
223 /* There may be several addresses from an A6 record. Put the configured
224 separator between them, just as for between several records. However, A6
225 support is not normally configured these days. */
234 for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
236 if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1);
237 yield = string_cat(yield, &size, &ptr, da->address,
238 Ustrlen(da->address));
243 /* Other kinds of record just have one piece of data each, but there may be
244 several of them, of course. */
246 if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1);
250 yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1),
253 else /* T_CNAME, T_MX, T_MXH, T_NS, T_SRV, T_PTR */
257 uschar *p = (uschar *)(rr->data);
261 /* mxh ignores the priority number and includes only the hostnames */
262 GETSHORT(num, p); /* pointer is advanced */
264 else if (type == T_MX)
266 GETSHORT(num, p); /* pointer is advanced */
267 sprintf(CS s, "%d ", num);
268 yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
270 else if (type == T_SRV)
273 GETSHORT(num, p); /* pointer is advanced */
276 sprintf(CS s, "%d %d %d ", num, weight, port);
277 yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
280 rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
281 (DN_EXPAND_ARG4_TYPE)(s), sizeof(s));
283 /* If an overlong response was received, the data will have been
284 truncated and dn_expand may fail. */
288 log_write(0, LOG_MAIN, "host name alias list truncated: type=%s "
289 "domain=%s", dns_text_type(type), domain);
292 else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
294 } /* Loop for list of returned records */
295 } /* Loop for list of domains */
297 /* Reclaim unused memory */
299 store_reset(yield + ptr + 1);
301 /* If ptr == 0 we have not found anything. Otherwise, insert the terminating
302 zero and return the result. */
304 if (ptr == 0) return FAIL;
310 /* End of lookups/dnsdb.c */