1 /* 2 * Copyright (c) 1986 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #include "sendmail.h" 10 11 #ifndef lint 12 #ifdef NAMED_BIND 13 static char sccsid[] = "@(#)domain.c 5.37 (Berkeley) 12/18/92 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 5.37 (Berkeley) 12/18/92 (without name server)"; 16 #endif 17 #endif /* not lint */ 18 19 #ifdef NAMED_BIND 20 21 #include <sys/param.h> 22 #include <errno.h> 23 #include <arpa/nameser.h> 24 #include <resolv.h> 25 #include <netdb.h> 26 27 typedef union { 28 HEADER qb1; 29 char qb2[PACKETSZ]; 30 } querybuf; 31 32 static char hostbuf[MAXMXHOSTS*PACKETSZ]; 33 34 getmxrr(host, mxhosts, localhost, rcode) 35 char *host, **mxhosts, *localhost; 36 int *rcode; 37 { 38 extern int h_errno; 39 register u_char *eom, *cp; 40 register int i, j, n, nmx; 41 register char *bp; 42 HEADER *hp; 43 querybuf answer; 44 int ancount, qdcount, buflen, seenlocal; 45 u_short pref, localpref, type, prefer[MAXMXHOSTS]; 46 47 errno = 0; 48 n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); 49 if (n < 0) 50 { 51 if (tTd(8, 1)) 52 printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 53 (host == NULL) ? "<NULL>" : host, errno, h_errno); 54 switch (h_errno) 55 { 56 case NO_DATA: 57 case NO_RECOVERY: 58 /* no MX data on this host */ 59 goto punt; 60 61 case HOST_NOT_FOUND: 62 /* the host just doesn't exist */ 63 *rcode = EX_NOHOST; 64 break; 65 66 case TRY_AGAIN: 67 /* couldn't connect to the name server */ 68 if (!UseNameServer && errno == ECONNREFUSED) 69 goto punt; 70 71 /* it might come up later; better queue it up */ 72 *rcode = EX_TEMPFAIL; 73 break; 74 } 75 76 /* irreconcilable differences */ 77 return (-1); 78 } 79 80 /* find first satisfactory answer */ 81 hp = (HEADER *)&answer; 82 cp = (u_char *)&answer + sizeof(HEADER); 83 eom = (u_char *)&answer + n; 84 for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 85 if ((n = dn_skipname(cp, eom)) < 0) 86 goto punt; 87 nmx = 0; 88 seenlocal = 0; 89 buflen = sizeof(hostbuf) - 1; 90 bp = hostbuf; 91 ancount = ntohs(hp->ancount); 92 while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) 93 { 94 if ((n = dn_expand((u_char *)&answer, 95 eom, cp, (u_char *)bp, buflen)) < 0) 96 break; 97 cp += n; 98 GETSHORT(type, cp); 99 cp += sizeof(u_short) + sizeof(u_long); 100 GETSHORT(n, cp); 101 if (type != T_MX) 102 { 103 if (tTd(8, 1) || _res.options & RES_DEBUG) 104 printf("unexpected answer type %d, size %d\n", 105 type, n); 106 cp += n; 107 continue; 108 } 109 GETSHORT(pref, cp); 110 if ((n = dn_expand((u_char *)&answer, eom, cp, 111 (u_char *)bp, buflen)) < 0) 112 break; 113 cp += n; 114 if (!strcasecmp(bp, localhost)) 115 { 116 if (seenlocal == 0 || pref < localpref) 117 localpref = pref; 118 seenlocal = 1; 119 continue; 120 } 121 prefer[nmx] = pref; 122 mxhosts[nmx++] = bp; 123 n = strlen(bp); 124 bp += n; 125 if (bp[-1] != '.') 126 { 127 *bp++ = '.'; 128 n++; 129 } 130 *bp++ = '\0'; 131 buflen -= n + 1; 132 } 133 if (nmx == 0) { 134 punt: mxhosts[0] = strcpy(hostbuf, host); 135 return(1); 136 } 137 138 /* sort the records */ 139 for (i = 0; i < nmx; i++) { 140 for (j = i + 1; j < nmx; j++) { 141 if (prefer[i] > prefer[j] || 142 (prefer[i] == prefer[j] && (rand() & 0100) == 0)) { 143 register int temp; 144 register char *temp1; 145 146 temp = prefer[i]; 147 prefer[i] = prefer[j]; 148 prefer[j] = temp; 149 temp1 = mxhosts[i]; 150 mxhosts[i] = mxhosts[j]; 151 mxhosts[j] = temp1; 152 } 153 } 154 if (seenlocal && prefer[i] >= localpref) { 155 /* 156 * truncate higher pref part of list; if we're 157 * the best choice left, we should have realized 158 * awhile ago that this was a local delivery. 159 */ 160 if (i == 0) { 161 *rcode = EX_CONFIG; 162 return(-1); 163 } 164 nmx = i; 165 break; 166 } 167 } 168 return(nmx); 169 } 170 /* 171 ** GETCANONNAME -- get the canonical name for named host 172 ** 173 ** Parameters: 174 ** host -- a buffer containing the name of the host. 175 ** This is a value-result parameter. 176 ** hbsize -- the size of the host buffer. 177 ** 178 ** Returns: 179 ** TRUE -- if the host matched. 180 ** FALSE -- otherwise. 181 ** 182 ** Notes: 183 ** Use query type of ANY if possible (NoWildcardMX), which 184 ** will find types CNAME, A, and MX, and will cause all 185 ** existing records to be cached by our local server. If 186 ** there is (might be) a wildcard MX record in the local 187 ** domain or its parents that are searched, we can't use 188 ** ANY; it would cause fully-qualified names to match as 189 ** names in a local domain. 190 */ 191 192 bool 193 getcanonname(host, hbsize) 194 char *host; 195 int hbsize; 196 { 197 extern int h_errno; 198 register u_char *eom, *ap; 199 register char *cp; 200 register int n; 201 HEADER *hp; 202 querybuf answer; 203 int first, ancount, qdcount, loopcnt; 204 int ret; 205 int qtype = NoWildcardMX ? T_ANY : T_CNAME; 206 char **domain; 207 bool rval; 208 int type; 209 char nbuf[PACKETSZ]; 210 211 if (tTd(8, 2)) 212 printf("getcanonname(%s)\n", host); 213 214 if ((_res.options & RES_INIT) == 0 && res_init() == -1) 215 return (FALSE); 216 217 loopcnt = 0; 218 rval = FALSE; 219 loop: 220 for (cp = host, n = 0; *cp; cp++) 221 if (*cp == '.') 222 n++; 223 if (n > 0 && *--cp == '.') 224 { 225 cp = host; 226 n = -1; 227 } 228 229 /* 230 ** If there is at least one dot, start by searching the 231 ** unmodified name. This lets us get "vse.CS" in Czechoslovakia 232 ** instead of CS.Berkeley.EDU. 233 */ 234 235 ret = -1; 236 if (n >= 1) 237 { 238 /* 239 ** Try the unmodified name. 240 */ 241 242 if (tTd(8, 5)) 243 printf("getcanonname: trying %s\n", host); 244 ret = res_query(host, C_IN, qtype, &answer, sizeof(answer)); 245 if (ret > 0) 246 { 247 cp = host; 248 if (tTd(8, 8)) 249 printf("\tYES\n"); 250 } 251 else 252 { 253 if (tTd(8, 8)) 254 printf("\tNO: h_errno=%d\n", h_errno); 255 if (errno == ECONNREFUSED) 256 { 257 /* no server -- try again later */ 258 h_errno = TRY_AGAIN; 259 return FALSE; 260 } 261 } 262 } 263 264 /* 265 ** We do at least one level of search if 266 ** - there is no dot and RES_DEFNAME is set, or 267 ** - there is at least one dot, there is no trailing dot, 268 ** and RES_DNSRCH is set. 269 */ 270 271 if (ret < 0 && 272 ((n == 0 && _res.options & RES_DEFNAMES) || 273 (n > 0 && *--cp != '.' && _res.options & RES_DNSRCH))) 274 { 275 for (domain = _res.dnsrch; *domain; domain++) 276 { 277 (void) sprintf(nbuf, "%.*s.%.*s", 278 MAXDNAME, host, MAXDNAME, *domain); 279 if (tTd(8, 5)) 280 printf("getcanonname: trying %s\n", nbuf); 281 ret = res_query(nbuf, C_IN, qtype, &answer, sizeof(answer)); 282 if (ret > 0) 283 { 284 if (tTd(8, 8)) 285 printf("\tYES\n"); 286 cp = nbuf; 287 break; 288 } 289 else if (tTd(8, 8)) 290 printf("\tNO: h_errno=%d\n", h_errno); 291 292 /* 293 * If no server present, give up. 294 * If name isn't found in this domain, 295 * keep trying higher domains in the search list 296 * (if that's enabled). 297 * On a NO_DATA error, keep trying, otherwise 298 * a wildcard entry of another type could keep us 299 * from finding this entry higher in the domain. 300 * If we get some other error (negative answer or 301 * server failure), then stop searching up, 302 * but try the input name below in case it's 303 * fully-qualified. 304 */ 305 306 if (errno == ECONNREFUSED) 307 { 308 h_errno = TRY_AGAIN; 309 return FALSE; 310 } 311 if (h_errno == NO_DATA) 312 { 313 ret = 0; 314 cp = nbuf; 315 break; 316 } 317 if ((h_errno != HOST_NOT_FOUND) || 318 (_res.options & RES_DNSRCH) == 0) 319 return FALSE; 320 } 321 } 322 if (ret < 0 && n <= 0) 323 { 324 /* 325 ** Try the unmodified name. 326 */ 327 328 cp = host; 329 if (tTd(8, 5)) 330 printf("getcanonname: trying %s\n", cp); 331 ret = res_query(cp, C_IN, qtype, &answer, sizeof(answer)); 332 if (ret > 0) 333 { 334 if (tTd(8, 8)) 335 printf("\tYES\n"); 336 } 337 else 338 { 339 if (tTd(8, 8)) 340 printf("\tNO: h_errno=%d\n", h_errno); 341 } 342 } 343 344 if (ret <= 0 && h_errno != NO_DATA) 345 return FALSE; 346 347 /* find first satisfactory answer */ 348 hp = (HEADER *)&answer; 349 ancount = ntohs(hp->ancount); 350 if (tTd(8, 3)) 351 printf("rcode = %d, ancount=%d, qdcount=%d\n", 352 hp->rcode, ancount, ntohs(hp->qdcount)); 353 354 /* we don't care about errors here, only if we got an answer */ 355 if (ancount == 0) 356 { 357 strncpy(host, cp, hbsize); 358 host[hbsize - 1] = '\0'; 359 return (TRUE); 360 } 361 ap = (u_char *)&answer + sizeof(HEADER); 362 eom = (u_char *)&answer + ret; 363 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 364 { 365 if ((ret = dn_skipname(ap, eom)) < 0) 366 { 367 if (tTd(8, 20)) 368 printf("qdcount failure (%d)\n", 369 ntohs(hp->qdcount)); 370 return FALSE; /* ???XXX??? */ 371 } 372 } 373 374 /* 375 * just in case someone puts a CNAME record after another record, 376 * check all records for CNAME; otherwise, just take the first 377 * name found. 378 */ 379 for (first = 1; --ancount >= 0 && ap < eom; ap += ret) 380 { 381 if ((ret = dn_expand((u_char *)&answer, 382 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 383 break; 384 if (first) { /* XXX */ 385 (void)strncpy(host, nbuf, hbsize); 386 host[hbsize - 1] = '\0'; 387 first = 0; 388 rval = TRUE; 389 } 390 ap += ret; 391 GETSHORT(type, ap); 392 ap += sizeof(u_short) + sizeof(u_long); 393 GETSHORT(ret, ap); 394 if (type == T_CNAME) { 395 /* 396 * assume that only one cname will be found. More 397 * than one is undefined. Copy so that if dn_expand 398 * fails, `host' is still okay. 399 */ 400 if ((ret = dn_expand((u_char *)&answer, 401 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 402 break; 403 (void)strncpy(host, nbuf, hbsize); /* XXX */ 404 host[hbsize - 1] = '\0'; 405 if (++loopcnt > 8) /* never be more than 1 */ 406 return FALSE; 407 rval = TRUE; 408 goto loop; 409 } 410 } 411 return rval; /* ???XXX??? */ 412 } 413 414 #else /* not NAMED_BIND */ 415 416 #include <netdb.h> 417 418 bool 419 getcanonname(host, hbsize) 420 char *host; 421 int hbsize; 422 { 423 struct hostent *hp; 424 425 hp = gethostbyname(host); 426 if (hp == NULL) 427 return (FALSE); 428 429 if (strlen(hp->h_name) >= hbsize) 430 return (FALSE); 431 432 (void) strcpy(host, hp->h_name); 433 return (TRUE); 434 } 435 436 #endif /* not NAMED_BIND */ 437