xref: /titanic_52/usr/src/cmd/sendmail/src/sm_resolve.c (revision d4660949aa62dd6a963f4913b7120b383cf473c4)
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  #pragma ident	"%Z%%M%	%I%	%E% SMI"
45  
46  #include <sendmail.h>
47  #if DNSMAP
48  # if NAMED_BIND
49  #  include "sm_resolve.h"
50  
51  SM_RCSID("$Id: sm_resolve.c,v 8.36 2008/02/11 23:04:16 ca Exp $")
52  
53  static struct stot
54  {
55  	const char	*st_name;
56  	int		st_type;
57  } stot[] =
58  {
59  #  if NETINET
60  	{	"A",		T_A		},
61  #  endif /* NETINET */
62  #  if NETINET6
63  	{	"AAAA",		T_AAAA		},
64  #  endif /* NETINET6 */
65  	{	"NS",		T_NS		},
66  	{	"CNAME",	T_CNAME		},
67  	{	"PTR",		T_PTR		},
68  	{	"MX",		T_MX		},
69  	{	"TXT",		T_TXT		},
70  	{	"AFSDB",	T_AFSDB		},
71  	{	"SRV",		T_SRV		},
72  	{	NULL,		0		}
73  };
74  
75  static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int));
76  
77  /*
78  **  DNS_STRING_TO_TYPE -- convert resource record name into type
79  **
80  **	Parameters:
81  **		name -- name of resource record type
82  **
83  **	Returns:
84  **		type if succeeded.
85  **		-1 otherwise.
86  */
87  
88  int
89  dns_string_to_type(name)
90  	const char *name;
91  {
92  	struct stot *p = stot;
93  
94  	for (p = stot; p->st_name != NULL; p++)
95  		if (sm_strcasecmp(name, p->st_name) == 0)
96  			return p->st_type;
97  	return -1;
98  }
99  
100  /*
101  **  DNS_TYPE_TO_STRING -- convert resource record type into name
102  **
103  **	Parameters:
104  **		type -- resource record type
105  **
106  **	Returns:
107  **		name if succeeded.
108  **		NULL otherwise.
109  */
110  
111  const char *
112  dns_type_to_string(type)
113  	int type;
114  {
115  	struct stot *p = stot;
116  
117  	for (p = stot; p->st_name != NULL; p++)
118  		if (type == p->st_type)
119  			return p->st_name;
120  	return NULL;
121  }
122  
123  /*
124  **  DNS_FREE_DATA -- free all components of a DNS_REPLY_T
125  **
126  **	Parameters:
127  **		r -- pointer to DNS_REPLY_T
128  **
129  **	Returns:
130  **		none.
131  */
132  
133  void
134  dns_free_data(r)
135  	DNS_REPLY_T *r;
136  {
137  	RESOURCE_RECORD_T *rr;
138  
139  	if (r->dns_r_q.dns_q_domain != NULL)
140  		sm_free(r->dns_r_q.dns_q_domain);
141  	for (rr = r->dns_r_head; rr != NULL; )
142  	{
143  		RESOURCE_RECORD_T *tmp = rr;
144  
145  		if (rr->rr_domain != NULL)
146  			sm_free(rr->rr_domain);
147  		if (rr->rr_u.rr_data != NULL)
148  			sm_free(rr->rr_u.rr_data);
149  		rr = rr->rr_next;
150  		sm_free(tmp);
151  	}
152  	sm_free(r);
153  }
154  
155  /*
156  **  PARSE_DNS_REPLY -- parse DNS reply data.
157  **
158  **	Parameters:
159  **		data -- pointer to dns data
160  **		len -- len of data
161  **
162  **	Returns:
163  **		pointer to DNS_REPLY_T if succeeded.
164  **		NULL otherwise.
165  */
166  
167  static DNS_REPLY_T *
168  parse_dns_reply(data, len)
169  	unsigned char *data;
170  	int len;
171  {
172  	unsigned char *p;
173  	unsigned short ans_cnt, ui;
174  	int status;
175  	size_t l;
176  	char host[MAXHOSTNAMELEN];
177  	DNS_REPLY_T *r;
178  	RESOURCE_RECORD_T **rr;
179  
180  	r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
181  	if (r == NULL)
182  		return NULL;
183  	memset(r, 0, sizeof(*r));
184  
185  	p = data;
186  
187  	/* doesn't work on Crays? */
188  	memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
189  	p += sizeof(r->dns_r_h);
190  	status = dn_expand(data, data + len, p, host, sizeof(host));
191  	if (status < 0)
192  	{
193  		dns_free_data(r);
194  		return NULL;
195  	}
196  	r->dns_r_q.dns_q_domain = sm_strdup(host);
197  	if (r->dns_r_q.dns_q_domain == NULL)
198  	{
199  		dns_free_data(r);
200  		return NULL;
201  	}
202  
203  	ans_cnt = ntohs((unsigned short) r->dns_r_h.ancount);
204  
205  	p += status;
206  	GETSHORT(r->dns_r_q.dns_q_type, p);
207  	GETSHORT(r->dns_r_q.dns_q_class, p);
208  	rr = &r->dns_r_head;
209  	ui = 0;
210  	while (p < data + len && ui < ans_cnt)
211  	{
212  		int type, class, ttl, size, txtlen;
213  
214  		status = dn_expand(data, data + len, p, host, sizeof(host));
215  		if (status < 0)
216  		{
217  			dns_free_data(r);
218  			return NULL;
219  		}
220  		++ui;
221  		p += status;
222  		GETSHORT(type, p);
223  		GETSHORT(class, p);
224  		GETLONG(ttl, p);
225  		GETSHORT(size, p);
226  		if (p + size > data + len)
227  		{
228  			/*
229  			**  announced size of data exceeds length of
230  			**  data paket: someone is cheating.
231  			*/
232  
233  			if (LogLevel > 5)
234  				sm_syslog(LOG_WARNING, NOQID,
235  					  "ERROR: DNS RDLENGTH=%d > data len=%d",
236  					  size, len - (p - data));
237  			dns_free_data(r);
238  			return NULL;
239  		}
240  		*rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
241  		if (*rr == NULL)
242  		{
243  			dns_free_data(r);
244  			return NULL;
245  		}
246  		memset(*rr, 0, sizeof(**rr));
247  		(*rr)->rr_domain = sm_strdup(host);
248  		if ((*rr)->rr_domain == NULL)
249  		{
250  			dns_free_data(r);
251  			return NULL;
252  		}
253  		(*rr)->rr_type = type;
254  		(*rr)->rr_class = class;
255  		(*rr)->rr_ttl = ttl;
256  		(*rr)->rr_size = size;
257  		switch (type)
258  		{
259  		  case T_NS:
260  		  case T_CNAME:
261  		  case T_PTR:
262  			status = dn_expand(data, data + len, p, host,
263  					   sizeof(host));
264  			if (status < 0)
265  			{
266  				dns_free_data(r);
267  				return NULL;
268  			}
269  			(*rr)->rr_u.rr_txt = sm_strdup(host);
270  			if ((*rr)->rr_u.rr_txt == NULL)
271  			{
272  				dns_free_data(r);
273  				return NULL;
274  			}
275  			break;
276  
277  		  case T_MX:
278  		  case T_AFSDB:
279  			status = dn_expand(data, data + len, p + 2, host,
280  					   sizeof(host));
281  			if (status < 0)
282  			{
283  				dns_free_data(r);
284  				return NULL;
285  			}
286  			l = strlen(host) + 1;
287  			(*rr)->rr_u.rr_mx = (MX_RECORD_T *)
288  				sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
289  			if ((*rr)->rr_u.rr_mx == NULL)
290  			{
291  				dns_free_data(r);
292  				return NULL;
293  			}
294  			(*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
295  			(void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
296  					  host, l);
297  			break;
298  
299  		  case T_SRV:
300  			status = dn_expand(data, data + len, p + 6, host,
301  					   sizeof(host));
302  			if (status < 0)
303  			{
304  				dns_free_data(r);
305  				return NULL;
306  			}
307  			l = strlen(host) + 1;
308  			(*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
309  				sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
310  			if ((*rr)->rr_u.rr_srv == NULL)
311  			{
312  				dns_free_data(r);
313  				return NULL;
314  			}
315  			(*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
316  			(*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
317  			(*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
318  			(void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
319  					  host, l);
320  			break;
321  
322  		  case T_TXT:
323  
324  			/*
325  			**  The TXT record contains the length as
326  			**  leading byte, hence the value is restricted
327  			**  to 255, which is less than the maximum value
328  			**  of RDLENGTH (size). Nevertheless, txtlen
329  			**  must be less than size because the latter
330  			**  specifies the length of the entire TXT
331  			**  record.
332  			*/
333  
334  			txtlen = *p;
335  			if (txtlen >= size)
336  			{
337  				if (LogLevel > 5)
338  					sm_syslog(LOG_WARNING, NOQID,
339  						  "ERROR: DNS TXT record size=%d <= text len=%d",
340  						  size, txtlen);
341  				dns_free_data(r);
342  				return NULL;
343  			}
344  			(*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
345  			if ((*rr)->rr_u.rr_txt == NULL)
346  			{
347  				dns_free_data(r);
348  				return NULL;
349  			}
350  			(void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
351  					  txtlen + 1);
352  			break;
353  
354  		  default:
355  			(*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
356  			if ((*rr)->rr_u.rr_data == NULL)
357  			{
358  				dns_free_data(r);
359  				return NULL;
360  			}
361  			(void) memcpy((*rr)->rr_u.rr_data, p, size);
362  			break;
363  		}
364  		p += size;
365  		rr = &(*rr)->rr_next;
366  	}
367  	*rr = NULL;
368  	return r;
369  }
370  
371  /*
372  **  DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
373  **
374  **	Parameters:
375  **		domain -- name to lookup
376  **		rr_class -- resource record class
377  **		rr_type -- resource record type
378  **		retrans -- retransmission timeout
379  **		retry -- number of retries
380  **
381  **	Returns:
382  **		result of lookup if succeeded.
383  **		NULL otherwise.
384  */
385  
386  DNS_REPLY_T *
387  dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
388  	const char *domain;
389  	int rr_class;
390  	int rr_type;
391  	time_t retrans;
392  	int retry;
393  {
394  	int len;
395  	unsigned long old_options = 0;
396  	time_t save_retrans = 0;
397  	int save_retry = 0;
398  	DNS_REPLY_T *r = NULL;
399  	unsigned char reply[1024];
400  
401  	if (tTd(8, 16))
402  	{
403  		old_options = _res.options;
404  		_res.options |= RES_DEBUG;
405  		sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
406  			   rr_class, dns_type_to_string(rr_type));
407  	}
408  	if (retrans > 0)
409  	{
410  		save_retrans = _res.retrans;
411  		_res.retrans = retrans;
412  	}
413  	if (retry > 0)
414  	{
415  		save_retry = _res.retry;
416  		_res.retry = retry;
417  	}
418  	errno = 0;
419  	SM_SET_H_ERRNO(0);
420  	len = res_search(domain, rr_class, rr_type, reply, sizeof(reply));
421  	if (tTd(8, 16))
422  	{
423  		_res.options = old_options;
424  		sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
425  			   domain, rr_class, dns_type_to_string(rr_type), len);
426  	}
427  	if (len >= 0)
428  		r = parse_dns_reply(reply, len);
429  	if (retrans > 0)
430  		_res.retrans = save_retrans;
431  	if (retry > 0)
432  		_res.retry = save_retry;
433  	return r;
434  }
435  
436  #  if 0
437  DNS_REPLY_T *
438  dns_lookup(domain, type_name, retrans, retry)
439  	const char *domain;
440  	const char *type_name;
441  	time_t retrans;
442  	int retry;
443  {
444  	int type;
445  
446  	type = dns_string_to_type(type_name);
447  	if (type == -1)
448  	{
449  		if (tTd(8, 16))
450  			sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
451  				type_name);
452  		return NULL;
453  	}
454  	return dns_lookup_int(domain, C_IN, type, retrans, retry);
455  }
456  #  endif /* 0 */
457  # endif /* NAMED_BIND */
458  #endif /* DNSMAP */
459