~ubuntu-branches/ubuntu/hardy/exim4/hardy-proposed

« back to all changes in this revision

Viewing changes to src/lookups/dnsdb.c

  • Committer: Bazaar Package Importer
  • Author(s): Marc Haber
  • Date: 2005-07-02 06:08:34 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20050702060834-qk17pd52kb9nt3bj
Tags: 4.52-1
* new upstream version 4.51. (mh)
  * adapt 70_remove_exim-users_references
  * remove 37_gnutlsparams
  * adapt 36_pcre
  * adapt 31_eximmanpage
* fix package priorities to have them in sync with override again. (mh)
* Fix error in nb (Norwegian) translation.
  Thanks to Helge Hafting. (mh). Closes: #315775
* Standards-Version: 3.6.2, no changes needed. (mh)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Cambridge: exim/exim-src/src/lookups/dnsdb.c,v 1.14 2005/06/10 18:59:35 fanf2 Exp $ */
 
2
 
1
3
/*************************************************
2
4
*     Exim - an Internet mail transport agent    *
3
5
*************************************************/
4
6
 
5
 
/* Copyright (c) University of Cambridge 1995 - 2004 */
 
7
/* Copyright (c) University of Cambridge 1995 - 2005 */
6
8
/* See the file NOTICE for conditions of use and distribution. */
7
9
 
8
10
#include "../exim.h"
29
31
  #endif
30
32
#endif
31
33
  "cname",
 
34
  "csa",
32
35
  "mx",
 
36
  "mxh",
33
37
  "ns",
34
38
  "ptr",
35
 
  "txt" };
 
39
  "srv",
 
40
  "txt",
 
41
  "zns"
 
42
};
36
43
 
37
44
static int type_values[] = {
38
45
  T_A,
43
50
  #endif
44
51
#endif
45
52
  T_CNAME,
 
53
  T_CSA,     /* Private type for "Client SMTP Authorization". */
46
54
  T_MX,
 
55
  T_MXH,     /* Private type for "MX hostnames" */
47
56
  T_NS,
48
57
  T_PTR,
49
 
  T_TXT };
 
58
  T_SRV,
 
59
  T_TXT,
 
60
  T_ZNS      /* Private type for "zone nameservers" */
 
61
};
50
62
 
51
63
 
52
64
/*************************************************
69
81
*           Find entry point for dnsdb           *
70
82
*************************************************/
71
83
 
72
 
/* See local README for interface description. */
 
84
/* See local README for interface description. The query in the "keystring" may
 
85
consist of a number of parts.
 
86
 
 
87
(a) If the first significant character is '>' then the next character is the
 
88
separator character that is used when multiple records are found. The default
 
89
separator is newline.
 
90
 
 
91
(b) If the next sequence of characters is 'defer_FOO' followed by a comma,
 
92
the defer behaviour is set to FOO. The possible behaviours are: 'strict', where
 
93
any defer causes the whole lookup to defer; 'lax', where a defer causes the
 
94
whole lookup to defer only if none of the DNS queries succeeds; and 'never',
 
95
where all defers are as if the lookup failed. The default is 'lax'.
 
96
 
 
97
(c) If the next sequence of characters is a sequence of letters and digits
 
98
followed by '=', it is interpreted as the name of the DNS record type. The
 
99
default is "TXT".
 
100
 
 
101
(d) Then there follows list of domain names. This is a generalized Exim list,
 
102
which may start with '<' in order to set a specific separator. The default
 
103
separator, as always, is colon. */
73
104
 
74
105
int
75
106
dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length,
76
 
  uschar **result, uschar **errmsg)
 
107
  uschar **result, uschar **errmsg, BOOL *do_cache)
