1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 * 12 * Sendmail 13 * Copyright (c) 1986 Eric P. Allman 14 * Berkeley, California 15 */ 16 17 #include "sendmail.h" 18 19 #ifndef lint 20 #ifdef MXDOMAIN 21 static char sccsid[] = "@(#)domain.c 5.10 (Berkeley) 03/24/88 (with MXDOMAIN)"; 22 #else 23 static char sccsid[] = "@(#)domain.c 5.10 (Berkeley) 03/24/88 (without MXDOMAIN)"; 24 #endif 25 #endif /* not lint */ 26 27 #ifdef MXDOMAIN 28 # include <sys/param.h> 29 # include <arpa/nameser.h> 30 # include <resolv.h> 31 # include <netdb.h> 32 33 typedef union { 34 HEADER qb1; 35 char qb2[PACKETSZ]; 36 } querybuf; 37 38 static char hostbuf[BUFSIZ]; 39 int h_errno; 40 extern u_short _getshort(); 41 42 getmxrr(host, mxhosts, maxmx, localhost) 43 char *host, **mxhosts; 44 int maxmx; 45 char *localhost; 46 { 47 48 HEADER *hp; 49 char *eom, *bp, *cp; 50 querybuf buf, answer; 51 int n, n1, i, j, nmx, ancount, qdcount, buflen; 52 int seenlocal; 53 u_short prefer[BUFSIZ]; 54 u_short pref, localpref, type, class; 55 56 n = res_mkquery(QUERY, host, C_IN, T_MX, (char *)NULL, 0, NULL, 57 (char *)&buf, sizeof(buf)); 58 if (n < 0) { 59 #ifdef DEBUG 60 if (tTd(8, 1) || _res.options & RES_DEBUG) 61 printf("res_mkquery failed\n"); 62 #endif 63 h_errno = NO_RECOVERY; 64 return(-2); 65 } 66 n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer)); 67 if (n < 0) { 68 #ifdef DEBUG 69 if (tTd(8, 1) || _res.options & RES_DEBUG) 70 printf("res_send failed\n"); 71 #endif 72 h_errno = TRY_AGAIN; 73 return (-1); 74 } 75 eom = (char *)&answer + n; 76 /* 77 * find first satisfactory answer 78 */ 79 hp = (HEADER *) &answer; 80 ancount = ntohs(hp->ancount); 81 qdcount = ntohs(hp->qdcount); 82 if (hp->rcode != NOERROR || ancount == 0) { 83 #ifdef DEBUG 84 if (tTd(8, 1) || _res.options & RES_DEBUG) 85 printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 86 #endif 87 switch (hp->rcode) { 88 case NXDOMAIN: 89 /* Check if it's an authoritive answer */ 90 if (hp->aa) { 91 h_errno = HOST_NOT_FOUND; 92 return(-3); 93 } else { 94 h_errno = TRY_AGAIN; 95 return(-1); 96 } 97 case SERVFAIL: 98 h_errno = TRY_AGAIN; 99 return(-1); 100 #ifdef OLDJEEVES 101 /* 102 * Jeeves (TOPS-20 server) still does not 103 * support MX records. For the time being, 104 * we must accept FORMERRs as the same as 105 * NOERROR. 106 */ 107 case FORMERR: 108 #endif OLDJEEVES 109 case NOERROR: 110 (void) strcpy(hostbuf, host); 111 mxhosts[0] = hostbuf; 112 return(1); 113 #ifndef OLDJEEVES 114 case FORMERR: 115 #endif OLDJEEVES 116 case NOTIMP: 117 case REFUSED: 118 h_errno = NO_RECOVERY; 119 return(-2); 120 } 121 return (-1); 122 } 123 bp = hostbuf; 124 nmx = 0; 125 seenlocal = 0; 126 buflen = sizeof(hostbuf); 127 cp = (char *)&answer + sizeof(HEADER); 128 if (qdcount) { 129 cp += dn_skipname(cp, eom) + QFIXEDSZ; 130 while (--qdcount > 0) 131 cp += dn_skipname(cp, eom) + QFIXEDSZ; 132 } 133 while (--ancount >= 0 && cp < eom && nmx < maxmx) { 134 if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 135 break; 136 cp += n; 137 type = _getshort(cp); 138 cp += sizeof(u_short); 139 /* 140 class = _getshort(cp); 141 */ 142 cp += sizeof(u_short) + sizeof(u_long); 143 n = _getshort(cp); 144 cp += sizeof(u_short); 145 if (type != T_MX) { 146 #ifdef DEBUG 147 if (tTd(8, 1) || _res.options & RES_DEBUG) 148 printf("unexpected answer type %d, size %d\n", 149 type, n); 150 #endif 151 cp += n; 152 continue; 153 } 154 pref = _getshort(cp); 155 cp += sizeof(u_short); 156 if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 157 break; 158 cp += n; 159 if (!strcasecmp(bp, localhost)) 160 { 161 seenlocal = 1; 162 localpref = pref; 163 continue; 164 } 165 prefer[nmx] = pref; 166 mxhosts[nmx++] = bp; 167 n1 = strlen(bp)+1; 168 bp += n1; 169 buflen -= n1; 170 } 171 if (nmx == 0) { 172 (void) strcpy(hostbuf, host); 173 mxhosts[0] = hostbuf; 174 return(1); 175 } 176 /* sort the records */ 177 for (i = 0; i < nmx; i++) { 178 for (j = i + 1; j < nmx; j++) { 179 if (prefer[i] > prefer[j]) { 180 int temp; 181 char *temp1; 182 183 temp = prefer[i]; 184 prefer[i] = prefer[j]; 185 prefer[j] = temp; 186 temp1 = mxhosts[i]; 187 mxhosts[i] = mxhosts[j]; 188 mxhosts[j] = temp1; 189 } 190 } 191 if (seenlocal && (prefer[i] >= localpref)) 192 { 193 nmx = i; 194 /* 195 * We are the first MX, might as well try delivering 196 * since nobody is supposed to have more info. 197 */ 198 if (nmx == 0) 199 { 200 (void) strcpy(hostbuf, host); 201 mxhosts[0] = hostbuf; 202 return(1); 203 } 204 break; 205 } 206 } 207 return(nmx); 208 } 209 210 211 getcanonname(host, hbsize) 212 char *host; 213 int hbsize; 214 { 215 216 HEADER *hp; 217 char *eom, *cp; 218 querybuf buf, answer; 219 int n, ancount, qdcount; 220 u_short type; 221 char nbuf[BUFSIZ]; 222 int first; 223 224 n = res_mkquery(QUERY, host, C_IN, T_ANY, (char *)NULL, 0, NULL, 225 (char *)&buf, sizeof(buf)); 226 if (n < 0) { 227 #ifdef DEBUG 228 if (tTd(8, 1) || _res.options & RES_DEBUG) 229 printf("res_mkquery failed\n"); 230 #endif 231 h_errno = NO_RECOVERY; 232 return; 233 } 234 n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer)); 235 if (n < 0) { 236 #ifdef DEBUG 237 if (tTd(8, 1) || _res.options & RES_DEBUG) 238 printf("res_send failed\n"); 239 #endif 240 h_errno = TRY_AGAIN; 241 return; 242 } 243 eom = (char *)&answer + n; 244 /* 245 * find first satisfactory answer 246 */ 247 hp = (HEADER *) &answer; 248 ancount = ntohs(hp->ancount); 249 qdcount = ntohs(hp->qdcount); 250 /* 251 * We don't care about errors here, only if we got an answer 252 */ 253 if (ancount == 0) { 254 #ifdef DEBUG 255 if (tTd(8, 1) || _res.options & RES_DEBUG) 256 printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 257 #endif 258 return; 259 } 260 cp = (char *)&answer + sizeof(HEADER); 261 if (qdcount) { 262 cp += dn_skipname(cp, eom) + QFIXEDSZ; 263 while (--qdcount > 0) 264 cp += dn_skipname(cp, eom) + QFIXEDSZ; 265 } 266 first = 1; 267 while (--ancount >= 0 && cp < eom) { 268 if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 269 sizeof(nbuf))) < 0) 270 break; 271 if (first) { 272 (void)strncpy(host, nbuf, hbsize); 273 host[hbsize - 1] = '\0'; 274 first = 0; 275 } 276 cp += n; 277 type = _getshort(cp); 278 cp += sizeof(u_short); 279 cp += sizeof(u_short) + sizeof(u_long); 280 n = _getshort(cp); 281 cp += sizeof(u_short); 282 if (type == T_CNAME) { 283 /* 284 * Assume that only one cname will be found. More 285 * than one is undefined. 286 */ 287 if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 288 sizeof(nbuf))) < 0) 289 break; 290 (void)strncpy(host, nbuf, hbsize); 291 host[hbsize - 1] = '\0'; 292 getcanonname(host, hbsize); 293 break; 294 } 295 cp += n; 296 } 297 return; 298 } 299 #endif MXDOMAIN 300