1 /*
2  * Copyright (c) 1983 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 provided
7  * that: (1) source distributions retain this entire copyright notice and
8  * comment, and (2) distributions including binaries display the following
9  * acknowledgement:  ``This product includes software developed by the
10  * University of California, Berkeley and its contributors'' in the
11  * documentation or other materials provided with the distribution and in
12  * all advertising materials mentioning features or use of this software.
13  * Neither the name of the University nor the names of its contributors may
14  * be used to endorse or promote products derived from this software without
15  * specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20 
21 #ifndef lint
22 static char Version[] = "@(#)mxrr.c	e07@nikhef.nl (Eric Wassenaar) 990522";
23 #endif
24 
25 #include "vrfy.h"
26 
27 extern int verbose;
28 extern int debug;
29 
30 #if PACKETSZ > 8192
31 #define MAXPACKET PACKETSZ	/* PACKETSZ should be the max udp size (512) */
32 #else
33 #define MAXPACKET 8192		/* but tcp packets can be considerably larger */
34 #endif
35 
36 typedef union {
37 	HEADER hdr;
38 	u_char buf[MAXPACKET];
39 } querybuf;
40 
41 #ifndef HFIXEDSZ
42 #define HFIXEDSZ 12		/* actually sizeof(HEADER) */
43 #endif
44 
45 #define MAXMXBUFSIZ (MAXMXHOSTS * (MAXHOST+1))
46 
47 static char hostbuf[MAXMXBUFSIZ];
48 
49 char *MxHosts[MAXMXHOSTS];	/* list of names of mx hosts found */
50 
51 char *dbprefix = DBPREFIX;	/* prefix for debug messages to stdout */
52 
53 /*
54 ** GETMXBYNAME -- Fetch mx hosts for a domain
55 ** ------------------------------------------
56 **
57 **	Returns:
58 **		Number of mx hosts found.
59 **
60 **	Outputs:
61 **		The global array MxHosts contains the mx names.
62 */
63 
64 int
getmxbyname(domain)65 getmxbyname(domain)
66 char *domain;				/* domain to get mx hosts for */
67 {
68 	querybuf answer;		/* answer buffer from nameserver */
69 	HEADER *hp;			/* answer buffer header */
70 	int ancount, qdcount;		/* answer count and query count */
71 	u_char *msg, *eom, *cp;		/* answer buffer positions */
72 	int type, class, dlen;		/* record type, class and length */
73 	u_short pref;			/* mx preference value */
74 	u_short prefer[MAXMXHOSTS];	/* saved preferences of mx records */
75 	char *bp;			/* hostbuf pointer */
76 	int nmx;			/* number of mx hosts found */
77 	register int i;
78 	register int j;
79 	register int n;
80 
81 /*
82  * Query the nameserver to retrieve mx records for the given domain.
83  */
84 	errno = 0;			/* reset before querying nameserver */
85 	h_errno = 0;
86 
87 	n = res_search(domain, C_IN, T_MX, (u_char *)&answer, sizeof(answer));
88 	if (n < 0)
89 	{
90 		if (_res.options & RES_DEBUG)
91 			printf("%sres_search failed\n", dbprefix);
92 		return(0);
93 	}
94 
95 	errno = 0;			/* reset after we got an answer */
96 
97 	if (n < HFIXEDSZ)
98 	{
99 		h_errno = NO_RECOVERY;
100 		return(0);
101 	}
102 
103 	/* avoid problems after truncation in tcp packets */
104 	if (n > sizeof(answer))
105 		n = sizeof(answer);
106 
107 /*
108  * Valid answer received. Skip the query record.
109  */
110 	hp = (HEADER *)&answer;
111 	qdcount = ntohs((u_short)hp->qdcount);
112 	ancount = ntohs((u_short)hp->ancount);
113 
114 	msg = (u_char *)&answer;
115 	eom = (u_char *)&answer + n;
116 	cp  = (u_char *)&answer + HFIXEDSZ;
117 
118 	while (qdcount-- > 0 && cp < eom)
119 	{
120 		n = dn_skipname(cp, eom);
121 		if (n < 0)
122 			return(0);
123 		cp += n;
124 		cp += QFIXEDSZ;
125 	}
126 
127 /*
128  * Loop through the answer buffer and extract mx records.
129  */
130 	nmx = 0;
131 	bp = hostbuf;
132 
133 	while (ancount-- > 0 && cp < eom && nmx < MAXMXHOSTS)
134 	{
135 #ifdef obsolete
136 		if (verbose >= 4 || debug)
137 			(void) p_rr((qbuf_t *)cp, (qbuf_t *)msg, stdout);
138 #endif /*obsolete*/
139 
140 		n = dn_expand(msg, eom, cp, (nbuf_t *)bp, MAXHOST);
141 		if (n < 0)
142 			break;
143 		cp += n;
144 
145 		type = _getshort(cp);
146  		cp += INT16SZ;
147 
148 		class = _getshort(cp);
149  		cp += INT16SZ;
150 
151 		/* ttl = _getlong(cp); */
152  		cp += INT32SZ;
153 
154 		dlen = _getshort(cp);
155 		cp += INT16SZ;
156 
157 		if (type != T_MX || class != C_IN)
158 		{
159 			cp += dlen;
160 			continue;
161 		}
162 
163 		pref = _getshort(cp);
164 		cp += INT16SZ;
165 
166 		n = dn_expand(msg, eom, cp, (nbuf_t *)bp, MAXHOST);
167 		if (n < 0)
168 			break;
169 		cp += n;
170 
171 		prefer[nmx] = pref;
172 		MxHosts[nmx] = bp;
173 		nmx++;
174 
175 		n = strlength(bp) + 1;
176 		bp += n;
177 	}
178 
179 /*
180  * Sort all records by preference.
181  */
182 	for (i = 0; i < nmx; i++)
183 	{
184 		for (j = i + 1; j < nmx; j++)
185 		{
186 			if (prefer[i] > prefer[j])
187 			{
188 				register u_short tmppref;
189 				register char *tmphost;
190 
191 				tmppref = prefer[i];
192 				prefer[i] = prefer[j];
193 				prefer[j] = tmppref;
194 
195 				tmphost = MxHosts[i];
196 				MxHosts[i] = MxHosts[j];
197 				MxHosts[j] = tmphost;
198 			}
199 		}
200 	}
201 
202 	return(nmx);
203 }
204