sendmail: Update vendor branch to v8.14.15
[dragonfly.git] / contrib / sendmail-8.14 / sendmail / sm_resolve.c
1 /*
2  * Copyright (c) 2000-2004, 2010 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 #  if NETINET
48 #   include <netinet/in_systm.h>
49 #   include <netinet/ip.h>
50 #  endif /* NETINET */
51 #  include "sm_resolve.h"
52
53 SM_RCSID("$Id: sm_resolve.c,v 8.39 2010/06/29 15:35:33 ca Exp $")
54
55 static struct stot
56 {
57         const char      *st_name;
58         int             st_type;
59 } stot[] =
60 {
61 #  if NETINET
62         {       "A",            T_A             },
63 #  endif /* NETINET */
64 #  if NETINET6
65         {       "AAAA",         T_AAAA          },
66 #  endif /* NETINET6 */
67         {       "NS",           T_NS            },
68         {       "CNAME",        T_CNAME         },
69         {       "PTR",          T_PTR           },
70         {       "MX",           T_MX            },
71         {       "TXT",          T_TXT           },
72         {       "AFSDB",        T_AFSDB         },
73         {       "SRV",          T_SRV           },
74         {       NULL,           0               }
75 };
76
77 static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int));
78
79 /*
80 **  DNS_STRING_TO_TYPE -- convert resource record name into type
81 **
82 **      Parameters:
83 **              name -- name of resource record type
84 **
85 **      Returns:
86 **              type if succeeded.
87 **              -1 otherwise.
88 */
89
90 int
91 dns_string_to_type(name)
92         const char *name;
93 {
94         struct stot *p = stot;
95
96         for (p = stot; p->st_name != NULL; p++)
97                 if (sm_strcasecmp(name, p->st_name) == 0)
98                         return p->st_type;
99         return -1;
100 }
101
102 /*
103 **  DNS_TYPE_TO_STRING -- convert resource record type into name
104 **
105 **      Parameters:
106 **              type -- resource record type
107 **
108 **      Returns:
109 **              name if succeeded.
110 **              NULL otherwise.
111 */
112
113 const char *
114 dns_type_to_string(type)
115         int type;
116 {
117         struct stot *p = stot;
118
119         for (p = stot; p->st_name != NULL; p++)
120                 if (type == p->st_type)
121                         return p->st_name;
122         return NULL;
123 }
124
125 /*
126 **  DNS_FREE_DATA -- free all components of a DNS_REPLY_T
127 **
128 **      Parameters:
129 **              r -- pointer to DNS_REPLY_T
130 **
131 **      Returns:
132 **              none.
133 */
134
135 void
136 dns_free_data(r)
137         DNS_REPLY_T *r;
138 {
139         RESOURCE_RECORD_T *rr;
140
141         if (r->dns_r_q.dns_q_domain != NULL)
142                 sm_free(r->dns_r_q.dns_q_domain);
143         for (rr = r->dns_r_head; rr != NULL; )
144         {
145                 RESOURCE_RECORD_T *tmp = rr;
146
147                 if (rr->rr_domain != NULL)
148                         sm_free(rr->rr_domain);
149                 if (rr->rr_u.rr_data != NULL)
150                         sm_free(rr->rr_u.rr_data);
151                 rr = rr->rr_next;
152                 sm_free(tmp);
153         }
154         sm_free(r);
155 }
156
157 /*
158 **  PARSE_DNS_REPLY -- parse DNS reply data.
159 **
160 **      Parameters:
161 **              data -- pointer to dns data
162 **              len -- len of data
163 **
164 **      Returns:
165 **              pointer to DNS_REPLY_T if succeeded.
166 **              NULL otherwise.
167 */
168
169 static DNS_REPLY_T *
170 parse_dns_reply(data, len)
171         unsigned char *data;
172         int len;
173 {
174         unsigned char *p;
175         unsigned short ans_cnt, ui;
176         int status;
177         size_t l;
178         char host[MAXHOSTNAMELEN];
179         DNS_REPLY_T *r;
180         RESOURCE_RECORD_T **rr;
181
182         r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
183         if (r == NULL)
184                 return NULL;
185         memset(r, 0, sizeof(*r));
186
187         p = data;
188
189         /* doesn't work on Crays? */
190         memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
191         p += sizeof(r->dns_r_h);
192         status = dn_expand(data, data + len, p, host, sizeof(host));
193         if (status < 0)
194         {
195                 dns_free_data(r);
196                 return NULL;
197         }
198         r->dns_r_q.dns_q_domain = sm_strdup(host);
199         if (r->dns_r_q.dns_q_domain == NULL)
200         {
201                 dns_free_data(r);
202                 return NULL;
203         }
204
205         ans_cnt = ntohs((unsigned short) r->dns_r_h.ancount);
206
207         p += status;
208         GETSHORT(r->dns_r_q.dns_q_type, p);
209         GETSHORT(r->dns_r_q.dns_q_class, p);
210         rr = &r->dns_r_head;
211         ui = 0;
212         while (p < data + len && ui < ans_cnt)
213         {
214                 int type, class, ttl, size, txtlen;
215
216                 status = dn_expand(data, data + len, p, host, sizeof(host));
217                 if (status < 0)
218                 {
219                         dns_free_data(r);
220                         return NULL;
221                 }
222                 ++ui;
223                 p += status;
224                 GETSHORT(type, p);
225                 GETSHORT(class, p);
226                 GETLONG(ttl, p);
227                 GETSHORT(size, p);
228                 if (p + size > data + len)
229                 {
230                         /*
231                         **  announced size of data exceeds length of
232                         **  data paket: someone is cheating.
233                         */
234
235                         if (LogLevel > 5)
236                                 sm_syslog(LOG_WARNING, NOQID,
237                                           "ERROR: DNS RDLENGTH=%d > data len=%d",
238                                           size, len - (p - data));
239                         dns_free_data(r);
240                         return NULL;
241                 }
242                 *rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
243                 if (*rr == NULL)
244                 {
245                         dns_free_data(r);
246                         return NULL;
247                 }
248                 memset(*rr, 0, sizeof(**rr));
249                 (*rr)->rr_domain = sm_strdup(host);
250                 if ((*rr)->rr_domain == NULL)
251                 {
252                         dns_free_data(r);
253                         return NULL;
254                 }
255                 (*rr)->rr_type = type;
256                 (*rr)->rr_class = class;
257                 (*rr)->rr_ttl = ttl;
258                 (*rr)->rr_size = size;
259                 switch (type)
260                 {
261                   case T_NS:
262                   case T_CNAME:
263                   case T_PTR:
264                         status = dn_expand(data, data + len, p, host,
265                                            sizeof(host));
266                         if (status < 0)
267                         {
268                                 dns_free_data(r);
269                                 return NULL;
270                         }
271                         (*rr)->rr_u.rr_txt = sm_strdup(host);
272                         if ((*rr)->rr_u.rr_txt == NULL)
273                         {
274                                 dns_free_data(r);
275                                 return NULL;
276                         }
277                         break;
278
279                   case T_MX:
280                   case T_AFSDB:
281                         status = dn_expand(data, data + len, p + 2, host,
282                                            sizeof(host));
283                         if (status < 0)
284                         {
285                                 dns_free_data(r);
286                                 return NULL;
287                         }
288                         l = strlen(host) + 1;
289                         (*rr)->rr_u.rr_mx = (MX_RECORD_T *)
290                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
291                         if ((*rr)->rr_u.rr_mx == NULL)
292                         {
293                                 dns_free_data(r);
294                                 return NULL;
295                         }
296                         (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
297                         (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
298                                           host, l);
299                         break;
300
301                   case T_SRV:
302                         status = dn_expand(data, data + len, p + 6, host,
303                                            sizeof(host));
304                         if (status < 0)
305                         {
306                                 dns_free_data(r);
307                                 return NULL;
308                         }
309                         l = strlen(host) + 1;
310                         (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
311                                 sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
312                         if ((*rr)->rr_u.rr_srv == NULL)
313                         {
314                                 dns_free_data(r);
315                                 return NULL;
316                         }
317                         (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
318                         (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
319                         (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
320                         (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
321                                           host, l);
322                         break;
323
324                   case T_TXT:
325
326                         /*
327                         **  The TXT record contains the length as
328                         **  leading byte, hence the value is restricted
329                         **  to 255, which is less than the maximum value
330                         **  of RDLENGTH (size). Nevertheless, txtlen
331                         **  must be less than size because the latter
332                         **  specifies the length of the entire TXT
333                         **  record.
334                         */
335
336                         txtlen = *p;
337                         if (txtlen >= size)
338                         {
339                                 if (LogLevel > 5)
340                                         sm_syslog(LOG_WARNING, NOQID,
341                                                   "ERROR: DNS TXT record size=%d <= text len=%d",
342                                                   size, txtlen);
343                                 dns_free_data(r);
344                                 return NULL;
345                         }
346                         (*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
347                         if ((*rr)->rr_u.rr_txt == NULL)
348                         {
349                                 dns_free_data(r);
350                                 return NULL;
351                         }
352                         (void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
353                                           txtlen + 1);
354                         break;
355
356                   default:
357                         (*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
358                         if ((*rr)->rr_u.rr_data == NULL)
359                         {
360                                 dns_free_data(r);
361                                 return NULL;
362                         }
363                         (void) memcpy((*rr)->rr_u.rr_data, p, size);
364                         break;
365                 }
366                 p += size;
367                 rr = &(*rr)->rr_next;
368         }
369         *rr = NULL;
370         return r;
371 }
372
373 /*
374 **  DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
375 **
376 **      Parameters:
377 **              domain -- name to lookup
378 **              rr_class -- resource record class
379 **              rr_type -- resource record type
380 **              retrans -- retransmission timeout
381 **              retry -- number of retries
382 **
383 **      Returns:
384 **              result of lookup if succeeded.
385 **              NULL otherwise.
386 */
387
388 DNS_REPLY_T *
389 dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
390         const char *domain;
391         int rr_class;
392         int rr_type;
393         time_t retrans;
394         int retry;
395 {
396         int len;
397         unsigned long old_options = 0;
398         time_t save_retrans = 0;
399         int save_retry = 0;
400         DNS_REPLY_T *r = NULL;
401         querybuf reply_buf;
402         unsigned char *reply;
403
404 #define SMRBSIZE sizeof(reply_buf)
405 #ifndef IP_MAXPACKET
406 # define IP_MAXPACKET   65535
407 #endif
408
409         if (tTd(8, 16))
410         {
411                 old_options = _res.options;
412                 _res.options |= RES_DEBUG;
413                 sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
414                            rr_class, dns_type_to_string(rr_type));
415         }
416         if (retrans > 0)
417         {
418                 save_retrans = _res.retrans;
419                 _res.retrans = retrans;
420         }
421         if (retry > 0)
422         {
423                 save_retry = _res.retry;
424                 _res.retry = retry;
425         }
426         errno = 0;
427         SM_SET_H_ERRNO(0);
428         reply = (unsigned char *)&reply_buf;
429         len = res_search(domain, rr_class, rr_type, reply, SMRBSIZE);
430         if (len >= SMRBSIZE)
431         {
432                 if (len >= IP_MAXPACKET)
433                 {
434                         if (tTd(8, 4))
435                                 sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response too long\n",
436                                            domain, len, (int) SMRBSIZE,
437                                            IP_MAXPACKET);
438                 }
439                 else
440                 {
441                         if (tTd(8, 6))
442                                 sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response longer than default size, resizing\n",
443                                            domain, len, (int) SMRBSIZE,
444                                            IP_MAXPACKET);
445                         reply = (unsigned char *)sm_malloc(IP_MAXPACKET);
446                         if (reply == NULL)
447                                 SM_SET_H_ERRNO(TRY_AGAIN);
448                         else
449                                 len = res_search(domain, rr_class, rr_type,
450                                                  reply, IP_MAXPACKET);
451                 }
452         }
453         if (tTd(8, 16))
454         {
455                 _res.options = old_options;
456                 sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
457                            domain, rr_class, dns_type_to_string(rr_type), len);
458         }
459         if (len >= 0 && len < IP_MAXPACKET && reply != NULL)
460                 r = parse_dns_reply(reply, len);
461         if (reply != (unsigned char *)&reply_buf && reply != NULL)
462         {
463                 sm_free(reply);
464                 reply = NULL;
465         }
466         if (retrans > 0)
467                 _res.retrans = save_retrans;
468         if (retry > 0)
469                 _res.retry = save_retry;
470         return r;
471 }
472
473 #  if 0
474 DNS_REPLY_T *
475 dns_lookup(domain, type_name, retrans, retry)
476         const char *domain;
477         const char *type_name;
478         time_t retrans;
479         int retry;
480 {
481         int type;
482
483         type = dns_string_to_type(type_name);
484         if (type == -1)
485         {
486                 if (tTd(8, 16))
487                         sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
488                                 type_name);
489                 return NULL;
490         }
491         return dns_lookup_int(domain, C_IN, type, retrans, retry);
492 }
493 #  endif /* 0 */
494 # endif /* NAMED_BIND */
495 #endif /* DNSMAP */