Home | History | Annotate | Download | only in src
      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 Hgskolan
     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