22a9de4f4b832536213f83c1e4042c1f89d3f5d1
1 /* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.3 2004/11/19 15:18:57 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
[] = {
42 static int type_values
[] = {
56 T_ZNS
/* Private type for "zone nameservers" */
60 /*************************************************
62 *************************************************/
64 /* See local README for interface description. */
67 dnsdb_open(uschar
*filename
, uschar
**errmsg
)
69 filename
= filename
; /* Keep picky compilers happy */
70 errmsg
= errmsg
; /* Ditto */
71 return (void *)(-1); /* Any non-0 value */
76 /*************************************************
77 * Find entry point for dnsdb *
78 *************************************************/
80 /* See local README for interface description. The query in the "keystring" may
81 consist of a number of parts.
83 (a) If the first significant character is '>' then the next character is the
84 separator character that is used when multiple records are found. The default
87 (b) If the next sequence of characters is a sequence of letters and digits
88 followed by '=', it is interpreted as the name of the DNS record type. The
91 (c) Then there follows list of domain names. This is a generalized Exim list,
92 which may start with '<' in order to set a specific separator. The default
93 separator, as always, is colon. */
96 dnsdb_find(void *handle
, uschar
*filename
, uschar
*keystring
, int length
,
97 uschar
**result
, uschar
**errmsg
, BOOL
*do_cache
)
104 uschar
*outsep
= US
"\n";
105 uschar
*equals
, *domain
;
108 /* Because we're the working in the search pool, we try to reclaim as much
109 store as possible later, so we preallocate the result here */
111 uschar
*yield
= store_get(size
);
117 handle
= handle
; /* Keep picky compilers happy */
122 /* If the string starts with '>' we change the output separator */
124 while (isspace(*keystring
)) keystring
++;
125 if (*keystring
== '>')
127 outsep
= keystring
+ 1;
129 while (isspace(*keystring
)) keystring
++;
132 /* If the keystring contains an = this must be preceded by a valid type name. */
134 if ((equals
= Ustrchr(keystring
, '=')) != NULL
)
137 uschar
*tend
= equals
;
139 while (tend
> keystring
&& isspace(tend
[-1])) tend
--;
140 len
= tend
- keystring
;
142 for (i
= 0; i
< sizeof(type_names
)/sizeof(uschar
*); i
++)
144 if (len
== Ustrlen(type_names
[i
]) &&
145 strncmpic(keystring
, US type_names
[i
], len
) == 0)
147 type
= type_values
[i
];
152 if (i
>= sizeof(type_names
)/sizeof(uschar
*))
154 *errmsg
= US
"unsupported DNS record type";
158 keystring
= equals
+ 1;
159 while (isspace(*keystring
)) keystring
++;
162 /* Initialize the resolver in case this is the first time it has been used. */
164 dns_init(FALSE
, FALSE
);
166 /* The remainder of the string must be a list of domains. As long as the lookup
167 for at least one of them succeeds, we return success. Failure means that none
170 The original implementation did not support a list of domains. Adding the list
171 feature is compatible, except in one case: when PTR records are being looked up
172 for a single IPv6 address. Fortunately, we can hack in a compatibility feature
173 here: If the type is PTR and no list separator is specified, and the entire
174 remaining string is valid as an IP address, set an impossible separator so that
175 it is treated as one item. */
177 if (type
== T_PTR
&& keystring
[0] != '<' &&
178 string_is_ip_address(keystring
, NULL
) > 0)
181 /* Now scan the list and do a lookup for each item */
183 while ((domain
= string_nextinlist(&keystring
, &sep
, buffer
, sizeof(buffer
)))
188 /* If the type is PTR, we have to construct the relevant magic lookup
189 key. This code is now in a separate function. */
193 dns_build_reverse(domain
, rbuffer
);
197 DEBUG(D_lookup
) debug_printf("dnsdb key: %s\n", domain
);
199 /* Do the lookup and sort out the result. We use the special
200 lookup function that knows about pseudo types like "zns". If the lookup
201 fails, continue with the next domain. */
203 rc
= dns_special_lookup(&dnsa
, domain
, type
, NULL
);
205 if (rc
== DNS_NOMATCH
|| rc
== DNS_NODATA
) continue;
206 if (rc
!= DNS_SUCCEED
) return DEFER
;
208 /* If the lookup was a pseudo-type, change it to the correct type for
209 searching the returned records; then search for them. */
211 if (type
== T_ZNS
) type
= T_NS
;
212 for (rr
= dns_next_rr(&dnsa
, &dnss
, RESET_ANSWERS
);
214 rr
= dns_next_rr(&dnsa
, &dnss
, RESET_NEXT
))
216 if (rr
->type
!= type
) continue;
218 /* There may be several addresses from an A6 record. Put the configured
219 separator between them, just as for between several records. However, A6
220 support is not normally configured these days. */
229 for (da
= dns_address_from_rr(&dnsa
, rr
); da
!= NULL
; da
= da
->next
)
231 if (ptr
!= 0) yield
= string_cat(yield
, &size
, &ptr
, outsep
, 1);
232 yield
= string_cat(yield
, &size
, &ptr
, da
->address
,
233 Ustrlen(da
->address
));
238 /* Other kinds of record just have one piece of data each, but there may be
239 several of them, of course. */
241 if (ptr
!= 0) yield
= string_cat(yield
, &size
, &ptr
, outsep
, 1);
245 yield
= string_cat(yield
, &size
, &ptr
, (uschar
*)(rr
->data
+1),
248 else /* T_CNAME, T_MX, T_NS, T_SRV, T_PTR */
251 uschar
*p
= (uschar
*)(rr
->data
);
255 GETSHORT(num
, p
); /* pointer is advanced */
256 sprintf(CS s
, "%d ", num
);
257 yield
= string_cat(yield
, &size
, &ptr
, s
, Ustrlen(s
));
259 else if (type
== T_SRV
)
261 int num
, weight
, port
;
262 GETSHORT(num
, p
); /* pointer is advanced */
265 sprintf(CS s
, "%d %d %d ", num
, weight
, port
);
266 yield
= string_cat(yield
, &size
, &ptr
, s
, Ustrlen(s
));
268 rc
= dn_expand(dnsa
.answer
, dnsa
.answer
+ dnsa
.answerlen
, p
,
269 (DN_EXPAND_ARG4_TYPE
)(s
), sizeof(s
));
271 /* If an overlong response was received, the data will have been
272 truncated and dn_expand may fail. */
276 log_write(0, LOG_MAIN
, "host name alias list truncated: type=%s "
277 "domain=%s", dns_text_type(type
), domain
);
280 else yield
= string_cat(yield
, &size
, &ptr
, s
, Ustrlen(s
));
282 } /* Loop for list of returned records */
283 } /* Loop for list of domains */
285 /* Reclaim unused memory */
287 store_reset(yield
+ ptr
+ 1);
289 /* If ptr == 0 we have not found anything. Otherwise, insert the terminating
290 zero and return the result. */
292 if (ptr
== 0) return FAIL
;
298 /* End of lookups/dnsdb.c */