77
108
{
78
109
int rc;
79
 
int size = 0;
 
110
int size = 256;
80
111
int ptr = 0;
 
112
int sep = 0;
 
113
int defer_mode = PASS;
81
114
int type = T_TXT;
82
 
uschar *yield = NULL;
83
 
uschar *equals = Ustrchr(keystring, '=');
 
115
int failrc = FAIL;
 
116
uschar *outsep = US"\n";
 
117
uschar *equals, *domain, *found;
84
118
uschar buffer[256];
85
119
 
 
120
/* Because we're the working in the search pool, we try to reclaim as much
 
121
store as possible later, so we preallocate the result here */
 
122
 
 
123
uschar *yield = store_get(size);
 
124
 
86
125
dns_record *rr;
87
126
dns_answer dnsa;
88
127
dns_scan dnss;
90
129
handle = handle;           /* Keep picky compilers happy */
91
130
filename = filename;
92
131
length = length;
93
 
 
94
 
/* If the keystring contains an = this is preceded by a type name. */
95
 
 
96
 
if (equals != NULL)
97
 
  {
98
 
  int i;
99
 
  int len = equals - keystring;
 
132
do_cache = do_cache;
 
133
 
 
134
/* If the string starts with '>' we change the output separator */
 
135
 
 
136
while (isspace(*keystring)) keystring++;
 
137
if (*keystring == '>')
 
138
  {
 
139
  outsep = keystring + 1;
 
140
  keystring += 2;
 
141
  while (isspace(*keystring)) keystring++;
 
142
  }
 
143
 
 
144
/* Check for a defer behaviour keyword. */
 
145
 
 
146
if (strncmpic(keystring, US"defer_", 6) == 0)
 
147
  {
 
148
  keystring += 6;
 
149
  if (strncmpic(keystring, US"strict", 6) == 0)
 
150
    {
 
151
    defer_mode = DEFER;
 
152
    keystring += 6;
 
153
    }
 
154
  else if (strncmpic(keystring, US"lax", 3) == 0)
 
155
    {
 
156
    defer_mode = PASS;
 
157
    keystring += 3;
 
158
    }
 
159
  else if (strncmpic(keystring, US"never", 5) == 0)
 
160
    {
 
161
    defer_mode = OK;
 
162
    keystring += 5;
 
163
    }
 
164
  else
 
165
    {
 
166
    *errmsg = US"unsupported dnsdb defer behaviour";
 
167
    return DEFER;
 
168
    }
 
169
  while (isspace(*keystring)) keystring++;
 
170
  if (*keystring++ != ',')
 
171
    {
 
172
    *errmsg = US"dnsdb defer behaviour syntax error";
 
173
    return DEFER;
 
174
    }
 
175
  while (isspace(*keystring)) keystring++;
 
176
  }
 
177
 
 
178
/* If the keystring contains an = this must be preceded by a valid type name. */
 
179
 
 
180
if ((equals = Ustrchr(keystring, '=')) != NULL)
 
181
  {
 
182
  int i, len;
 
183
  uschar *tend = equals;
 
184
 
 
185
  while (tend > keystring && isspace(tend[-1])) tend--;
 
186
  len = tend - keystring;
 
187
 
100
188
  for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++)
101
189
    {
102
190
    if (len == Ustrlen(type_names[i]) &&
106
194
      break;
107
195
      }
108
196
    }
 
197
 
109
198
  if (i >= sizeof(type_names)/sizeof(uschar *))
110
199
    {
111
200
    *errmsg = US"unsupported DNS record type";
112
201
    return DEFER;
113
202
    }
114
 
  keystring += len + 1;
115
 
  }
116
 
 
117
 
/* If the type is PTR, we have to construct the relevant magic lookup
118
 
key. This code is now in a separate function. */
119
 
 
120
 
if (type == T_PTR)
121
 
  {
122
 
  dns_build_reverse(keystring, buffer);
123
 
  keystring = buffer;
124
 
  }
125
 
 
126
 
DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", keystring);
127
 
 
128
 
/* Initialize the resolver, in case this is the first time it is used
129
 
in this run. Then do the lookup and sort out the result. */
 
203
 
 
204
  keystring = equals + 1;
 
205
  while (isspace(*keystring)) keystring++;
 
206
  }
 
207
 
 
208
/* Initialize the resolver in case this is the first time it has been used. */
130
209
 
131
210
dns_init(FALSE, FALSE);
132
 
rc = dns_lookup(&dnsa, keystring, type, NULL);
133
 
 
134
 
if (rc == DNS_NOMATCH || rc == DNS_NODATA) return FAIL;
135
 
if (rc != DNS_SUCCEED) return DEFER;
136
 
 
137
 
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
138
 
     rr != NULL;
139
 
     rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
 
211
 
 
212
/* The remainder of the string must be a list of domains. As long as the lookup
 
213
for at least one of them succeeds, we return success. Failure means that none
 
214
of them were found.
 
215
 
 
216
The original implementation did not support a list of domains. Adding the list
 
217
feature is compatible, except in one case: when PTR records are being looked up
 
218
for a single IPv6 address. Fortunately, we can hack in a compatibility feature
 
219
here: If the type is PTR and no list separator is specified, and the entire
 
220
remaining string is valid as an IP address, set an impossible separator so that
 
221
it is treated as one item. */
 
222
 
 
223
if (type == T_PTR && keystring[0] != '<' &&
 
224
    string_is_ip_address(keystring, NULL) > 0)
 
