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