~ubuntu-branches/ubuntu/natty/exim4/natty-updates

« back to all changes in this revision

Viewing changes to src/lookups/dnsdb.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Bienia
  • Date: 2010-01-01 16:28:19 UTC
  • mfrom: (2.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20100101162819-htn71my7yj4v1vkr
Tags: 4.71-3ubuntu1
* Merge with Debian unstable (lp: #501657). Remaining changes:
  + debian/patches/71_exiq_grep_error_on_messages_without_size.dpatch:
    Improve handling of broken messages when "exim4 -bp" (mailq) reports
    lines without size info.
  + Don't declare a Provides: default-mta; in Ubuntu, we want postfix to be
    the default.
  + debian/control: Change build dependencies to MySQL 5.1.
  + debian/{control,rules}: add and enable hardened build for PIE
    (Debian bug 542726).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Cambridge: exim/exim-src/src/lookups/dnsdb.c,v 1.19 2009/11/16 19:50:38 nm4 Exp $ */
 
2
 
 
3
/*************************************************
 
4
*     Exim - an Internet mail transport agent    *
 
5
*************************************************/
 
6
 
 
7
/* Copyright (c) University of Cambridge 1995 - 2009 */
 
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
  "csa",
 
35
  "mx",
 
36
  "mxh",
 
37
  "ns",
 
38
  "ptr",
 
39
  "srv",
 
40
  "txt",
 
41
  "zns"
 
42
};
 
43
 
 
44
static int type_values[] = {
 
45
  T_A,
 
46
#if HAVE_IPV6
 
47
  T_AAAA,
 
48
  #ifdef SUPPORT_A6
 
49
  T_A6,
 
50
  #endif
 
51
#endif
 
52
  T_CNAME,
 
53
  T_CSA,     /* Private type for "Client SMTP Authorization". */
 
54
  T_MX,
 
55
  T_MXH,     /* Private type for "MX hostnames" */
 
56
  T_NS,
 
57
  T_PTR,
 
58
  T_SRV,
 
59
  T_TXT,
 
60
  T_ZNS      /* Private type for "zone nameservers" */
 
61
};
 
62
 
 
63
 
 
64
/*************************************************
 
65
*              Open entry point                  *
 
66
*************************************************/
 
67
 
 
68
/* See local README for interface description. */
 
69
 
 
70
void *
 
71
dnsdb_open(uschar *filename, uschar **errmsg)
 
72
{
 
73
filename = filename;   /* Keep picky compilers happy */
 
74
errmsg = errmsg;       /* Ditto */
 
75
return (void *)(-1);   /* Any non-0 value */
 
76
}
 
77
 
 
78
 
 
79
 
 
80
/*************************************************
 
81
*           Find entry point for dnsdb           *
 
82
*************************************************/
 
83
 
 
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. */
 
104
 
 
105
int
 
106
dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length,
 
107
  uschar **result, uschar **errmsg, BOOL *do_cache)
 
108
{
 
109
int rc;
 
110
int size = 256;
 
111
int ptr = 0;
 
112
int sep = 0;
 
113
int defer_mode = PASS;
 
114
int type = T_TXT;
 
115
int failrc = FAIL;
 
116
uschar *outsep = US"\n";
 
117
uschar *equals, *domain, *found;
 
118
uschar buffer[256];
 
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
 
 
125
dns_record *rr;
 
126
dns_answer dnsa;
 
127
dns_scan dnss;
 
128
 
 
129
handle = handle;           /* Keep picky compilers happy */
 
130
filename = filename;
 
131
length = length;
 
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
 
 
188
  for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++)
 
189
    {
 
190
    if (len == Ustrlen(type_names[i]) &&
 
191
        strncmpic(keystring, US type_names[i], len) == 0)
 
192
      {
 
193
      type = type_values[i];
 
194
      break;
 
195
      }
 
196
    }
 
197
 
 
198
  if (i >= sizeof(type_names)/sizeof(uschar *))
 
