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