Merge from vendor branch CVS:
[dragonfly.git] / contrib / sendmail-8.13.4 / sendmail / sm_resolve.c
1 /*
2  * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10
11 /*
12  * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
13  * (Royal Institute of Technology, Stockholm, Sweden).
14  * All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  *
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  *
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  *
27  * 3. Neither the name of the Institute nor the names of its contributors
28  *    may be used to endorse or promote products derived from this software
29  *    without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
32  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
35  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41  * SUCH DAMAGE.
42  */
43
44 #include <sendmail.h>
45 #if DNSMAP
46 # if NAMED_BIND
47 #  include "sm_resolve.h"
48
49 SM_RCSID("$Id: sm_resolve.c,v 8.33 2004/08/04 21:17:57 ca Exp $")
50
51 static struct stot
52 {
53         const char      *st_name;
54         int             st_type;
55 } stot[] =
56 {
57 #  if NETINET
58         {       "A",            T_A             },
59 #  endif /* NETINET */
60 #  if NETINET6
61         {       "AAAA",         T_AAAA          },
62 #  endif /* NETINET6 */
63         {       "NS",           T_NS            },
64         {       "CNAME",        T_CNAME         },
65         {       "PTR",          T_PTR           },
66         {       "MX",           T_MX            },
67         {       "TXT",          T_TXT           },
68         {       "AFSDB",        T_AFSDB         },
69         {       "SRV",          T_SRV           },
70         {       NULL,           0               }
71 };
72
73 static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int));
74
75 /*
76 **  DNS_STRING_TO_TYPE -- convert resource record name into type
77 **
78 **      Parameters:
79 **              name -- name of resource record type
80 **
81 **      Returns:
82 **              type if succeeded.
83 **              -1 otherwise.
84 */
85
86 int
87 dns_string_to_type(name)
88         const char *name;
89 {
90         struct stot *p = stot;
91
92         for (p = stot; p->st_name != NULL; p++)
93                 if (sm_strcasecmp(name, p->st_name) == 0)
94                         return p->st_type;
95         return -1;
96 }
97
98 /*
99 **  DNS_TYPE_TO_STRING -- convert resource record type into name
100 **
101 **      Parameters:
102 **              type -- resource record type
103 **
104 **      Returns:
105 **              name if succeeded.
106 **              NULL otherwise.
107 */
108
109 const char *
110 dns_type_to_string(type)
111         int type;
112 {
113         struct stot *p = stot;
114
115         for (p = stot; p->st_name != NULL; p++)
116                 if (type == p->st_type)
117                         return p->st_name;
118         return NULL;
119 }
120
121 /*
122 **  DNS_FREE_DATA -- free all components of a DNS_REPLY_T
123 **
124 **      Parameters:
125 **              r -- pointer to DNS_REPLY_T
126 **
127 **      Returns:
128 **              none.
129 */
130
131 void
132 dns_free_data(r)
133         DNS_REPLY_T *r;
134 {
135         RESOURCE_RECORD_T *rr;
136
137         if (r->dns_r_q.dns_q_domain != NULL)
138                 sm_free(r->dns_r_q.dns_q_domain);
139         for (rr = r->dns_r_head; rr != NULL; )
140         {
141                 RESOURCE_RECORD_T *tmp = rr;
142
143                 if (rr->rr_domain != NULL)
144                         sm_free(rr->rr_domain);
145                 if (rr->rr_u.rr_data != NULL)
146                         sm_free(rr->rr_u.rr_data);
147                 rr = rr->rr_next;
148                 sm_free(tmp);
149         }
150         sm_free(r);
151 }
152
153 /*
154 **  PARSE_DNS_REPLY -- parse DNS reply data.
155 **
156 **      Parameters:
157 **              data -- pointer to dns data
158 **              len -- len of data
159 **
160 **      Returns:
161 **              pointer to DNS_REPLY_T if succeeded.
162 **              NULL otherwise.
163 */
164
165 static DNS_REPLY_T *
166 parse_dns_reply(data, len)
167         unsigned char *data;
168         int len;
169 {
170         unsigned char *p;
171         int status;
172         size_t l;
173         char host[MAXHOSTNAMELEN];
174         DNS_REPLY_T *r;
175         RESOURCE_RECORD_T **rr;
176
177         r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
178         if (r == NULL)
179                 return NULL;
180         memset(r, 0, sizeof(*r));
181
182         p = data;
183
184         /* doesn't work on Crays? */
185         memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
186         p += sizeof(r->dns_r_h);
187         status = dn_expand(data, data + len, p, host, sizeof host);
188         if (status < 0)
189         {
190                 dns_free_data(r);
191                 return NULL;
192         }
193         r->dns_r_q.dns_q_domain = sm_strdup(host);
194         if (r->dns_r_q.dns_q_domain == NULL)
195         {
196                 dns_free_data(r);
197                 return NULL;
198         }
199         p += status;
200         GETSHORT(r->dns_r_q.dns_q_type, p);
201         GETSHORT(r->dns_r_q.dns_q_class, p);
202         rr = &r->dns_r_head;
203         while (p < data + len)
204         {
205                 int type, class, ttl, size, txtlen;
206
207                 status = dn_expand(data, data + len, p, host, sizeof host);
208                 if (status < 0)
209                 {
210                         dns_free_data(r);
211                         return NULL;
212                 }
213                 p += status;
214                 GETSHORT(type, p);
215                 GETSHORT(class, p);
216                 GETLONG(ttl, p);
217                 GETSHORT(size, p);
218                 if (p + size > data + len)
219                 {
220                         /*
221                         **  announced size of data exceeds length of
222                         **  data paket: someone is cheating.
223                         */
224
225                         if (LogLevel > 5)
226                                 sm_syslog(LOG_WARNING, NOQID,
227                                           "ERROR: DNS RDLENGTH=%d > data len=%d",
228                                           size, len - (p - data));
229                         dns_free_data(r);
230                         return NULL;
231                 }
232                 *rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
233                 if (*rr == NULL)
234                 {
235                         dns_free_data(r);
236                         return NULL;
237                 }
238                 memset(*rr, 0, sizeof(**rr));
239                 (*rr)->rr_domain = sm_strdup(host);
240                 if ((*rr)->rr_domain == NULL)
241                 {
242                         dns_free_data(r);
243                         return NULL;
244                 }
245                 (*rr)->rr_type = type;
246                 (*rr)->rr_class = class;
247                 (*rr)->rr_ttl = ttl;
248                 (*rr)->rr_size = size;
249                 switch (type)
250                 {
251                   case T_NS:
252                   case T_CNAME:
253                   case T_PTR:
254                         status = dn_expand(data, data + len, p, host,
255                                            sizeof host);
256                         if (status < 0)
257                         {
258                                 dns_free_data(r);
259                                 return NULL;
260                         }
261                         (*rr)->rr_u.rr_txt = sm_strdup(host);
262                         if ((*rr)->rr_u.rr_txt == NULL)
263                         {
264                                 dns_free_data(r);
265                                 return NULL;
266                         }
267                         break;
268
269                   case T_MX:
270                   case T_AFSDB:
271                         status = dn_expand(data, data + len, p + 2, host,
272                                            sizeof host);
273                         if (status < 0)
274                         {
275                                 dns_free_data(r);
276                                 return NULL;
277                         }
278                         l = strlen(host) + 1;
279                         (*rr)->rr_u.rr_mx = (MX_RECORD_T *)
280                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
281                         if ((*rr)->rr_u.rr_mx == NULL)
282                         {
283                                 dns_free_data(r);
284                                 return NULL;
285                         }
286                         (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
287                         (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
288                                           host, l);
289                         break;
290
291                   case T_SRV:
292                         status = dn_expand(data, data + len, p + 6, host,
293                                            sizeof host);
294                         if (status < 0)
295                         {
296                                 dns_free_data(r);
297                                 return NULL;
298                         }
299                         l = strlen(host) + 1;
300                         (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
301                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
302                         if ((*rr)->rr_u.rr_srv == NULL)
303                         {
304                                 dns_free_data(r);
305                                 return NULL;
306                         }
307                         (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
308                         (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
309                         (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
310                         (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
311                                           host, l);
312                         break;
313
314                   case T_TXT:
315
316                         /*
317                         **  The TXT record contains the length as
318                         **  leading byte, hence the value is restricted
319                         **  to 255, which is less than the maximum value
320                         **  of RDLENGTH (size). Nevertheless, txtlen
321                         **  must be less than size because the latter
322                         **  specifies the length of the entire TXT
323                         **  record.
324                         */
325
326                         txtlen = *p;
327                         if (txtlen >= size)
328                         {
329                                 if (LogLevel > 5)
330                                         sm_syslog(LOG_WARNING, NOQID,
331                                                   "ERROR: DNS TXT record size=%d <= text len=%d",
332                                                   size, txtlen);
333                                 dns_free_data(r);
334                                 return NULL;
335                         }
336                         (*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
337                         if ((*rr)->rr_u.rr_txt == NULL)
338                         {
339                                 dns_free_data(r);
340                                 return NULL;
341                         }
342                         (void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
343                                           txtlen + 1);
344                         break;
345
346                   default:
347                         (*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
348                         if ((*rr)->rr_u.rr_data == NULL)
349                         {
350                                 dns_free_data(r);
351                                 return NULL;
352                         }
353                         (void) memcpy((*rr)->rr_u.rr_data, p, size);
354                         break;
355                 }
356                 p += size;
357                 rr = &(*rr)->rr_next;
358         }
359         *rr = NULL;
360         return r;
361 }
362
363 /*
364 **  DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
365 **
366 **      Parameters:
367 **              domain -- name to lookup
368 **              rr_class -- resource record class
369 **              rr_type -- resource record type
370 **              retrans -- retransmission timeout
371 **              retry -- number of retries
372 **
373 **      Returns:
374 **              result of lookup if succeeded.
375 **              NULL otherwise.
376 */
377
378 DNS_REPLY_T *
379 dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
380         const char *domain;
381         int rr_class;
382         int rr_type;
383         time_t retrans;
384         int retry;
385 {
386         int len;
387         unsigned long old_options = 0;
388         time_t save_retrans = 0;
389         int save_retry = 0;
390         DNS_REPLY_T *r = NULL;
391         unsigned char reply[1024];
392
393         if (tTd(8, 16))
394         {
395                 old_options = _res.options;
396                 _res.options |= RES_DEBUG;
397                 sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
398                            rr_class, dns_type_to_string(rr_type));
399         }
400         if (retrans > 0)
401         {
402                 save_retrans = _res.retrans;
403                 _res.retrans = retrans;
404         }
405         if (retry > 0)
406         {
407                 save_retry = _res.retry;
408                 _res.retry = retry;
409         }
410         errno = 0;
411         SM_SET_H_ERRNO(0);
412         len = res_search(domain, rr_class, rr_type, reply, sizeof reply);
413         if (tTd(8, 16))
414         {
415                 _res.options = old_options;
416                 sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
417                            domain, rr_class, dns_type_to_string(rr_type), len);
418         }
419         if (len >= 0)
420                 r = parse_dns_reply(reply, len);
421         if (retrans > 0)
422                 _res.retrans = save_retrans;
423         if (retry > 0)
424                 _res.retry = save_retry;
425         return r;
426 }
427
428 #  if 0
429 DNS_REPLY_T *
430 dns_lookup(domain, type_name, retrans, retry)
431         const char *domain;
432         const char *type_name;
433         time_t retrans;
434         int retry;
435 {
436         int type;
437
438         type = dns_string_to_type(type_name);
439         if (type == -1)
440         {
441                 if (tTd(8, 16))
442                         sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
443                                 type_name);
444                 return NULL;
445         }
446         return dns_lookup_int(domain, C_IN, type, retrans, retry);
447 }
448 #  endif /* 0 */
449 # endif /* NAMED_BIND */
450 #endif /* DNSMAP */