xref: /csrg-svn/usr.sbin/sendmail/src/domain.c (revision 33778)
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