225
  sep = -1;
 
226
 
 
227
/* Now scan the list and do a lookup for each item */
 
228
 
 
229
while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer)))
 
230
        != NULL)
140
231
  {
141
 
  int len = 0;        /* Stop picky compilers warning */
142
 
  uschar *s = NULL;
143
 
 
144
 
  if (rr->type != type) continue;
145
 
 
146
 
  /* There may be several addresses from an A6 record. Put newlines between
147
 
  them, just as for between several records. */
148
 
 
149
 
  if (type == T_A ||
150
 
      #ifdef SUPPORT_A6
151
 
      type == T_A6 ||
152
 
      #endif
153
 
      type == T_AAAA)
154
 
    {
155
 
    dns_address *da;
156
 
    for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
157
 
      {
158
 
      len = Ustrlen(da->address);
159
 
      if (yield == NULL)
160
 
        {
161
 
        yield = da->address;
162
 
        ptr = len;
163
 
        size = ptr + 1;
164
 
        }
165
 
      else
166
 
        {
167
 
        yield = string_cat(yield, &size, &ptr, US"\n", 1);
168
 
        yield = string_cat(yield, &size, &ptr, da->address, len);
169
 
        }
170
 
      }
171
 
    continue;
172
 
    }
173
 
 
174
 
  /* Other kinds of record just have one piece of data each. */
175
 
 
176
 
  if (type == T_TXT)
177
 
    {
178
 
    len = (rr->data)[0];
179
 
    s = string_copyn((uschar *)(rr->data+1), len);
180
 
    }
181
 
  else   /* T_CNAME, T_MX, T_NS, T_PTR */
182
 
    {
183
 
    uschar *p = (uschar *)(rr->data);
184
 
    int ssize = 264;
185
 
    s = store_get(ssize);
186
 
    if (type == T_MX)
187
 
      {
188
 
      int number;
189
 
      GETSHORT(number, p);      /* pointer is advanced */
190
 
      sprintf(CS s, "%d ", number);
191
 
      len = Ustrlen(s);
192
 
      }
193
 
    p += dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
194
 
      (DN_EXPAND_ARG4_TYPE)(s + len), ssize - len);
195
 
    len += Ustrlen(s+len);
196
 
    store_reset(s + len + 1);
197
 
    }
198
 
 
199
 
  if (yield == NULL)
200
 
    {
201
 
    yield = s;
202
 
    ptr = len;
203
 
    size = ptr + 1;
204
 
    }
205
 
  else
206
 
    {
207
 
    yield = string_cat(yield, &size, &ptr, US"\n", 1);
208
 
    yield = string_cat(yield, &size, &ptr, s, len);
209
 
    }
210
 
  }
211
 
 
 
232
  uschar rbuffer[256];
 
233
  int searchtype = (type == T_CSA)? T_SRV :         /* record type we want */
 
234
                   (type == T_MXH)? T_MX :
 
235
                   (type == T_ZNS)? T_NS : type;
 
236
 
 
237
  /* If the type is PTR or CSA, we have to construct the relevant magic lookup
 
238
  key if the original is an IP address (some experimental protocols are using
 
239
  PTR records for different purposes where the key string is a host name, and
 
240
  Exim's extended CSA can be keyed by domains or IP addresses). This code for
 
241
  doing the reversal is now in a separate function. */
 
242
 
 
243
  if ((type == T_PTR || type == T_CSA) &&
 
244
      string_is_ip_address(domain, NULL) > 0)
 
245
    {
 
246
    dns_build_reverse(domain, rbuffer);
 
247
    domain = rbuffer;
 
248
    }
 
249
 
 
250
  DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", domain);
 
251
 
 
252
  /* Do the lookup and sort out the result. There are three special types that
 
253
  are handled specially: T_CSA, T_ZNS and T_MXH. The former two are handled in
 
254
  a special lookup function so that the facility could be used from other
 
255
  parts of the Exim code. The latter affects only what happens later on in
 
256
  this function, but for tidiness it is handled in a similar way. If the
 
257
  lookup fails, continue with the next domain. In the case of DEFER, adjust
 
258
  the final "nothing found" result, but carry on to the next domain. */
 
259
 
 
260
  found = domain;
 
261
  rc = dns_special_lookup(&dnsa, domain, type, &found);
 
262
 
 
263
  if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue;
 
264
  if (rc != DNS_SUCCEED)
 
265
    {
 
266
    if (defer_mode == DEFER) return DEFER;          /* always defer */
 
267
      else if (defer_mode == PASS) failrc = DEFER;  /* defer only if all do */
 
268
    continue;                                       /* treat defer as fail */
 
269
    }
 
