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