xref: /freebsd/contrib/sendmail/src/sm_resolve.c (revision aa24f48b361effe51163877d84f1b70d32b77e04)
1 /*
2  * Copyright (c) 2000-2004, 2010 Proofpoint, 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.40 2013-11-22 20:51:56 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 - (int)(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 */
496