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
53SM_RCSID("$Id: sm_resolve.c,v 8.40 2013-11-22 20:51:56 ca Exp $")
54
55static 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
77static 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
90int
91dns_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
113const char *
114dns_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
135void
136dns_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
169static DNS_REPLY_T *
170parse_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 - (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
388DNS_REPLY_T *
389dns_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
474DNS_REPLY_T *
475dns_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