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