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