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