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.35 (Berkeley) 09/23/92 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 5.35 (Berkeley) 09/23/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 /* 172 * Use query type of ANY if possible (NoWildcardMX), which will 173 * find types CNAME, A, and MX, and will cause all existing records 174 * to be cached by our local server. If there is (might be) a 175 * wildcard MX record in the local domain or its parents that are 176 * searched, we can't use ANY; it would cause fully-qualified names 177 * to match as names in a local domain. 178 */ 179 180 bool 181 getcanonname(host, hbsize) 182 char *host; 183 int hbsize; 184 { 185 extern int h_errno; 186 register u_char *eom, *ap; 187 register char *cp; 188 register int n; 189 HEADER *hp; 190 querybuf answer; 191 int first, ancount, qdcount, loopcnt; 192 int ret; 193 int qtype = NoWildcardMX ? T_ANY : T_CNAME; 194 char **domain; 195 bool rval; 196 int type; 197 char nbuf[PACKETSZ]; 198 199 if (tTd(8, 2)) 200 printf("getcanonname(%s)\n", host); 201 202 if ((_res.options & RES_INIT) == 0 && res_init() == -1) 203 return (FALSE); 204 205 loopcnt = 0; 206 rval = FALSE; 207 loop: 208 for (cp = host, n = 0; *cp; cp++) 209 if (*cp == '.') 210 n++; 211 if (n > 0 && *--cp == '.') 212 { 213 cp = host; 214 n = -1; 215 } 216 217 /* 218 * We do at least one level of search if 219 * - there is no dot and RES_DEFNAME is set, or 220 * - there is at least one dot, there is no trailing dot, 221 * and RES_DNSRCH is set. 222 */ 223 ret = -1; 224 if ((n == 0 && _res.options & RES_DEFNAMES) || 225 (n > 0 && *--cp != '.' && _res.options & RES_DNSRCH)) 226 { 227 for (domain = _res.dnsrch; *domain; domain++) 228 { 229 (void) sprintf(nbuf, "%.*s.%.*s", 230 MAXDNAME, host, MAXDNAME, *domain); 231 if (tTd(8, 5)) 232 printf("getcanonname: trying %s\n", nbuf); 233 ret = res_query(nbuf, C_IN, qtype, &answer, sizeof(answer)); 234 if (ret > 0) 235 { 236 if (tTd(8, 8)) 237 printf("\tYES\n"); 238 cp = nbuf; 239 break; 240 } 241 else if (tTd(8, 8)) 242 printf("\tNO: h_errno=%d\n", h_errno); 243 244 /* 245 * If no server present, give up. 246 * If name isn't found in this domain, 247 * keep trying higher domains in the search list 248 * (if that's enabled). 249 * On a NO_DATA error, keep trying, otherwise 250 * a wildcard entry of another type could keep us 251 * from finding this entry higher in the domain. 252 * If we get some other error (negative answer or 253 * server failure), then stop searching up, 254 * but try the input name below in case it's fully-qualified. 255 */ 256 if (errno == ECONNREFUSED) { 257 h_errno = TRY_AGAIN; 258 return FALSE; 259 } 260 if (h_errno == NO_DATA) 261 { 262 ret = 0; 263 cp = nbuf; 264 break; 265 } 266 if ((h_errno != HOST_NOT_FOUND) || 267 (_res.options & RES_DNSRCH) == 0) 268 return FALSE; 269 } 270 } 271 if (ret < 0) 272 { 273 /* 274 ** Try the unmodified name. 275 */ 276 277 cp = host; 278 if (tTd(8, 5)) 279 printf("getcanonname: trying %s\n", cp); 280 ret = res_query(cp, C_IN, qtype, &answer, sizeof(answer)); 281 if (ret > 0) 282 { 283 if (tTd(8, 8)) 284 printf("\tYES\n"); 285 } 286 else 287 { 288 if (tTd(8, 8)) 289 printf("\tNO: h_errno=%d\n", h_errno); 290 if (h_errno != NO_DATA) 291 return FALSE; 292 } 293 } 294 295 /* find first satisfactory answer */ 296 hp = (HEADER *)&answer; 297 ancount = ntohs(hp->ancount); 298 if (tTd(8, 3)) 299 printf("rcode = %d, ancount=%d, qdcount=%d\n", 300 hp->rcode, ancount, ntohs(hp->qdcount)); 301 302 /* we don't care about errors here, only if we got an answer */ 303 if (ancount == 0) 304 { 305 strncpy(host, cp, hbsize); 306 host[hbsize - 1] = '\0'; 307 return (TRUE); 308 } 309 ap = (u_char *)&answer + sizeof(HEADER); 310 eom = (u_char *)&answer + ret; 311 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 312 { 313 if ((ret = dn_skipname(ap, eom)) < 0) 314 { 315 if (tTd(8, 20)) 316 printf("qdcount failure (%d)\n", 317 ntohs(hp->qdcount)); 318 return FALSE; /* ???XXX??? */ 319 } 320 } 321 322 /* 323 * just in case someone puts a CNAME record after another record, 324 * check all records for CNAME; otherwise, just take the first 325 * name found. 326 */ 327 for (first = 1; --ancount >= 0 && ap < eom; ap += ret) 328 { 329 if ((ret = dn_expand((u_char *)&answer, 330 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 331 break; 332 if (first) { /* XXX */ 333 (void)strncpy(host, nbuf, hbsize); 334 host[hbsize - 1] = '\0'; 335 first = 0; 336 rval = TRUE; 337 } 338 ap += ret; 339 GETSHORT(type, ap); 340 ap += sizeof(u_short) + sizeof(u_long); 341 GETSHORT(ret, ap); 342 if (type == T_CNAME) { 343 /* 344 * assume that only one cname will be found. More 345 * than one is undefined. Copy so that if dn_expand 346 * fails, `host' is still okay. 347 */ 348 if ((ret = dn_expand((u_char *)&answer, 349 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 350 break; 351 (void)strncpy(host, nbuf, hbsize); /* XXX */ 352 host[hbsize - 1] = '\0'; 353 if (++loopcnt > 8) /* never be more than 1 */ 354 return FALSE; 355 rval = TRUE; 356 goto loop; 357 } 358 } 359 return rval; /* ???XXX??? */ 360 } 361 362 #else /* not NAMED_BIND */ 363 364 #include <netdb.h> 365 366 bool 367 getcanonname(host, hbsize) 368 char *host; 369 int hbsize; 370 { 371 struct hostent *hp; 372 373 hp = gethostbyname(host); 374 if (hp == NULL) 375 return (FALSE); 376 377 if (strlen(hp->h_name) >= hbsize) 378 return (FALSE); 379 380 (void) strcpy(host, hp->h_name); 381 return (TRUE); 382 } 383 384 #endif /* not NAMED_BIND */ 385