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