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 6.6 (Berkeley) 02/20/93 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 6.6 (Berkeley) 02/20/93 (without name server)"; 16 #endif 17 #endif /* not lint */ 18 19 #ifdef NAMED_BIND 20 21 #include <errno.h> 22 #include <arpa/nameser.h> 23 #include <resolv.h> 24 #include <netdb.h> 25 26 typedef union 27 { 28 HEADER qb1; 29 char qb2[PACKETSZ]; 30 } querybuf; 31 32 static char hostbuf[MAXMXHOSTS*PACKETSZ]; 33 34 #ifndef MAXDNSRCH 35 #define MAXDNSRCH 6 /* number of possible domains to search */ 36 #endif 37 38 #ifndef MAX 39 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 40 #endif 41 42 getmxrr(host, mxhosts, localhost, rcode) 43 char *host, **mxhosts, *localhost; 44 int *rcode; 45 { 46 extern int h_errno; 47 register u_char *eom, *cp; 48 register int i, j, n, nmx; 49 register char *bp; 50 HEADER *hp; 51 querybuf answer; 52 int ancount, qdcount, buflen, seenlocal; 53 u_short pref, localpref, type, prefer[MAXMXHOSTS]; 54 int weight[MAXMXHOSTS]; 55 56 errno = 0; 57 n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); 58 if (n < 0) 59 { 60 if (tTd(8, 1)) 61 printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 62 (host == NULL) ? "<NULL>" : host, errno, h_errno); 63 switch (h_errno) 64 { 65 case NO_DATA: 66 case NO_RECOVERY: 67 /* no MX data on this host */ 68 goto punt; 69 70 case HOST_NOT_FOUND: 71 /* the host just doesn't exist */ 72 *rcode = EX_NOHOST; 73 break; 74 75 case TRY_AGAIN: 76 /* couldn't connect to the name server */ 77 if (!UseNameServer && errno == ECONNREFUSED) 78 goto punt; 79 80 /* it might come up later; better queue it up */ 81 *rcode = EX_TEMPFAIL; 82 break; 83 } 84 85 /* irreconcilable differences */ 86 return (-1); 87 } 88 89 /* find first satisfactory answer */ 90 hp = (HEADER *)&answer; 91 cp = (u_char *)&answer + sizeof(HEADER); 92 eom = (u_char *)&answer + n; 93 for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 94 if ((n = dn_skipname(cp, eom)) < 0) 95 goto punt; 96 nmx = 0; 97 seenlocal = 0; 98 buflen = sizeof(hostbuf) - 1; 99 bp = hostbuf; 100 ancount = ntohs(hp->ancount); 101 while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) 102 { 103 if ((n = dn_expand((u_char *)&answer, 104 eom, cp, (u_char *)bp, buflen)) < 0) 105 break; 106 cp += n; 107 GETSHORT(type, cp); 108 cp += sizeof(u_short) + sizeof(u_long); 109 GETSHORT(n, cp); 110 if (type != T_MX) 111 { 112 if (tTd(8, 8) || _res.options & RES_DEBUG) 113 printf("unexpected answer type %d, size %d\n", 114 type, n); 115 cp += n; 116 continue; 117 } 118 GETSHORT(pref, cp); 119 if ((n = dn_expand((u_char *)&answer, eom, cp, 120 (u_char *)bp, buflen)) < 0) 121 break; 122 cp += n; 123 if (!strcasecmp(bp, localhost)) 124 { 125 if (seenlocal == 0 || pref < localpref) 126 localpref = pref; 127 seenlocal = 1; 128 continue; 129 } 130 weight[nmx] = mxrand(bp); 131 prefer[nmx] = pref; 132 mxhosts[nmx++] = bp; 133 n = strlen(bp); 134 bp += n; 135 if (bp[-1] != '.') 136 { 137 *bp++ = '.'; 138 n++; 139 } 140 *bp++ = '\0'; 141 buflen -= n + 1; 142 } 143 if (nmx == 0) 144 { 145 punt: mxhosts[0] = strcpy(hostbuf, host); 146 return (1); 147 } 148 149 /* sort the records */ 150 for (i = 0; i < nmx; i++) 151 { 152 for (j = i + 1; j < nmx; j++) 153 { 154 if (prefer[i] > prefer[j] || 155 (prefer[i] == prefer[j] && weight[i] > weight[j])) 156 { 157 register int temp; 158 register char *temp1; 159 160 temp = prefer[i]; 161 prefer[i] = prefer[j]; 162 prefer[j] = temp; 163 temp1 = mxhosts[i]; 164 mxhosts[i] = mxhosts[j]; 165 mxhosts[j] = temp1; 166 temp = weight[i]; 167 weight[i] = weight[j]; 168 weight[j] = temp; 169 } 170 } 171 if (seenlocal && prefer[i] >= localpref) 172 { 173 /* 174 * truncate higher pref part of list; if we're 175 * the best choice left, we should have realized 176 * awhile ago that this was a local delivery. 177 */ 178 if (i == 0) 179 { 180 *rcode = EX_CONFIG; 181 return (-1); 182 } 183 nmx = i; 184 break; 185 } 186 } 187 return (nmx); 188 } 189 /* 190 ** MXRAND -- create a randomizer for equal MX preferences 191 ** 192 ** If two MX hosts have equal preferences we want to randomize 193 ** the selection. But in order for signatures to be the same, 194 ** we need to randomize the same way each time. This function 195 ** computes a pseudo-random hash function from the host name. 196 ** 197 ** Parameters: 198 ** host -- the name of the host. 199 ** 200 ** Returns: 201 ** A random but repeatable value based on the host name. 202 ** 203 ** Side Effects: 204 ** none. 205 */ 206 207 mxrand(host) 208 register char *host; 209 { 210 int hfunc; 211 static unsigned int seed; 212 213 if (seed == 0) 214 { 215 seed = (int) curtime() & 0xffff; 216 if (seed == 0) 217 seed++; 218 } 219 220 if (tTd(17, 9)) 221 printf("mxrand(%s)", host); 222 223 hfunc = seed; 224 while (*host != '\0') 225 { 226 int c = *host++; 227 228 if (isascii(c) && isupper(c)) 229 c = tolower(c); 230 hfunc = ((hfunc << 1) + c) % 2003; 231 } 232 233 hfunc &= 0xff; 234 235 if (tTd(17, 9)) 236 printf(" = %d\n", hfunc); 237 return hfunc; 238 } 239 /* 240 ** GETCANONNAME -- get the canonical name for named host 241 ** 242 ** This algorithm tries to be smart about wildcard MX records. 243 ** This is hard to do because DNS doesn't tell is if we matched 244 ** against a wildcard or a specific MX. 245 ** 246 ** We always prefer A & CNAME records, since these are presumed 247 ** to be specific. 248 ** 249 ** If we match an MX in one pass and lose it in the next, we use 250 ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 251 ** A hostname bletch.foo.bar.com will match against this MX, but 252 ** will stop matching when we try bletch.bar.com -- so we know 253 ** that bletch.foo.bar.com must have been right. This fails if 254 ** there was also an MX record matching *.BAR.COM, but there are 255 ** some things that just can't be fixed. 256 ** 257 ** Parameters: 258 ** host -- a buffer containing the name of the host. 259 ** This is a value-result parameter. 260 ** hbsize -- the size of the host buffer. 261 ** 262 ** Returns: 263 ** TRUE -- if the host matched. 264 ** FALSE -- otherwise. 265 */ 266 267 bool 268 getcanonname(host, hbsize) 269 char *host; 270 int hbsize; 271 { 272 extern int h_errno; 273 register u_char *eom, *ap; 274 register char *cp; 275 register int n; 276 HEADER *hp; 277 querybuf answer; 278 int first, ancount, qdcount; 279 int ret; 280 char **domain; 281 int type; 282 char **dp; 283 char *mxmatch; 284 bool amatch; 285 char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; 286 char *searchlist[MAXDNSRCH+2]; 287 288 if (tTd(8, 2)) 289 printf("getcanonname(%s)\n", host); 290 291 if ((_res.options & RES_INIT) == 0 && res_init() == -1) 292 return (FALSE); 293 294 for (cp = host, n = 0; *cp; cp++) 295 if (*cp == '.') 296 n++; 297 298 /* 299 ** Initialize domain search list. If there is at least one 300 ** dot in the name, search the unmodified name first so we 301 ** find "vse.CS" in Czechoslovakia instead of in the local 302 ** domain (e.g., vse.CS.Berkeley.EDU). 303 ** 304 ** Older versions of the resolver could create this 305 ** list by tearing apart the host name. 306 */ 307 308 dp = searchlist; 309 if (n > 0) 310 *dp++ = ""; 311 if (n == 0 || n > 0 && *--cp != '.') 312 { 313 for (domain = _res.dnsrch; *domain != NULL; ) 314 *dp++ = *domain++; 315 } 316 *dp = NULL; 317 318 /* 319 ** Now run through the search list for the name in question. 320 */ 321 322 dp = searchlist; 323 mxmatch = NULL; 324 325 for (dp = searchlist; *dp != NULL; dp++) 326 { 327 if (tTd(8, 5)) 328 printf("getcanonname: trying %s.%s\n", host, *dp); 329 ret = res_querydomain(host, *dp, C_IN, T_ANY, 330 &answer, sizeof(answer)); 331 if (ret <= 0) 332 { 333 if (tTd(8, 8)) 334 printf("\tNO: errno=%d, h_errno=%d\n", 335 errno, h_errno); 336 337 if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 338 { 339 /* the name server seems to be down */ 340 h_errno = TRY_AGAIN; 341 return FALSE; 342 } 343 344 if (mxmatch != NULL) 345 { 346 /* we matched before -- use that one */ 347 break; 348 } 349 continue; 350 } 351 if (tTd(8, 8)) 352 printf("\tYES\n"); 353 354 /* 355 ** This might be a bogus match. Search for A or 356 ** CNAME records. If we don't have a matching 357 ** wild card MX record, we will accept MX as well. 358 */ 359 360 hp = (HEADER *) &answer; 361 ap = (u_char *) &answer + sizeof(HEADER); 362 eom = (u_char *) &answer + ret; 363 364 /* skip question part of response -- we know what we asked */ 365 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 366 { 367 if ((ret = dn_skipname(ap, eom)) < 0) 368 { 369 if (tTd(8, 20)) 370 printf("qdcount failure (%d)\n", 371 ntohs(hp->qdcount)); 372 return FALSE; /* ???XXX??? */ 373 } 374 } 375 376 amatch = FALSE; 377 for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) 378 { 379 n = dn_expand((u_char *) &answer, eom, ap, 380 (u_char *) nbuf, sizeof nbuf); 381 if (n < 0) 382 break; 383 ap += n; 384 GETSHORT(type, ap); 385 ap += sizeof(u_short) + sizeof(u_long); 386 GETSHORT(n, ap); 387 switch (type) 388 { 389 case T_MX: 390 if (**dp != '\0') 391 { 392 /* got a match -- save that info */ 393 if (mxmatch == NULL) 394 mxmatch = *dp; 395 continue; 396 } 397 398 /* exact MX matches are as good as an A match */ 399 /* fall through */ 400 401 case T_A: 402 /* good show */ 403 amatch = TRUE; 404 405 /* continue in case a CNAME also exists */ 406 continue; 407 408 case T_CNAME: 409 /* value points at name */ 410 if ((ret = dn_expand((u_char *)&answer, 411 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 412 break; 413 (void)strncpy(host, nbuf, hbsize); /* XXX */ 414 host[hbsize - 1] = '\0'; 415 return TRUE; 416 417 default: 418 /* not a record of interest */ 419 continue; 420 } 421 } 422 423 if (amatch) 424 { 425 /* got an A record and no CNAME */ 426 mxmatch = *dp; 427 break; 428 } 429 } 430 431 if (mxmatch == NULL) 432 return FALSE; 433 434 /* create matching name and return */ 435 (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, 436 *mxmatch == '\0' ? "" : ".", 437 MAXDNAME, mxmatch); 438 strncpy(host, nbuf, hbsize); 439 host[hbsize - 1] = '\0'; 440 return TRUE; 441 } 442 443 #else /* not NAMED_BIND */ 444 445 #include <netdb.h> 446 447 bool 448 getcanonname(host, hbsize) 449 char *host; 450 int hbsize; 451 { 452 struct hostent *hp; 453 454 hp = gethostbyname(host); 455 if (hp == NULL) 456 return (FALSE); 457 458 if (strlen(hp->h_name) >= hbsize) 459 return (FALSE); 460 461 (void) strcpy(host, hp->h_name); 462 return (TRUE); 463 } 464 465 #endif /* not NAMED_BIND */ 466