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