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 */