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