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