199
    {
 
200
    *errmsg = US"unsupported DNS record type";
 
201
    return DEFER;
 
202
    }
 
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. */
 
209
 
 
210
dns_init(FALSE, FALSE);
 
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)
 
231
  {
 
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
      int data_offset = 0;
 
307
      while (data_offset < rr->size)
 
308
        {
 
309
        uschar chunk_len = (rr->data)[data_offset++];
 
310
        yield = string_cat(yield, &size, &ptr,
 
311
                           (uschar *)((rr->data)+data_offset), chunk_len);
 
312
        data_offset += chunk_len;
 
313
        }
 
314
      }
 
315
    else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */
 
316
      {
 
317
      int priority, weight, port;
 
318
      uschar s[264];
 
319
      uschar *p = (uschar *)(rr->data);
 
320
 
 
321
      if (type == T_MXH)
 
322
        {
 
323
        /* mxh ignores the priority number and includes only the hostnames */
 
324
        GETSHORT(priority, p);
 
325
        }
 
326
      else if (type == T_MX)
 
327
        {
 
328
        GETSHORT(priority, p);
 
329
        sprintf(CS s, "%d ", priority);
 
330
        yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
331
        }
 
332
      else if (type == T_SRV)
 
333
        {
 
334
        GETSHORT(priority, p);
 
335
        GETSHORT(weight, p);
 
336
        GETSHORT(port, p);
 
337
        sprintf(CS s, "%d %d %d ", priority, weight, port);
 
338
        yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
339
        }
 
340
      else if (type == T_CSA)
 
341
        {
 
342
        /* See acl_verify_csa() for more comments about CSA. */
 
343
 
 
344
        GETSHORT(priority, p);
 
345
        GETSHORT(weight, p);
 
346
        GETSHORT(port, p);
 
347
 
 
348
        if (priority != 1) continue;      /* CSA version must be 1 */
 
349
 
 
350
        /* If the CSA record we found is not the one we asked for, analyse
 
351
        the subdomain assertions in the port field, else analyse the direct
 
352
        authorization status in the weight field. */
 
353
 
 
354
        if (found != domain)
 
355
          {
 
356
          if (port & 1) *s = 'X';         /* explicit authorization required */
 
357
          else *s = '?';                  /* no subdomain assertions here */
 
358
          }
 
359
        else
 
360
          {
 
361
          if (weight < 2) *s = 'N';       /* not authorized */
 
362
          else if (weight == 2) *s = 'Y'; /* authorized */
 
363
          else if (weight == 3) *s = '?'; /* unauthorizable */
 
364
          else continue;                  /* invalid */
 
365
          }
 
366
 
 
367
        s[1] = ' ';
 
368
        yield = string_cat(yield, &size, &ptr, s, 2);
 
369
        }
 
370
 
 
371
      /* GETSHORT() has advanced the pointer to the target domain. */
 
372
 
 
373
      rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
 
374
        (DN_EXPAND_ARG4_TYPE)(s), sizeof(s));
 
375
 
 
376
      /* If an overlong response was received, the data will have been
 
377
      truncated and dn_expand may fail. */
 
378
 
 
379
      if (rc < 0)
 
380
        {
 
381
        log_write(0, LOG_MAIN, "host name alias list truncated: type=%s "
 
382
          "domain=%s", dns_text_type(type), domain);
 
383
        break;
 
384
        }
 
385
      else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
 
386
      }
 
387
    }    /* Loop for list of returned records */
 
388
  }      /* Loop for list of domains */
 
389
 
 
390
/* Reclaim unused memory */
 
391
 
 
392
store_reset(yield + ptr + 1);
 
393
 
 
394
/* If ptr == 0 we have not found anything. Otherwise, insert the terminating
 
395
zero and return the result. */
 
396
 
 
397
if (ptr == 0) return failrc;
 
398
yield[ptr] = 0;
 
399
*result = yield;
 
400
return OK;
 
401
}
 
402
 
 
403
/* End of lookups/dnsdb.c */