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.33 2004/08/04 21:17:57 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 int status; 172 size_t l; 173 char host[MAXHOSTNAMELEN]; 174 DNS_REPLY_T *r; 175 RESOURCE_RECORD_T **rr; 176 177 r = (DNS_REPLY_T *) sm_malloc(sizeof(*r)); 178 if (r == NULL) 179 return NULL; 180 memset(r, 0, sizeof(*r)); 181 182 p = data; 183 184 /* doesn't work on Crays? */ 185 memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h)); 186 p += sizeof(r->dns_r_h); 187 status = dn_expand(data, data + len, p, host, sizeof host); 188 if (status < 0) 189 { 190 dns_free_data(r); 191 return NULL; 192 } 193 r->dns_r_q.dns_q_domain = sm_strdup(host); 194 if (r->dns_r_q.dns_q_domain == NULL) 195 { 196 dns_free_data(r); 197 return NULL; 198 } 199 p += status; 200 GETSHORT(r->dns_r_q.dns_q_type, p); 201 GETSHORT(r->dns_r_q.dns_q_class, p); 202 rr = &r->dns_r_head; 203 while (p < data + len) 204 { 205 int type, class, ttl, size, txtlen; 206 207 status = dn_expand(data, data + len, p, host, sizeof host); 208 if (status < 0) 209 { 210 dns_free_data(r); 211 return NULL; 212 } 213 p += status; 214 GETSHORT(type, p); 215 GETSHORT(class, p); 216 GETLONG(ttl, p); 217 GETSHORT(size, p); 218 if (p + size > data + len) 219 { 220 /* 221 ** announced size of data exceeds length of 222 ** data paket: someone is cheating. 223 */ 224 225 if (LogLevel > 5) 226 sm_syslog(LOG_WARNING, NOQID, 227 "ERROR: DNS RDLENGTH=%d > data len=%d", 228 size, len - (p - data)); 229 dns_free_data(r); 230 return NULL; 231 } 232 *rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr)); 233 if (*rr == NULL) 234 { 235 dns_free_data(r); 236 return NULL; 237 } 238 memset(*rr, 0, sizeof(**rr)); 239 (*rr)->rr_domain = sm_strdup(host); 240 if ((*rr)->rr_domain == NULL) 241 { 242 dns_free_data(r); 243 return NULL; 244 } 245 (*rr)->rr_type = type; 246 (*rr)->rr_class = class; 247 (*rr)->rr_ttl = ttl; 248 (*rr)->rr_size = size; 249 switch (type) 250 { 251 case T_NS: 252 case T_CNAME: 253 case T_PTR: 254 status = dn_expand(data, data + len, p, host, 255 sizeof host); 256 if (status < 0) 257 { 258 dns_free_data(r); 259 return NULL; 260 } 261 (*rr)->rr_u.rr_txt = sm_strdup(host); 262 if ((*rr)->rr_u.rr_txt == NULL) 263 { 264 dns_free_data(r); 265 return NULL; 266 } 267 break; 268 269 case T_MX: 270 case T_AFSDB: 271 status = dn_expand(data, data + len, p + 2, host, 272 sizeof host); 273 if (status < 0) 274 { 275 dns_free_data(r); 276 return NULL; 277 } 278 l = strlen(host) + 1; 279 (*rr)->rr_u.rr_mx = (MX_RECORD_T *) 280 sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l); 281 if ((*rr)->rr_u.rr_mx == NULL) 282 { 283 dns_free_data(r); 284 return NULL; 285 } 286 (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1]; 287 (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain, 288 host, l); 289 break; 290 291 case T_SRV: 292 status = dn_expand(data, data + len, p + 6, host, 293 sizeof host); 294 if (status < 0) 295 { 296 dns_free_data(r); 297 return NULL; 298 } 299 l = strlen(host) + 1; 300 (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*) 301 sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l); 302 if ((*rr)->rr_u.rr_srv == NULL) 303 { 304 dns_free_data(r); 305 return NULL; 306 } 307 (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1]; 308 (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3]; 309 (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5]; 310 (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target, 311 host, l); 312 break; 313 314 case T_TXT: 315 316 /* 317 ** The TXT record contains the length as 318 ** leading byte, hence the value is restricted 319 ** to 255, which is less than the maximum value 320 ** of RDLENGTH (size). Nevertheless, txtlen 321 ** must be less than size because the latter 322 ** specifies the length of the entire TXT 323 ** record. 324 */ 325 326 txtlen = *p; 327 if (txtlen >= size) 328 { 329 if (LogLevel > 5) 330 sm_syslog(LOG_WARNING, NOQID, 331 "ERROR: DNS TXT record size=%d <= text len=%d", 332 size, txtlen); 333 dns_free_data(r); 334 return NULL; 335 } 336 (*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1); 337 if ((*rr)->rr_u.rr_txt == NULL) 338 { 339 dns_free_data(r); 340 return NULL; 341 } 342 (void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1, 343 txtlen + 1); 344 break; 345 346 default: 347 (*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size); 348 if ((*rr)->rr_u.rr_data == NULL) 349 { 350 dns_free_data(r); 351 return NULL; 352 } 353 (void) memcpy((*rr)->rr_u.rr_data, p, size); 354 break; 355 } 356 p += size; 357 rr = &(*rr)->rr_next; 358 } 359 *rr = NULL; 360 return r; 361 } 362 363 /* 364 ** DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine) 365 ** 366 ** Parameters: 367 ** domain -- name to lookup 368 ** rr_class -- resource record class 369 ** rr_type -- resource record type 370 ** retrans -- retransmission timeout 371 ** retry -- number of retries 372 ** 373 ** Returns: 374 ** result of lookup if succeeded. 375 ** NULL otherwise. 376 */ 377 378 DNS_REPLY_T * 379 dns_lookup_int(domain, rr_class, rr_type, retrans, retry) 380 const char *domain; 381 int rr_class; 382 int rr_type; 383 time_t retrans; 384 int retry; 385 { 386 int len; 387 unsigned long old_options = 0; 388 time_t save_retrans = 0; 389 int save_retry = 0; 390 DNS_REPLY_T *r = NULL; 391 unsigned char reply[1024]; 392 393 if (tTd(8, 16)) 394 { 395 old_options = _res.options; 396 _res.options |= RES_DEBUG; 397 sm_dprintf("dns_lookup(%s, %d, %s)\n", domain, 398 rr_class, dns_type_to_string(rr_type)); 399 } 400 if (retrans > 0) 401 { 402 save_retrans = _res.retrans; 403 _res.retrans = retrans; 404 } 405 if (retry > 0) 406 { 407 save_retry = _res.retry; 408 _res.retry = retry; 409 } 410 errno = 0; 411 SM_SET_H_ERRNO(0); 412 len = res_search(domain, rr_class, rr_type, reply, sizeof reply); 413 if (tTd(8, 16)) 414 { 415 _res.options = old_options; 416 sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n", 417 domain, rr_class, dns_type_to_string(rr_type), len); 418 } 419 if (len >= 0) 420 r = parse_dns_reply(reply, len); 421 if (retrans > 0) 422 _res.retrans = save_retrans; 423 if (retry > 0) 424 _res.retry = save_retry; 425 return r; 426 } 427 428 # if 0 429 DNS_REPLY_T * 430 dns_lookup(domain, type_name, retrans, retry) 431 const char *domain; 432 const char *type_name; 433 time_t retrans; 434 int retry; 435 { 436 int type; 437 438 type = dns_string_to_type(type_name); 439 if (type == -1) 440 { 441 if (tTd(8, 16)) 442 sm_dprintf("dns_lookup: unknown resource type: `%s'\n", 443 type_name); 444 return NULL; 445 } 446 return dns_lookup_int(domain, C_IN, type, retrans, retry); 447 } 448 # endif /* 0 */ 449 # endif /* NAMED_BIND */ 450 #endif /* DNSMAP */ 451