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