xref: /freebsd/contrib/sendmail/src/sm_resolve.c (revision b2db760808f74bb53c232900091c9da801ebbfcc)
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 */
457