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.38 (Berkeley) 12/18/92 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 5.38 (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 assume that RES_DEFNAMES and RES_DNSRCH are set -- if we 266 ** don't want this behaviour, don't use $[ ... $] at all! 267 */ 268 269 if (ret < 0 && (n == 0 || (n > 0 && *--cp != '.'))) 270 { 271 for (domain = _res.dnsrch; *domain; domain++) 272 { 273 (void) sprintf(nbuf, "%.*s.%.*s", 274 MAXDNAME, host, MAXDNAME, *domain); 275 if (tTd(8, 5)) 276 printf("getcanonname: trying %s\n", nbuf); 277 ret = res_query(nbuf, C_IN, qtype, &answer, sizeof(answer)); 278 if (ret > 0) 279 { 280 if (tTd(8, 8)) 281 printf("\tYES\n"); 282 cp = nbuf; 283 break; 284 } 285 else if (tTd(8, 8)) 286 printf("\tNO: h_errno=%d\n", h_errno); 287 288 /* 289 * If no server present, give up. 290 * If name isn't found in this domain, 291 * keep trying higher domains in the search list 292 * (if that's enabled). 293 * On a NO_DATA error, keep trying, otherwise 294 * a wildcard entry of another type could keep us 295 * from finding this entry higher in the domain. 296 * If we get some other error (negative answer or 297 * server failure), then stop searching up, 298 * but try the input name below in case it's 299 * fully-qualified. 300 */ 301 302 if (errno == ECONNREFUSED) 303 { 304 h_errno = TRY_AGAIN; 305 return FALSE; 306 } 307 if (h_errno == NO_DATA) 308 { 309 ret = 0; 310 cp = nbuf; 311 break; 312 } 313 if ((h_errno != HOST_NOT_FOUND) || 314 (_res.options & RES_DNSRCH) == 0) 315 return FALSE; 316 } 317 } 318 if (ret < 0 && n <= 0) 319 { 320 /* 321 ** Try the unmodified name. 322 */ 323 324 cp = host; 325 if (tTd(8, 5)) 326 printf("getcanonname: trying %s\n", cp); 327 ret = res_query(cp, C_IN, qtype, &answer, sizeof(answer)); 328 if (ret > 0) 329 { 330 if (tTd(8, 8)) 331 printf("\tYES\n"); 332 } 333 else 334 { 335 if (tTd(8, 8)) 336 printf("\tNO: h_errno=%d\n", h_errno); 337 } 338 } 339 340 if (ret <= 0 && h_errno != NO_DATA) 341 return FALSE; 342 343 /* find first satisfactory answer */ 344 hp = (HEADER *)&answer; 345 ancount = ntohs(hp->ancount); 346 if (tTd(8, 3)) 347 printf("rcode = %d, ancount=%d, qdcount=%d\n", 348 hp->rcode, ancount, ntohs(hp->qdcount)); 349 350 /* we don't care about errors here, only if we got an answer */ 351 if (ancount == 0) 352 { 353 strncpy(host, cp, hbsize); 354 host[hbsize - 1] = '\0'; 355 return (TRUE); 356 } 357 ap = (u_char *)&answer + sizeof(HEADER); 358 eom = (u_char *)&answer + ret; 359 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 360 { 361 if ((ret = dn_skipname(ap, eom)) < 0) 362 { 363 if (tTd(8, 20)) 364 printf("qdcount failure (%d)\n", 365 ntohs(hp->qdcount)); 366 return FALSE; /* ???XXX??? */ 367 } 368 } 369 370 /* 371 * just in case someone puts a CNAME record after another record, 372 * check all records for CNAME; otherwise, just take the first 373 * name found. 374 */ 375 for (first = 1; --ancount >= 0 && ap < eom; ap += ret) 376 { 377 if ((ret = dn_expand((u_char *)&answer, 378 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 379 break; 380 if (first) { /* XXX */ 381 (void)strncpy(host, nbuf, hbsize); 382 host[hbsize - 1] = '\0'; 383 first = 0; 384 rval = TRUE; 385 } 386 ap += ret; 387 GETSHORT(type, ap); 388 ap += sizeof(u_short) + sizeof(u_long); 389 GETSHORT(ret, ap); 390 if (type == T_CNAME) { 391 /* 392 * assume that only one cname will be found. More 393 * than one is undefined. Copy so that if dn_expand 394 * fails, `host' is still okay. 395 */ 396 if ((ret = dn_expand((u_char *)&answer, 397 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 398 break; 399 (void)strncpy(host, nbuf, hbsize); /* XXX */ 400 host[hbsize - 1] = '\0'; 401 if (++loopcnt > 8) /* never be more than 1 */ 402 return FALSE; 403 rval = TRUE; 404 goto loop; 405 } 406 } 407 return rval; /* ???XXX??? */ 408 } 409 410 #else /* not NAMED_BIND */ 411 412 #include <netdb.h> 413 414 bool 415 getcanonname(host, hbsize) 416 char *host; 417 int hbsize; 418 { 419 struct hostent *hp; 420 421 hp = gethostbyname(host); 422 if (hp == NULL) 423 return (FALSE); 424 425 if (strlen(hp->h_name) >= hbsize) 426 return (FALSE); 427 428 (void) strcpy(host, hp->h_name); 429 return (TRUE); 430 } 431 432 #endif /* not NAMED_BIND */ 433