sendmail: Update vendor branch to v8.14.4
[dragonfly.git] / contrib / sendmail-8.14 / 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.36 2008/02/11 23:04:16 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         unsigned short ans_cnt, ui;
172         int status;
173         size_t l;
174         char host[MAXHOSTNAMELEN];
175         DNS_REPLY_T *r;
176         RESOURCE_RECORD_T **rr;
177
178         r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
179         if (r == NULL)
180                 return NULL;
181         memset(r, 0, sizeof(*r));
182
183         p = data;
184
185         /* doesn't work on Crays? */
186         memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
187         p += sizeof(r->dns_r_h);
188         status = dn_expand(data, data + len, p, host, sizeof(host));
189         if (status < 0)
190         {
191                 dns_free_data(r);
192                 return NULL;
193         }
194         r->dns_r_q.dns_q_domain = sm_strdup(host);
195         if (r->dns_r_q.dns_q_domain == NULL)
196         {
197                 dns_free_data(r);
198                 return NULL;
199         }
200
201         ans_cnt = ntohs((unsigned short) r->dns_r_h.ancount);
202
203         p += status;
204         GETSHORT(r->dns_r_q.dns_q_type, p);
205         GETSHORT(r->dns_r_q.dns_q_class, p);
206         rr = &r->dns_r_head;
207         ui = 0;
208         while (p < data + len && ui < ans_cnt)
209         {
210                 int type, class, ttl, size, txtlen;
211
212                 status = dn_expand(data, data + len, p, host, sizeof(host));
213                 if (status < 0)
214                 {
215                         dns_free_data(r);
216                         return NULL;
217                 }
218                 ++ui;
219                 p += status;
220                 GETSHORT(type, p);
221                 GETSHORT(class, p);
222                 GETLONG(ttl, p);
223                 GETSHORT(size, p);
224                 if (p + size > data + len)
225                 {
226                         /*
227                         **  announced size of data exceeds length of
228                         **  data paket: someone is cheating.
229                         */
230
231                         if (LogLevel > 5)
232                                 sm_syslog(LOG_WARNING, NOQID,
233                                           "ERROR: DNS RDLENGTH=%d > data len=%d",
234                                           size, len - (p - data));
235                         dns_free_data(r);
236                         return NULL;
237                 }
238                 *rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
239                 if (*rr == NULL)
240                 {
241                         dns_free_data(r);
242                         return NULL;
243                 }
244                 memset(*rr, 0, sizeof(**rr));
245                 (*rr)->rr_domain = sm_strdup(host);
246                 if ((*rr)->rr_domain == NULL)
247                 {
248                         dns_free_data(r);
249                         return NULL;
250                 }
251                 (*rr)->rr_type = type;
252                 (*rr)->rr_class = class;
253                 (*rr)->rr_ttl = ttl;
254                 (*rr)->rr_size = size;
255                 switch (type)
256                 {
257                   case T_NS:
258                   case T_CNAME:
259                   case T_PTR:
260                         status = dn_expand(data, data + len, p, host,
261                                            sizeof(host));
262                         if (status < 0)
263                         {
264                                 dns_free_data(r);
265                                 return NULL;
266                         }
267                         (*rr)->rr_u.rr_txt = sm_strdup(host);
268                         if ((*rr)->rr_u.rr_txt == NULL)
269                         {
270                                 dns_free_data(r);
271                                 return NULL;
272                         }
273                         break;
274
275                   case T_MX:
276                   case T_AFSDB:
277                         status = dn_expand(data, data + len, p + 2, host,
278                                            sizeof(host));
279                         if (status < 0)
280                         {
281                                 dns_free_data(r);
282                                 return NULL;
283                         }
284                         l = strlen(host) + 1;
285                         (*rr)->rr_u.rr_mx = (MX_RECORD_T *)
286                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
287                         if ((*rr)->rr_u.rr_mx == NULL)
288                         {
289                                 dns_free_data(r);
290                                 return NULL;
291                         }
292                         (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
293                         (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
294                                           host, l);
295                         break;
296
297                   case T_SRV:
298                         status = dn_expand(data, data + len, p + 6, host,
299                                            sizeof(host));
300                         if (status < 0)
301                         {
302                                 dns_free_data(r);
303                                 return NULL;
304                         }
305                         l = strlen(host) + 1;
306                         (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
307                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
308                         if ((*rr)->rr_u.rr_srv == NULL)
309                         {
310                                 dns_free_data(r);
311                                 return NULL;
312                         }
313                         (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
314                         (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
315                         (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
316                         (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
317                                           host, l);
318                         break;
319
320                   case T_TXT:
321
322                         /*
323                         **  The TXT record contains the length as
324                         **  leading byte, hence the value is restricted
325                         **  to 255, which is less than the maximum value
326                         **  of RDLENGTH (size). Nevertheless, txtlen
327                         **  must be less than size because the latter
328                         **  specifies the length of the entire TXT
329                         **  record.
330                         */
331
332                         txtlen = *p;
333                         if (txtlen >= size)
334                         {
335                                 if (LogLevel > 5)
336                                         sm_syslog(LOG_WARNING, NOQID,
337                                                   "ERROR: DNS TXT record size=%d <= text len=%d",
338                                                   size, txtlen);
339                                 dns_free_data(r);
340                                 return NULL;
341                         }
342                         (*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
343                         if ((*rr)->rr_u.rr_txt == NULL)
344                         {
345                                 dns_free_data(r);
346                                 return NULL;
347                         }
348                         (void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
349                                           txtlen + 1);
350                         break;
351
352                   default:
353                         (*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
354                         if ((*rr)->rr_u.rr_data == NULL)
355                         {
356                                 dns_free_data(r);
357                                 return NULL;
358                         }
359                         (void) memcpy((*rr)->rr_u.rr_data, p, size);
360                         break;
361                 }
362                 p += size;
363                 rr = &(*rr)->rr_next;
364         }
365         *rr = NULL;
366         return r;
367 }
368
369 /*
370 **  DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
371 **
372 **      Parameters:
373 **              domain -- name to lookup
374 **              rr_class -- resource record class
375 **              rr_type -- resource record type
376 **              retrans -- retransmission timeout
377 **              retry -- number of retries
378 **
379 **      Returns:
380 **              result of lookup if succeeded.
381 **              NULL otherwise.
382 */
383
384 DNS_REPLY_T *
385 dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
386         const char *domain;
387         int rr_class;
388         int rr_type;
389         time_t retrans;
390         int retry;
391 {
392         int len;
393         unsigned long old_options = 0;
394         time_t save_retrans = 0;
395         int save_retry = 0;
396         DNS_REPLY_T *r = NULL;
397         unsigned char reply[1024];
398
399         if (tTd(8, 16))
400         {
401                 old_options = _res.options;
402                 _res.options |= RES_DEBUG;
403                 sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
404                            rr_class, dns_type_to_string(rr_type));
405         }
406         if (retrans > 0)
407         {
408                 save_retrans = _res.retrans;
409                 _res.retrans = retrans;
410         }
411         if (retry > 0)
412         {
413                 save_retry = _res.retry;
414                 _res.retry = retry;
415         }
416         errno = 0;
417         SM_SET_H_ERRNO(0);
418         len = res_search(domain, rr_class, rr_type, reply, sizeof(reply));
419         if (tTd(8, 16))
420         {
421                 _res.options = old_options;
422                 sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
423                            domain, rr_class, dns_type_to_string(rr_type), len);
424         }
425         if (len >= 0)
426                 r = parse_dns_reply(reply, len);
427         if (retrans > 0)
428                 _res.retrans = save_retrans;
429         if (retry > 0)
430                 _res.retry = save_retry;
431         return r;
432 }
433
434 #  if 0
435 DNS_REPLY_T *
436 dns_lookup(domain, type_name, retrans, retry)
437         const char *domain;
438         const char *type_name;
439         time_t retrans;
440         int retry;
441 {
442         int type;
443
444         type = dns_string_to_type(type_name);
445         if (type == -1)
446         {
447                 if (tTd(8, 16))
448                         sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
449                                 type_name);
450                 return NULL;
451         }
452         return dns_lookup_int(domain, C_IN, type, retrans, retry);
453 }
454 #  endif /* 0 */
455 # endif /* NAMED_BIND */
456 #endif /* DNSMAP */