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