270
 
 
271
  /* Search the returned records */
 
272
 
 
273
  for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
 
274
       rr != NULL;
 
275
       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
 
276
    {
 
277
    if (rr->type != searchtype) continue;
 
278
 
 
279
    /* There may be several addresses from an A6 record. Put the configured
 
280
    separator between them, just as for between several records. However, A6
 
281
    support is not normally configured these days. */
 
282
 
 
283
    if (type == T_A ||
 
284
        #ifdef SUPPORT_A6
 
285
        type == T_A6 ||
 
286
        #endif
 
287
        type == T_AAAA)
 
288
      {
 
289
      dns_address *da;
 
290
      for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
 
291
        {
 
292
        if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1);
 
293
        yield = string_cat(yield, &size, &ptr, da->address,
 
294
          Ustrlen(da->address));
 
295
        }
 
296
      continue;
 
297
      }
 
298
 
 
299
    /* Other kinds of record just have one piece of data each, but there may be
 
300
    several of them, of course. */
 
301
 
 
302
    if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1);
 
303
 
 
304
    if (type == T_TXT)
 
305
      {
 
306
      yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1),
 
307
        (rr->data)[0]);
 
308
      }
 
309
    else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */
 
310
      {
 
311
      int priority, weight, port;
 
312
      uschar s[264];
 
313
      uschar *p = (uschar *)(rr->data);
 
314
 
 
315
      if (type == T_MXH)
 
316
        {
 
317
        /* mxh ignores the priority number and includes only the hostnames */
 
318
        GETSHORT(priority, p);
 
319
        }
 
320
      else if (type == T_MX)
 
321
        {
 
322
        GETSHORT(priority, p);
 
323
        sprintf(CS s, "%d ", priority);
 
324
        yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
325
        }
 
326
      else if (type == T_SRV)
 
327
        {
 
328
        GETSHORT(priority, p);
 
329
        GETSHORT(weight, p);
 
330
        GETSHORT(port, p);
 
331
        sprintf(CS s, "%d %d %d ", priority, weight, port);
 
332
        yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
333
        }
 
334
      else if (type == T_CSA)
 
335
        {
 
336
        /* See acl_verify_csa() for more comments about CSA. */
 
337
 
 
338
        GETSHORT(priority, p);
 
339
        GETSHORT(weight, p);
 
340
        GETSHORT(port, p);
 
341
 
 
342
        if (priority != 1) continue;      /* CSA version must be 1 */
 
343
 
 
344
        /* If the CSA record we found is not the one we asked for, analyse
 
345
        the subdomain assertions in the port field, else analyse the direct
 
346
        authorization status in the weight field. */
 
347
 
 
348
        if (found != domain)
 
349
          {
 
350
          if (port & 1) *s = 'X';         /* explicit authorization required */
 
351
          else *s = '?';                  /* no subdomain assertions here */
 
352
          }
 
353
        else
 
354
          {
 
355
          if (weight < 2) *s = 'N';       /* not authorized */
 
356
          else if (weight == 2) *s = 'Y'; /* authorized */
 
357
          else if (weight == 3) *s = '?'; /* unauthorizable */
 
358
          else continue;                  /* invalid */
 
359
          }
 
360
 
 
361
        s[1] = ' ';
 
362
        yield = string_cat(yield, &size, &ptr, s, 2);
 
363
        }
 
364
 
 
365
      /* GETSHORT() has advanced the pointer to the target domain. */
 
366
 
 
367
      rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
 
368
        (DN_EXPAND_ARG4_TYPE)(s), sizeof(s));
 
369
 
 
370
      /* If an overlong response was received, the data will have been
 
371
      truncated and dn_expand may fail. */
 
372
 
 
373
      if (rc < 0)
 
374
        {
 
375
        log_write(0, LOG_MAIN, "host name alias list truncated: type=%s "
 
376
          "domain=%s", dns_text_type(type), domain);
 
377
        break;
 
378
        }
 
379
      else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
380
      }
 
381
    }    /* Loop for list of returned records */
 
382
  }      /* Loop for list of domains */
 
383
 
 
384
/* Reclaim unused memory */
 
385
 
 
386
store_reset(yield + ptr + 1);
 
387
 
 
388
/* If ptr == 0 we have not found anything. Otherwise, insert the terminating
 
389
zero and return the result. */
 
390
 
 
391
if (ptr == 0) return failrc;
212
392
yield[ptr] = 0;
213
393
*result = yield;
214
 
 
215
394
return OK;
216
395
}
217
396