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.31 (Berkeley) 11/11/91 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 5.31 (Berkeley) 11/11/91 (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 failed (errno=%d, h_errno=%d)\n", 53 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); 90 bp = hostbuf; 91 ancount = ntohs(hp->ancount); 92 while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { 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 if (tTd(8, 1) || _res.options & RES_DEBUG) 102 printf("unexpected answer type %d, size %d\n", 103 type, n); 104 cp += n; 105 continue; 106 } 107 GETSHORT(pref, cp); 108 if ((n = dn_expand((u_char *)&answer, 109 eom, cp, (u_char *)bp, buflen)) < 0) 110 break; 111 cp += n; 112 if (!strcasecmp(bp, localhost)) { 113 if (seenlocal == 0 || pref < localpref) 114 localpref = pref; 115 seenlocal = 1; 116 continue; 117 } 118 prefer[nmx] = pref; 119 mxhosts[nmx++] = bp; 120 n = strlen(bp) + 1; 121 bp += n; 122 buflen -= n; 123 } 124 if (nmx == 0) { 125 punt: mxhosts[0] = strcpy(hostbuf, host); 126 return(1); 127 } 128 129 /* sort the records */ 130 for (i = 0; i < nmx; i++) { 131 for (j = i + 1; j < nmx; j++) { 132 if (prefer[i] > prefer[j] || 133 (prefer[i] == prefer[j] && rand() % 1 == 0)) { 134 register int temp; 135 register char *temp1; 136 137 temp = prefer[i]; 138 prefer[i] = prefer[j]; 139 prefer[j] = temp; 140 temp1 = mxhosts[i]; 141 mxhosts[i] = mxhosts[j]; 142 mxhosts[j] = temp1; 143 } 144 } 145 if (seenlocal && prefer[i] >= localpref) { 146 /* 147 * truncate higher pref part of list; if we're 148 * the best choice left, we should have realized 149 * awhile ago that this was a local delivery. 150 */ 151 if (i == 0) { 152 *rcode = EX_CONFIG; 153 return(-1); 154 } 155 nmx = i; 156 break; 157 } 158 } 159 return(nmx); 160 } 161 162 /* 163 * Use query type of ANY if possible (NoWildcardMX), which will 164 * find types CNAME, A, and MX, and will cause all existing records 165 * to be cached by our local server. If there is (might be) a 166 * wildcard MX record in the local domain or its parents that are 167 * searched, we can't use ANY; it would cause fully-qualified names 168 * to match as names in a local domain. 169 */ 170 171 bool 172 getcanonname(host, hbsize) 173 char *host; 174 int hbsize; 175 { 176 extern int h_errno; 177 register u_char *eom, *ap; 178 register char *cp; 179 register int n; 180 HEADER *hp; 181 querybuf answer; 182 int first, ancount, qdcount, loopcnt; 183 int ret; 184 int qtype = NoWildcardMX ? T_ANY : T_CNAME; 185 char **domain; 186 bool rval; 187 int type; 188 char nbuf[PACKETSZ]; 189 190 if (tTd(8, 2)) 191 printf("getcanonname(%s)\n", host); 192 193 if ((_res.options & RES_INIT) == 0 && res_init() == -1) 194 return (FALSE); 195 196 loopcnt = 0; 197 rval = FALSE; 198 loop: 199 for (cp = host, n = 0; *cp; cp++) 200 if (*cp == '.') 201 n++; 202 if (n > 0 && *--cp == '.') 203 { 204 cp = host; 205 n = -1; 206 } 207 208 /* 209 * We do at least one level of search if 210 * - there is no dot and RES_DEFNAME is set, or 211 * - there is at least one dot, there is no trailing dot, 212 * and RES_DNSRCH is set. 213 */ 214 ret = -1; 215 if ((n == 0 && _res.options & RES_DEFNAMES) || 216 (n > 0 && *--cp != '.' && _res.options & RES_DNSRCH)) 217 { 218 for (domain = _res.dnsrch; *domain; domain++) 219 { 220 (void) sprintf(nbuf, "%.*s.%.*s", 221 MAXDNAME, host, MAXDNAME, *domain); 222 if (tTd(8, 5)) 223 printf("getcanonname: trying %s\n", nbuf); 224 ret = res_query(nbuf, C_IN, qtype, &answer, sizeof(answer)); 225 if (ret > 0) 226 { 227 if (tTd(8, 8)) 228 printf("\tYES\n"); 229 cp = nbuf; 230 break; 231 } 232 else if (tTd(8, 8)) 233 printf("\tNO: h_errno=%d\n", h_errno); 234 235 /* 236 * If no server present, give up. 237 * If name isn't found in this domain, 238 * keep trying higher domains in the search list 239 * (if that's enabled). 240 * On a NO_DATA error, keep trying, otherwise 241 * a wildcard entry of another type could keep us 242 * from finding this entry higher in the domain. 243 * If we get some other error (negative answer or 244 * server failure), then stop searching up, 245 * but try the input name below in case it's fully-qualified. 246 */ 247 if (errno == ECONNREFUSED) { 248 h_errno = TRY_AGAIN; 249 return rval; 250 } 251 if (h_errno == NO_DATA) 252 { 253 ret = 0; 254 cp = nbuf; 255 break; 256 } 257 if ((h_errno != HOST_NOT_FOUND) || 258 (_res.options & RES_DNSRCH) == 0) 259 return rval; 260 } 261 } 262 if (ret < 0) 263 { 264 cp = host; 265 if (tTd(8, 5)) 266 printf("getcanonname: trying %s\n", cp); 267 ret = res_query(cp, C_IN, qtype, &answer, sizeof(answer)); 268 if (ret > 0) 269 { 270 if (tTd(8, 8)) 271 printf("\tYES\n"); 272 } 273 else 274 { 275 if (tTd(8, 8)) 276 printf("\tNO: h_errno=%d\n", h_errno); 277 return rval; 278 } 279 } 280 281 /* find first satisfactory answer */ 282 hp = (HEADER *)&answer; 283 ancount = ntohs(hp->ancount); 284 if (tTd(8, 3)) 285 printf("rcode = %d, ancount=%d, qdcount=%d\n", 286 hp->rcode, ancount, ntohs(hp->qdcount)); 287 288 /* we don't care about errors here, only if we got an answer */ 289 if (ancount == 0) 290 { 291 strncpy(host, cp, hbsize); 292 host[hbsize - 1] = '\0'; 293 return (TRUE); 294 } 295 ap = (u_char *)&answer + sizeof(HEADER); 296 eom = (u_char *)&answer + ret; 297 for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 298 { 299 if ((ret = dn_skipname(ap, eom)) < 0) 300 { 301 if (tTd(8, 20)) 302 printf("qdcount failure (%d)\n", 303 ntohs(hp->qdcount)); 304 return rval; /* ???XXX??? */ 305 } 306 } 307 308 /* 309 * just in case someone puts a CNAME record after another record, 310 * check all records for CNAME; otherwise, just take the first 311 * name found. 312 */ 313 for (first = 1; --ancount >= 0 && ap < eom; ap += ret) 314 { 315 if ((ret = dn_expand((u_char *)&answer, 316 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 317 break; 318 if (first) { /* XXX */ 319 (void)strncpy(host, nbuf, hbsize); 320 host[hbsize - 1] = '\0'; 321 first = 0; 322 rval = TRUE; 323 } 324 ap += ret; 325 GETSHORT(type, ap); 326 ap += sizeof(u_short) + sizeof(u_long); 327 GETSHORT(ret, ap); 328 if (type == T_CNAME) { 329 /* 330 * assume that only one cname will be found. More 331 * than one is undefined. Copy so that if dn_expand 332 * fails, `host' is still okay. 333 */ 334 if ((ret = dn_expand((u_char *)&answer, 335 eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 336 break; 337 (void)strncpy(host, nbuf, hbsize); /* XXX */ 338 host[hbsize - 1] = '\0'; 339 if (++loopcnt > 8) /* never be more than 1 */ 340 return FALSE; 341 rval = TRUE; 342 goto loop; 343 } 344 } 345 return rval; /* ???XXX??? */ 346 } 347 348 #else /* not NAMED_BIND */ 349 350 #include <netdb.h> 351 352 bool 353 getcanonname(host, hbsize) 354 char *host; 355 int hbsize; 356 { 357 struct hostent *hp; 358 359 hp = gethostbyname(host); 360 if (hp == NULL) 361 return (FALSE); 362 363 if (strlen(hp->h_name) >= hbsize) 364 return (FALSE); 365 366 (void) strcpy(host, hp->h_name); 367 return (TRUE); 368 } 369 370 #endif /* not NAMED_BIND */ 371