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.26 (Berkeley) 07/29/91 (with name server)"; 14 #else 15 static char sccsid[] = "@(#)domain.c 5.26 (Berkeley) 07/29/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 #ifndef BSD4_4 28 #define __dn_skipname dn_skipname 29 #endif 30 31 typedef union { 32 HEADER qb1; 33 char qb2[PACKETSZ]; 34 } querybuf; 35 36 static char hostbuf[MAXMXHOSTS*PACKETSZ]; 37 38 getmxrr(host, mxhosts, localhost, rcode) 39 char *host, **mxhosts, *localhost; 40 int *rcode; 41 { 42 extern int h_errno; 43 register u_char *eom, *cp; 44 register int i, j, n, nmx; 45 register char *bp; 46 HEADER *hp; 47 querybuf answer; 48 int ancount, qdcount, buflen, seenlocal; 49 u_short pref, localpref, type, prefer[MAXMXHOSTS]; 50 51 errno = 0; 52 n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); 53 if (n < 0) 54 { 55 if (tTd(8, 1)) 56 printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n", 57 errno, h_errno); 58 switch (h_errno) 59 { 60 case NO_DATA: 61 case NO_RECOVERY: 62 /* no MX data on this host */ 63 goto punt; 64 65 case HOST_NOT_FOUND: 66 /* the host just doesn't exist */ 67 *rcode = EX_NOHOST; 68 break; 69 70 case TRY_AGAIN: 71 /* couldn't connect to the name server */ 72 if (!UseNameServer && errno == ECONNREFUSED) 73 goto punt; 74 75 /* it might come up later; better queue it up */ 76 *rcode = EX_TEMPFAIL; 77 break; 78 } 79 80 /* irreconcilable differences */ 81 return (-1); 82 } 83 84 /* find first satisfactory answer */ 85 hp = (HEADER *)&answer; 86 cp = (u_char *)&answer + sizeof(HEADER); 87 eom = (u_char *)&answer + n; 88 for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 89 if ((n = __dn_skipname(cp, eom)) < 0) 90 goto punt; 91 nmx = 0; 92 seenlocal = 0; 93 buflen = sizeof(hostbuf); 94 bp = hostbuf; 95 ancount = ntohs(hp->ancount); 96 while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { 97 if ((n = dn_expand((u_char *)&answer, 98 eom, cp, (u_char *)bp, buflen)) < 0) 99 break; 100 cp += n; 101 GETSHORT(type, cp); 102 cp += sizeof(u_short) + sizeof(u_long); 103 GETSHORT(n, cp); 104 if (type != T_MX) { 105 if (tTd(8, 1) || _res.options & RES_DEBUG) 106 printf("unexpected answer type %d, size %d\n", 107 type, n); 108 cp += n; 109 continue; 110 } 111 GETSHORT(pref, cp); 112 if ((n = dn_expand((u_char *)&answer, 113 eom, cp, (u_char *)bp, buflen)) < 0) 114 break; 115 cp += n; 116 if (!strcasecmp(bp, localhost)) { 117 if (seenlocal == 0 || pref < localpref) 118 localpref = pref; 119 seenlocal = 1; 120 continue; 121 } 122 prefer[nmx] = pref; 123 mxhosts[nmx++] = bp; 124 n = strlen(bp) + 1; 125 bp += n; 126 buflen -= n; 127 } 128 if (nmx == 0) { 129 punt: mxhosts[0] = strcpy(hostbuf, host); 130 return(1); 131 } 132 133 /* sort the records */ 134 for (i = 0; i < nmx; i++) { 135 for (j = i + 1; j < nmx; j++) { 136 if (prefer[i] > prefer[j] || 137 (prefer[i] == prefer[j] && rand() % 1 == 0)) { 138 register int temp; 139 register char *temp1; 140 141 temp = prefer[i]; 142 prefer[i] = prefer[j]; 143 prefer[j] = temp; 144 temp1 = mxhosts[i]; 145 mxhosts[i] = mxhosts[j]; 146 mxhosts[j] = temp1; 147 } 148 } 149 if (seenlocal && prefer[i] >= localpref) { 150 /* 151 * truncate higher pref part of list; if we're 152 * the best choice left, we should have realized 153 * awhile ago that this was a local delivery. 154 */ 155 if (i == 0) { 156 *rcode = EX_CONFIG; 157 return(-1); 158 } 159 nmx = i; 160 break; 161 } 162 } 163 return(nmx); 164 } 165 166 getcanonname(host, hbsize) 167 char *host; 168 int hbsize; 169 { 170 extern int h_errno; 171 register u_char *eom, *cp; 172 register int n; 173 HEADER *hp; 174 querybuf answer; 175 u_short type; 176 int first, ancount, qdcount, loopcnt; 177 char nbuf[PACKETSZ]; 178 179 loopcnt = 0; 180 loop: 181 /* 182 * Use query type of ANY if possible (NO_WILDCARD_MX), which will 183 * find types CNAME, A, and MX, and will cause all existing records 184 * to be cached by our local server. If there is (might be) a 185 * wildcard MX record in the local domain or its parents that are 186 * searched, we can't use ANY; it would cause fully-qualified names 187 * to match as names in a local domain. 188 */ 189 n = res_search(host, C_IN, WildcardMX ? T_CNAME : T_ANY, 190 (char *)&answer, sizeof(answer)); 191 if (n < 0) { 192 if (tTd(8, 1)) 193 printf("getcanonname: res_search failed (errno=%d, h_errno=%d)\n", 194 errno, h_errno); 195 return; 196 } 197 198 /* find first satisfactory answer */ 199 hp = (HEADER *)&answer; 200 ancount = ntohs(hp->ancount); 201 202 /* we don't care about errors here, only if we got an answer */ 203 if (ancount == 0) { 204 if (tTd(8, 1)) 205 printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 206 return; 207 } 208 cp = (u_char *)&answer + sizeof(HEADER); 209 eom = (u_char *)&answer + n; 210 for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 211 if ((n = __dn_skipname(cp, eom)) < 0) 212 return; 213 214 /* 215 * just in case someone puts a CNAME record after another record, 216 * check all records for CNAME; otherwise, just take the first 217 * name found. 218 */ 219 for (first = 1; --ancount >= 0 && cp < eom; cp += n) { 220 if ((n = dn_expand((u_char *)&answer, 221 eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) 222 break; 223 if (first) { /* XXX */ 224 (void)strncpy(host, nbuf, hbsize); 225 host[hbsize - 1] = '\0'; 226 first = 0; 227 } 228 cp += n; 229 GETSHORT(type, cp); 230 cp += sizeof(u_short) + sizeof(u_long); 231 GETSHORT(n, cp); 232 if (type == T_CNAME) { 233 /* 234 * assume that only one cname will be found. More 235 * than one is undefined. Copy so that if dn_expand 236 * fails, `host' is still okay. 237 */ 238 if ((n = dn_expand((u_char *)&answer, 239 eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) 240 break; 241 (void)strncpy(host, nbuf, hbsize); /* XXX */ 242 host[hbsize - 1] = '\0'; 243 if (++loopcnt > 8) /* never be more than 1 */ 244 return; 245 goto loop; 246 } 247 } 248 } 249 250 #else /* not NAMED_BIND */ 251 252 #include <netdb.h> 253 254 getcanonname(host, hbsize) 255 char *host; 256 int hbsize; 257 { 258 struct hostent *hp; 259 260 hp = gethostbyname(host); 261 if (hp == NULL) 262 return; 263 264 if (strlen(hp->h_name) >= hbsize) 265 return; 266 267 (void) strcpy(host, hp->h_name); 268 } 269 270 #endif /* not NAMED_BIND */ 271