xref: /freebsd/contrib/sendmail/src/sm_resolve.c (revision 2b743a9e9ddc6736208dc8ca1ce06ce64ad20a19)
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.33 2004/08/04 21:17:57 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 	int status;
172 	size_t l;
173 	char host[MAXHOSTNAMELEN];
174 	DNS_REPLY_T *r;
175 	RESOURCE_RECORD_T **rr;
176 
177 	r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
178 	if (r == NULL)
179 		return NULL;
180 	memset(r, 0, sizeof(*r));
181 
182 	p = data;
183 
184 	/* doesn't work on Crays? */
185 	memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
186 	p += sizeof(r->dns_r_h);
187 	status = dn_expand(data, data + len, p, host, sizeof host);
188 	if (status < 0)
189 	{
190 		dns_free_data(r);
191 		return NULL;
192 	}
193 	r->dns_r_q.dns_q_domain = sm_strdup(host);
194 	if (r->dns_r_q.dns_q_domain == NULL)
195 	{
196 		dns_free_data(r);
197 		return NULL;
198 	}
199 	p += status;
200 	GETSHORT(r->dns_r_q.dns_q_type, p);
201 	GETSHORT(r->dns_r_q.dns_q_class, p);
202 	rr = &r->dns_r_head;
203 	while (p < data + len)
204 	{
205 		int type, class, ttl, size, txtlen;
206 
207 		status = dn_expand(data, data + len, p, host, sizeof host);
208 		if (status < 0)
209 		{
210 			dns_free_data(r);
211 			return NULL;
212 		}
213 		p += status;
214 		GETSHORT(type, p);
215 		GETSHORT(class, p);
216 		GETLONG(ttl, p);
217 		GETSHORT(size, p);
218 		if (p + size > data + len)
219 		{
220 			/*
221 			**  announced size of data exceeds length of
222 			**  data paket: someone is cheating.
223 			*/
224 
225 			if (LogLevel > 5)
226 				sm_syslog(LOG_WARNING, NOQID,
227 					  "ERROR: DNS RDLENGTH=%d > data len=%d",
228 					  size, len - (p - data));
229 			dns_free_data(r);
230 			return NULL;
231 		}
232 		*rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
233 		if (*rr == NULL)
234 		{
235 			dns_free_data(r);
236 			return NULL;
237 		}
238 		memset(*rr, 0, sizeof(**rr));
239 		(*rr)->rr_domain = sm_strdup(host);
240 		if ((*rr)->rr_domain == NULL)
241 		{
242 			dns_free_data(r);
243 			return NULL;
244 		}
245 		(*rr)->rr_type = type;
246 		(*rr)->rr_class = class;
247 		(*rr)->rr_ttl = ttl;
248 		(*rr)->rr_size = size;
249 		switch (type)
250 		{
251 		  case T_NS:
252 		  case T_CNAME:
253 		  case T_PTR:
254 			status = dn_expand(data, data + len, p, host,
255 					   sizeof host);
256 			if (status < 0)
257 			{
258 				dns_free_data(r);
259 				return NULL;
260 			}
261 			(*rr)->rr_u.rr_txt = sm_strdup(host);
262 			if ((*rr)->rr_u.rr_txt == NULL)
263 			{
264 				dns_free_data(r);
265 				return NULL;
266 			}
267 			break;
268 
269 		  case T_MX:
270 		  case T_AFSDB:
271 			status = dn_expand(data, data + len, p + 2, host,
272 					   sizeof host);
273 			if (status < 0)
274 			{
275 				dns_free_data(r);
276 				return NULL;
277 			}
278 			l = strlen(host) + 1;
279 			(*rr)->rr_u.rr_mx = (MX_RECORD_T *)
280 				sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
281 			if ((*rr)->rr_u.rr_mx == NULL)
282 			{
283 				dns_free_data(r);
284 				return NULL;
285 			}
286 			(*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
287 			(void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
288 					  host, l);
289 			break;
290 
291 		  case T_SRV:
292 			status = dn_expand(data, data + len, p + 6, host,
293 					   sizeof host);
294 			if (status < 0)
295 			{
296 				dns_free_data(r);
297 				return NULL;
298 			}
299 			l = strlen(host) + 1;
300 			(*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
301 				sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
302 			if ((*rr)->rr_u.rr_srv == NULL)
303 			{
304 				dns_free_data(r);
305 				return NULL;
306 			}
307 			(*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
308 			(*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
309 			(*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
310 			(void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
311 					  host, l);
312 			break;
313 
314 		  case T_TXT:
315 
316 			/*
317 			**  The TXT record contains the length as
318 			**  leading byte, hence the value is restricted
319 			**  to 255, which is less than the maximum value
320 			**  of RDLENGTH (size). Nevertheless, txtlen
321 			**  must be less than size because the latter
322 			**  specifies the length of the entire TXT
323 			**  record.
324 			*/
325 
326 			txtlen = *p;
327 			if (txtlen >= size)
328 			{
329 				if (LogLevel > 5)
330 					sm_syslog(LOG_WARNING, NOQID,
331 						  "ERROR: DNS TXT record size=%d <= text len=%d",
332 						  size, txtlen);
333 				dns_free_data(r);
334 				return NULL;
335 			}
336 			(*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
337 			if ((*rr)->rr_u.rr_txt == NULL)
338 			{
339 				dns_free_data(r);
340 				return NULL;
341 			}
342 			(void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
343 					  txtlen + 1);
344 			break;
345 
346 		  default:
347 			(*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
348 			if ((*rr)->rr_u.rr_data == NULL)
349 			{
350 				dns_free_data(r);
351 				return NULL;
352 			}
353 			(void) memcpy((*rr)->rr_u.rr_data, p, size);
354 			break;
355 		}
356 		p += size;
357 		rr = &(*rr)->rr_next;
358 	}
359 	*rr = NULL;
360 	return r;
361 }
362 
363 /*
364 **  DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
365 **
366 **	Parameters:
367 **		domain -- name to lookup
368 **		rr_class -- resource record class
369 **		rr_type -- resource record type
370 **		retrans -- retransmission timeout
371 **		retry -- number of retries
372 **
373 **	Returns:
374 **		result of lookup if succeeded.
375 **		NULL otherwise.
376 */
377 
378 DNS_REPLY_T *
379 dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
380 	const char *domain;
381 	int rr_class;
382 	int rr_type;
383 	time_t retrans;
384 	int retry;
385 {
386 	int len;
387 	unsigned long old_options = 0;
388 	time_t save_retrans = 0;
389 	int save_retry = 0;
390 	DNS_REPLY_T *r = NULL;
391 	unsigned char reply[1024];
392 
393 	if (tTd(8, 16))
394 	{
395 		old_options = _res.options;
396 		_res.options |= RES_DEBUG;
397 		sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
398 			   rr_class, dns_type_to_string(rr_type));
399 	}
400 	if (retrans > 0)
401 	{
402 		save_retrans = _res.retrans;
403 		_res.retrans = retrans;
404 	}
405 	if (retry > 0)
406 	{
407 		save_retry = _res.retry;
408 		_res.retry = retry;
409 	}
410 	errno = 0;
411 	SM_SET_H_ERRNO(0);
412 	len = res_search(domain, rr_class, rr_type, reply, sizeof reply);
413 	if (tTd(8, 16))
414 	{
415 		_res.options = old_options;
416 		sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
417 			   domain, rr_class, dns_type_to_string(rr_type), len);
418 	}
419 	if (len >= 0)
420 		r = parse_dns_reply(reply, len);
421 	if (retrans > 0)
422 		_res.retrans = save_retrans;
423 	if (retry > 0)
424 		_res.retry = save_retry;
425 	return r;
426 }
427 
428 #  if 0
429 DNS_REPLY_T *
430 dns_lookup(domain, type_name, retrans, retry)
431 	const char *domain;
432 	const char *type_name;
433 	time_t retrans;
434 	int retry;
435 {
436 	int type;
437 
438 	type = dns_string_to_type(type_name);
439 	if (type == -1)
440 	{
441 		if (tTd(8, 16))
442 			sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
443 				type_name);
444 		return NULL;
445 	}
446 	return dns_lookup_int(domain, C_IN, type, retrans, retry);
447 }
448 #  endif /* 0 */
449 # endif /* NAMED_BIND */
450 #endif /* DNSMAP */
451