Implement the pseudo dns lookup type "zns" for ${dnsdb lookups.
[exim.git] / src / src / lookups / dnsdb.c
1 /* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.2 2004/11/19 09:45:54 ph10 Exp $ */
2
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2004 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 #include "../exim.h"
11 #include "lf_functions.h"
12 #include "dnsdb.h"
13
14
15
16 /* Ancient systems (e.g. SunOS4) don't appear to have T_TXT defined in their
17 header files. */
18
19 #ifndef T_TXT
20 #define T_TXT 16
21 #endif
22
23 /* Table of recognized DNS record types and their integer values. */
24
25 static char *type_names[] = {
26 "a",
27 #if HAVE_IPV6
28 "aaaa",
29 #ifdef SUPPORT_A6
30 "a6",
31 #endif
32 #endif
33 "cname",
34 "mx",
35 "ns",
36 "ptr",
37 "srv",
38 "txt",
39 "zns"
40 };
41
42 static int type_values[] = {
43 T_A,
44 #if HAVE_IPV6
45 T_AAAA,
46 #ifdef SUPPORT_A6
47 T_A6,
48 #endif
49 #endif
50 T_CNAME,
51 T_MX,
52 T_NS,
53 T_PTR,
54 T_SRV,
55 T_TXT,
56 T_ZNS /* Private type for "zone nameservers" */
57 };
58
59
60 /*************************************************
61 * Open entry point *
62 *************************************************/
63
64 /* See local README for interface description. */
65
66 void *
67 dnsdb_open(uschar *filename, uschar **errmsg)
68 {
69 filename = filename; /* Keep picky compilers happy */
70 errmsg = errmsg; /* Ditto */
71 return (void *)(-1); /* Any non-0 value */
72 }
73
74
75
76 /*************************************************
77 * Find entry point for dnsdb *
78 *************************************************/
79
80 /* See local README for interface description. */
81
82 int
83 dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length,
84 uschar **result, uschar **errmsg, BOOL *do_cache)
85 {
86 int rc;
87 int size = 256;
88 int ptr = 0;
89 int type = T_TXT;
90 uschar *orig_keystring = keystring;
91 uschar *equals = Ustrchr(keystring, '=');
92 uschar buffer[256];
93
94 /* Because we're the working in the search pool, we try to reclaim as much
95 store as possible later, so we preallocate the result here */
96
97 uschar *yield = store_get(size);
98
99 dns_record *rr;
100 dns_answer dnsa;
101 dns_scan dnss;
102
103 handle = handle; /* Keep picky compilers happy */
104 filename = filename;
105 length = length;
106 do_cache = do_cache;
107
108 /* If the keystring contains an = this is preceded by a type name. */
109
110 if (equals != NULL)
111 {
112 int i;
113 int len = equals - keystring;
114 for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++)
115 {
116 if (len == Ustrlen(type_names[i]) &&
117 strncmpic(keystring, US type_names[i], len) == 0)
118 {
119 type = type_values[i];
120 break;
121 }
122 }
123 if (i >= sizeof(type_names)/sizeof(uschar *))
124 {
125 *errmsg = US"unsupported DNS record type";
126 return DEFER;
127 }
128 keystring += len + 1;
129 }
130
131 /* If the type is PTR, we have to construct the relevant magic lookup
132 key. This code is now in a separate function. */
133
134 if (type == T_PTR)
135 {
136 dns_build_reverse(keystring, buffer);
137 keystring = buffer;
138 }
139
140 DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", keystring);
141
142 /* Initialize the resolver, in case this is the first time it is used
143 in this run. Then do the lookup and sort out the result. */
144
145 dns_init(FALSE, FALSE);
146 rc = dns_special_lookup(&dnsa, keystring, type, NULL);
147
148 if (rc == DNS_NOMATCH || rc == DNS_NODATA) return FAIL;
149 if (rc != DNS_SUCCEED) return DEFER;
150
151 /* If the lookup was a pseudo-type, change it to the correct type for searching
152 the returned records; then search for them. */
153
154 if (type == T_ZNS) type = T_NS;
155 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
156 rr != NULL;
157 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
158 {
159 if (rr->type != type) continue;
160
161 /* There may be several addresses from an A6 record. Put newlines between
162 them, just as for between several records. */
163
164 if (type == T_A ||
165 #ifdef SUPPORT_A6
166 type == T_A6 ||
167 #endif
168 type == T_AAAA)
169 {
170 dns_address *da;
171 for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
172 {
173 if (ptr != 0) yield = string_cat(yield, &size, &ptr, US"\n", 1);
174 yield = string_cat(yield, &size, &ptr, da->address, Ustrlen(da->address));
175 }
176 continue;
177 }
178
179 /* Other kinds of record just have one piece of data each. */
180
181 if (ptr != 0) yield = string_cat(yield, &size, &ptr, US"\n", 1);
182
183 if (type == T_TXT)
184 {
185 yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1),
186 (rr->data)[0]);
187 }
188 else /* T_CNAME, T_MX, T_NS, T_PTR */
189 {
190 uschar s[264];
191 uschar *p = (uschar *)(rr->data);
192 if (type == T_MX)
193 {
194 int num;
195 GETSHORT(num, p); /* pointer is advanced */
196 sprintf(CS s, "%d ", num);
197 yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
198 }
199 else if (type == T_SRV)
200 {
201 int num, weight, port;
202 GETSHORT(num, p); /* pointer is advanced */
203 GETSHORT(weight, p);
204 GETSHORT(port, p);
205 sprintf(CS s, "%d %d %d ", num, weight, port);
206 yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
207 }
208 rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
209 (DN_EXPAND_ARG4_TYPE)(s), sizeof(s));
210
211 /* If an overlong response was received, the data will have been
212 truncated and dn_expand may fail. */
213
214 if (rc < 0)
215 {
216 log_write(0, LOG_MAIN, "host name alias list truncated for %s",
217 orig_keystring);
218 break;
219 }
220 else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
221 }
222 }
223
224 yield[ptr] = 0;
225 store_reset(yield + ptr + 1); /* Reclaim unused */
226 *result = yield;
227
228 return OK;
229 }
230
231 /* End of lookups/dnsdb.c */