xref: /freebsd/contrib/dma/dns.c (revision a9e8641da961bcf3d24afc85fd657f2083a872a2)
1*a9e8641dSBaptiste Daroussin /*
2*a9e8641dSBaptiste Daroussin  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3*a9e8641dSBaptiste Daroussin  *
4*a9e8641dSBaptiste Daroussin  * This code is derived from software contributed to The DragonFly Project
5*a9e8641dSBaptiste Daroussin  * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
6*a9e8641dSBaptiste Daroussin  *
7*a9e8641dSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
8*a9e8641dSBaptiste Daroussin  * modification, are permitted provided that the following conditions
9*a9e8641dSBaptiste Daroussin  * are met:
10*a9e8641dSBaptiste Daroussin  *
11*a9e8641dSBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
12*a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
13*a9e8641dSBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
14*a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in
15*a9e8641dSBaptiste Daroussin  *    the documentation and/or other materials provided with the
16*a9e8641dSBaptiste Daroussin  *    distribution.
17*a9e8641dSBaptiste Daroussin  * 3. Neither the name of The DragonFly Project nor the names of its
18*a9e8641dSBaptiste Daroussin  *    contributors may be used to endorse or promote products derived
19*a9e8641dSBaptiste Daroussin  *    from this software without specific, prior written permission.
20*a9e8641dSBaptiste Daroussin  *
21*a9e8641dSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*a9e8641dSBaptiste Daroussin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*a9e8641dSBaptiste Daroussin  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24*a9e8641dSBaptiste Daroussin  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25*a9e8641dSBaptiste Daroussin  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26*a9e8641dSBaptiste Daroussin  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27*a9e8641dSBaptiste Daroussin  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28*a9e8641dSBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29*a9e8641dSBaptiste Daroussin  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30*a9e8641dSBaptiste Daroussin  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31*a9e8641dSBaptiste Daroussin  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*a9e8641dSBaptiste Daroussin  * SUCH DAMAGE.
33*a9e8641dSBaptiste Daroussin  */
34*a9e8641dSBaptiste Daroussin 
35*a9e8641dSBaptiste Daroussin #include <sys/types.h>
36*a9e8641dSBaptiste Daroussin #include <netinet/in.h>
37*a9e8641dSBaptiste Daroussin #include <arpa/inet.h>
38*a9e8641dSBaptiste Daroussin #include <arpa/nameser.h>
39*a9e8641dSBaptiste Daroussin #include <errno.h>
40*a9e8641dSBaptiste Daroussin #include <netdb.h>
41*a9e8641dSBaptiste Daroussin #include <resolv.h>
42*a9e8641dSBaptiste Daroussin #include <string.h>
43*a9e8641dSBaptiste Daroussin #include <stdlib.h>
44*a9e8641dSBaptiste Daroussin 
45*a9e8641dSBaptiste Daroussin #include "dma.h"
46*a9e8641dSBaptiste Daroussin 
47*a9e8641dSBaptiste Daroussin static int
48*a9e8641dSBaptiste Daroussin sort_pref(const void *a, const void *b)
49*a9e8641dSBaptiste Daroussin {
50*a9e8641dSBaptiste Daroussin 	const struct mx_hostentry *ha = a, *hb = b;
51*a9e8641dSBaptiste Daroussin 	int v;
52*a9e8641dSBaptiste Daroussin 
53*a9e8641dSBaptiste Daroussin 	/* sort increasing by preference primarily */
54*a9e8641dSBaptiste Daroussin 	v = ha->pref - hb->pref;
55*a9e8641dSBaptiste Daroussin 	if (v != 0)
56*a9e8641dSBaptiste Daroussin 		return (v);
57*a9e8641dSBaptiste Daroussin 
58*a9e8641dSBaptiste Daroussin 	/* sort PF_INET6 before PF_INET */
59*a9e8641dSBaptiste Daroussin 	v = - (ha->ai.ai_family - hb->ai.ai_family);
60*a9e8641dSBaptiste Daroussin 	return (v);
61*a9e8641dSBaptiste Daroussin }
62*a9e8641dSBaptiste Daroussin 
63*a9e8641dSBaptiste Daroussin static int
64*a9e8641dSBaptiste Daroussin add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps)
65*a9e8641dSBaptiste Daroussin {
66*a9e8641dSBaptiste Daroussin 	struct addrinfo hints, *res, *res0 = NULL;
67*a9e8641dSBaptiste Daroussin 	char servname[10];
68*a9e8641dSBaptiste Daroussin 	struct mx_hostentry *p;
69*a9e8641dSBaptiste Daroussin 	const int count_inc = 10;
70*a9e8641dSBaptiste Daroussin 	int err;
71*a9e8641dSBaptiste Daroussin 
72*a9e8641dSBaptiste Daroussin 	memset(&hints, 0, sizeof(hints));
73*a9e8641dSBaptiste Daroussin 	hints.ai_family = PF_UNSPEC;
74*a9e8641dSBaptiste Daroussin 	hints.ai_socktype = SOCK_STREAM;
75*a9e8641dSBaptiste Daroussin 	hints.ai_protocol = IPPROTO_TCP;
76*a9e8641dSBaptiste Daroussin 
77*a9e8641dSBaptiste Daroussin 	snprintf(servname, sizeof(servname), "%d", port);
78*a9e8641dSBaptiste Daroussin 	err = getaddrinfo(host, servname, &hints, &res0);
79*a9e8641dSBaptiste Daroussin 	if (err)
80*a9e8641dSBaptiste Daroussin 		return (err == EAI_AGAIN ? 1 : -1);
81*a9e8641dSBaptiste Daroussin 
82*a9e8641dSBaptiste Daroussin 	for (res = res0; res != NULL; res = res->ai_next) {
83*a9e8641dSBaptiste Daroussin 		if (*ps + 1 >= roundup(*ps, count_inc)) {
84*a9e8641dSBaptiste Daroussin 			size_t newsz = roundup(*ps + 2, count_inc);
85*a9e8641dSBaptiste Daroussin 			*he = reallocf(*he, newsz * sizeof(**he));
86*a9e8641dSBaptiste Daroussin 			if (*he == NULL)
87*a9e8641dSBaptiste Daroussin 				goto out;
88*a9e8641dSBaptiste Daroussin 		}
89*a9e8641dSBaptiste Daroussin 
90*a9e8641dSBaptiste Daroussin 		p = &(*he)[*ps];
91*a9e8641dSBaptiste Daroussin 		strlcpy(p->host, host, sizeof(p->host));
92*a9e8641dSBaptiste Daroussin 		p->pref = pref;
93*a9e8641dSBaptiste Daroussin 		p->ai = *res;
94*a9e8641dSBaptiste Daroussin 		p->ai.ai_addr = NULL;
95*a9e8641dSBaptiste Daroussin 		bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen);
96*a9e8641dSBaptiste Daroussin 
97*a9e8641dSBaptiste Daroussin 		getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen,
98*a9e8641dSBaptiste Daroussin 			    p->addr, sizeof(p->addr),
99*a9e8641dSBaptiste Daroussin 			    NULL, 0, NI_NUMERICHOST);
100*a9e8641dSBaptiste Daroussin 
101*a9e8641dSBaptiste Daroussin 		(*ps)++;
102*a9e8641dSBaptiste Daroussin 	}
103*a9e8641dSBaptiste Daroussin 	freeaddrinfo(res0);
104*a9e8641dSBaptiste Daroussin 
105*a9e8641dSBaptiste Daroussin 	return (0);
106*a9e8641dSBaptiste Daroussin 
107*a9e8641dSBaptiste Daroussin out:
108*a9e8641dSBaptiste Daroussin 	if (res0 != NULL)
109*a9e8641dSBaptiste Daroussin 		freeaddrinfo(res0);
110*a9e8641dSBaptiste Daroussin 	return (1);
111*a9e8641dSBaptiste Daroussin }
112*a9e8641dSBaptiste Daroussin 
113*a9e8641dSBaptiste Daroussin int
114*a9e8641dSBaptiste Daroussin dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx)
115*a9e8641dSBaptiste Daroussin {
116*a9e8641dSBaptiste Daroussin 	char outname[MAXDNAME];
117*a9e8641dSBaptiste Daroussin 	ns_msg msg;
118*a9e8641dSBaptiste Daroussin 	ns_rr rr;
119*a9e8641dSBaptiste Daroussin 	const char *searchhost;
120*a9e8641dSBaptiste Daroussin 	const unsigned char *cp;
121*a9e8641dSBaptiste Daroussin 	unsigned char *ans;
122*a9e8641dSBaptiste Daroussin 	struct mx_hostentry *hosts = NULL;
123*a9e8641dSBaptiste Daroussin 	size_t nhosts = 0;
124*a9e8641dSBaptiste Daroussin 	size_t anssz;
125*a9e8641dSBaptiste Daroussin 	int pref;
126*a9e8641dSBaptiste Daroussin 	int cname_recurse;
127*a9e8641dSBaptiste Daroussin 	int have_mx = 0;
128*a9e8641dSBaptiste Daroussin 	int err;
129*a9e8641dSBaptiste Daroussin 	int i;
130*a9e8641dSBaptiste Daroussin 
131*a9e8641dSBaptiste Daroussin 	res_init();
132*a9e8641dSBaptiste Daroussin 	searchhost = host;
133*a9e8641dSBaptiste Daroussin 	cname_recurse = 0;
134*a9e8641dSBaptiste Daroussin 
135*a9e8641dSBaptiste Daroussin 	anssz = 65536;
136*a9e8641dSBaptiste Daroussin 	ans = malloc(anssz);
137*a9e8641dSBaptiste Daroussin 	if (ans == NULL)
138*a9e8641dSBaptiste Daroussin 		return (1);
139*a9e8641dSBaptiste Daroussin 
140*a9e8641dSBaptiste Daroussin 	if (no_mx)
141*a9e8641dSBaptiste Daroussin 		goto out;
142*a9e8641dSBaptiste Daroussin 
143*a9e8641dSBaptiste Daroussin repeat:
144*a9e8641dSBaptiste Daroussin 	err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
145*a9e8641dSBaptiste Daroussin 	if (err < 0) {
146*a9e8641dSBaptiste Daroussin 		switch (h_errno) {
147*a9e8641dSBaptiste Daroussin 		case NO_DATA:
148*a9e8641dSBaptiste Daroussin 			/*
149*a9e8641dSBaptiste Daroussin 			 * Host exists, but no MX (or CNAME) entry.
150*a9e8641dSBaptiste Daroussin 			 * Not an error, use host name instead.
151*a9e8641dSBaptiste Daroussin 			 */
152*a9e8641dSBaptiste Daroussin 			goto out;
153*a9e8641dSBaptiste Daroussin 		case TRY_AGAIN:
154*a9e8641dSBaptiste Daroussin 			/* transient error */
155*a9e8641dSBaptiste Daroussin 			goto transerr;
156*a9e8641dSBaptiste Daroussin 		case NO_RECOVERY:
157*a9e8641dSBaptiste Daroussin 		case HOST_NOT_FOUND:
158*a9e8641dSBaptiste Daroussin 		default:
159*a9e8641dSBaptiste Daroussin 			errno = ENOENT;
160*a9e8641dSBaptiste Daroussin 			goto err;
161*a9e8641dSBaptiste Daroussin 		}
162*a9e8641dSBaptiste Daroussin 	}
163*a9e8641dSBaptiste Daroussin 
164*a9e8641dSBaptiste Daroussin 	if (!ns_initparse(ans, anssz, &msg))
165*a9e8641dSBaptiste Daroussin 		goto transerr;
166*a9e8641dSBaptiste Daroussin 
167*a9e8641dSBaptiste Daroussin 	switch (ns_msg_getflag(msg, ns_f_rcode)) {
168*a9e8641dSBaptiste Daroussin 	case ns_r_noerror:
169*a9e8641dSBaptiste Daroussin 		break;
170*a9e8641dSBaptiste Daroussin 	case ns_r_nxdomain:
171*a9e8641dSBaptiste Daroussin 		goto err;
172*a9e8641dSBaptiste Daroussin 	default:
173*a9e8641dSBaptiste Daroussin 		goto transerr;
174*a9e8641dSBaptiste Daroussin 	}
175*a9e8641dSBaptiste Daroussin 
176*a9e8641dSBaptiste Daroussin 	for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
177*a9e8641dSBaptiste Daroussin 		if (ns_parserr(&msg, ns_s_an, i, &rr))
178*a9e8641dSBaptiste Daroussin 			goto transerr;
179*a9e8641dSBaptiste Daroussin 
180*a9e8641dSBaptiste Daroussin 		cp = ns_rr_rdata(rr);
181*a9e8641dSBaptiste Daroussin 
182*a9e8641dSBaptiste Daroussin 		switch (ns_rr_type(rr)) {
183*a9e8641dSBaptiste Daroussin 		case ns_t_mx:
184*a9e8641dSBaptiste Daroussin 			have_mx = 1;
185*a9e8641dSBaptiste Daroussin 			pref = ns_get16(cp);
186*a9e8641dSBaptiste Daroussin 			cp += 2;
187*a9e8641dSBaptiste Daroussin 			err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
188*a9e8641dSBaptiste Daroussin 						 cp, outname, sizeof(outname));
189*a9e8641dSBaptiste Daroussin 			if (err < 0)
190*a9e8641dSBaptiste Daroussin 				goto transerr;
191*a9e8641dSBaptiste Daroussin 
192*a9e8641dSBaptiste Daroussin 			err = add_host(pref, outname, port, &hosts, &nhosts);
193*a9e8641dSBaptiste Daroussin 			if (err == -1)
194*a9e8641dSBaptiste Daroussin 				goto err;
195*a9e8641dSBaptiste Daroussin 			break;
196*a9e8641dSBaptiste Daroussin 
197*a9e8641dSBaptiste Daroussin 		case ns_t_cname:
198*a9e8641dSBaptiste Daroussin 			err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
199*a9e8641dSBaptiste Daroussin 						 cp, outname, sizeof(outname));
200*a9e8641dSBaptiste Daroussin 			if (err < 0)
201*a9e8641dSBaptiste Daroussin 				goto transerr;
202*a9e8641dSBaptiste Daroussin 
203*a9e8641dSBaptiste Daroussin 			/* Prevent a CNAME loop */
204*a9e8641dSBaptiste Daroussin 			if (cname_recurse++ > 10)
205*a9e8641dSBaptiste Daroussin 				goto err;
206*a9e8641dSBaptiste Daroussin 
207*a9e8641dSBaptiste Daroussin 			searchhost = outname;
208*a9e8641dSBaptiste Daroussin 			goto repeat;
209*a9e8641dSBaptiste Daroussin 
210*a9e8641dSBaptiste Daroussin 		default:
211*a9e8641dSBaptiste Daroussin 			break;
212*a9e8641dSBaptiste Daroussin 		}
213*a9e8641dSBaptiste Daroussin 	}
214*a9e8641dSBaptiste Daroussin 
215*a9e8641dSBaptiste Daroussin out:
216*a9e8641dSBaptiste Daroussin 	err = 0;
217*a9e8641dSBaptiste Daroussin 	if (0) {
218*a9e8641dSBaptiste Daroussin transerr:
219*a9e8641dSBaptiste Daroussin 		if (nhosts == 0)
220*a9e8641dSBaptiste Daroussin 			err = 1;
221*a9e8641dSBaptiste Daroussin 	}
222*a9e8641dSBaptiste Daroussin 	if (0) {
223*a9e8641dSBaptiste Daroussin err:
224*a9e8641dSBaptiste Daroussin 		err = -1;
225*a9e8641dSBaptiste Daroussin 	}
226*a9e8641dSBaptiste Daroussin 
227*a9e8641dSBaptiste Daroussin 	free(ans);
228*a9e8641dSBaptiste Daroussin 
229*a9e8641dSBaptiste Daroussin 	if (err == 0) {
230*a9e8641dSBaptiste Daroussin 		if (!have_mx) {
231*a9e8641dSBaptiste Daroussin 			/*
232*a9e8641dSBaptiste Daroussin 			 * If we didn't find any MX, use the hostname instead.
233*a9e8641dSBaptiste Daroussin 			 */
234*a9e8641dSBaptiste Daroussin 			err = add_host(0, host, port, &hosts, &nhosts);
235*a9e8641dSBaptiste Daroussin 		} else if (nhosts == 0) {
236*a9e8641dSBaptiste Daroussin 			/*
237*a9e8641dSBaptiste Daroussin 			 * We did get MX, but couldn't resolve any of them
238*a9e8641dSBaptiste Daroussin 			 * due to transient errors.
239*a9e8641dSBaptiste Daroussin 			 */
240*a9e8641dSBaptiste Daroussin 			err = 1;
241*a9e8641dSBaptiste Daroussin 		}
242*a9e8641dSBaptiste Daroussin 	}
243*a9e8641dSBaptiste Daroussin 
244*a9e8641dSBaptiste Daroussin 	if (nhosts > 0) {
245*a9e8641dSBaptiste Daroussin 		qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
246*a9e8641dSBaptiste Daroussin 		/* terminate list */
247*a9e8641dSBaptiste Daroussin 		*hosts[nhosts].host = 0;
248*a9e8641dSBaptiste Daroussin 	} else {
249*a9e8641dSBaptiste Daroussin 		if (hosts != NULL)
250*a9e8641dSBaptiste Daroussin 			free(hosts);
251*a9e8641dSBaptiste Daroussin 		hosts = NULL;
252*a9e8641dSBaptiste Daroussin 	}
253*a9e8641dSBaptiste Daroussin 
254*a9e8641dSBaptiste Daroussin 	*he = hosts;
255*a9e8641dSBaptiste Daroussin 	return (err);
256*a9e8641dSBaptiste Daroussin 
257*a9e8641dSBaptiste Daroussin 	free(ans);
258*a9e8641dSBaptiste Daroussin 	if (hosts != NULL)
259*a9e8641dSBaptiste Daroussin 		free(hosts);
260*a9e8641dSBaptiste Daroussin 	return (err);
261*a9e8641dSBaptiste Daroussin }
262*a9e8641dSBaptiste Daroussin 
263*a9e8641dSBaptiste Daroussin #if defined(TESTING)
264*a9e8641dSBaptiste Daroussin int
265*a9e8641dSBaptiste Daroussin main(int argc, char **argv)
266*a9e8641dSBaptiste Daroussin {
267*a9e8641dSBaptiste Daroussin 	struct mx_hostentry *he, *p;
268*a9e8641dSBaptiste Daroussin 	int err;
269*a9e8641dSBaptiste Daroussin 
270*a9e8641dSBaptiste Daroussin 	err = dns_get_mx_list(argv[1], 53, &he, 0);
271*a9e8641dSBaptiste Daroussin 	if (err)
272*a9e8641dSBaptiste Daroussin 		return (err);
273*a9e8641dSBaptiste Daroussin 
274*a9e8641dSBaptiste Daroussin 	for (p = he; *p->host != 0; p++) {
275*a9e8641dSBaptiste Daroussin 		printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
276*a9e8641dSBaptiste Daroussin 	}
277*a9e8641dSBaptiste Daroussin 
278*a9e8641dSBaptiste Daroussin 	return (0);
279*a9e8641dSBaptiste Daroussin }
280*a9e8641dSBaptiste Daroussin #endif
281