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