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