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