dma(8): Upgrade to v0.7.
[dragonfly.git] / libexec / dma / dns.c
CommitLineData
3021968a
SS
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
47static int
48sort_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
63static int
64add_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;
3021968a
SS
69 const int count_inc = 10;
70 int err;
71
3021968a
SS
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 err = getaddrinfo(host, servname, &hints, &res0);
79 if (err)
c8b07ee5 80 return (err == EAI_AGAIN ? 1 : -1);
3021968a
SS
81
82 for (res = res0; res != NULL; res = res->ai_next) {
83 if (*ps + 1 >= roundup(*ps, count_inc)) {
84 size_t newsz = roundup(*ps + 2, count_inc);
85 *he = reallocf(*he, newsz * sizeof(**he));
86 if (*he == NULL)
87 goto out;
88 }
89
90 p = &(*he)[*ps];
91 strlcpy(p->host, host, sizeof(p->host));
92 p->pref = pref;
93 p->ai = *res;
94 p->ai.ai_addr = NULL;
95 bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen);
96
725a35c6 97 getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen,
3021968a
SS
98 p->addr, sizeof(p->addr),
99 NULL, 0, NI_NUMERICHOST);
100
101 (*ps)++;
102 }
103 freeaddrinfo(res0);
104
c8b07ee5 105 return (0);
3021968a
SS
106
107out:
108 if (res0 != NULL)
109 freeaddrinfo(res0);
c8b07ee5 110 return (1);
3021968a
SS
111}
112
113int
114dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx)
115{
116 char outname[MAXDNAME];
117 ns_msg msg;
118 ns_rr rr;
119 const char *searchhost;
c8b07ee5
SW
120 const unsigned char *cp;
121 unsigned char *ans;
3021968a
SS
122 struct mx_hostentry *hosts = NULL;
123 size_t nhosts = 0;
124 size_t anssz;
125 int pref;
126 int cname_recurse;
c8b07ee5 127 int have_mx = 0;
3021968a
SS
128 int err;
129 int i;
130
131 res_init();
132 searchhost = host;
133 cname_recurse = 0;
134
135 anssz = 65536;
136 ans = malloc(anssz);
137 if (ans == NULL)
138 return (1);
139
140 if (no_mx)
141 goto out;
142
143repeat:
144 err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
145 if (err < 0) {
146 switch (h_errno) {
147 case NO_DATA:
148 /*
149 * Host exists, but no MX (or CNAME) entry.
150 * Not an error, use host name instead.
151 */
152 goto out;
153 case TRY_AGAIN:
154 /* transient error */
155 goto transerr;
156 case NO_RECOVERY:
157 case HOST_NOT_FOUND:
158 default:
159 errno = ENOENT;
160 goto err;
161 }
162 }
163
164 if (!ns_initparse(ans, anssz, &msg))
165 goto transerr;
166
167 switch (ns_msg_getflag(msg, ns_f_rcode)) {
168 case ns_r_noerror:
169 break;
170 case ns_r_nxdomain:
171 goto err;
172 default:
173 goto transerr;
174 }
175
176 for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
177 if (ns_parserr(&msg, ns_s_an, i, &rr))
178 goto transerr;
179
c8b07ee5 180 cp = ns_rr_rdata(rr);
3021968a
SS
181
182 switch (ns_rr_type(rr)) {
183 case ns_t_mx:
c8b07ee5 184 have_mx = 1;
3021968a
SS
185 pref = ns_get16(cp);
186 cp += 2;
187 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
188 cp, outname, sizeof(outname));
189 if (err < 0)
190 goto transerr;
191
c8b07ee5
SW
192 err = add_host(pref, outname, port, &hosts, &nhosts);
193 if (err == -1)
194 goto err;
3021968a
SS
195 break;
196
197 case ns_t_cname:
198 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
199 cp, outname, sizeof(outname));
200 if (err < 0)
201 goto transerr;
202
203 /* Prevent a CNAME loop */
204 if (cname_recurse++ > 10)
205 goto err;
206
207 searchhost = outname;
208 goto repeat;
209
210 default:
211 break;
212 }
213 }
214
215out:
216 err = 0;
217 if (0) {
218transerr:
219 if (nhosts == 0)
220 err = 1;
221 }
222 if (0) {
223err:
224 err = -1;
225 }
226
227 free(ans);
228
c8b07ee5
SW
229 if (err == 0) {
230 if (!have_mx) {
231 /*
232 * If we didn't find any MX, use the hostname instead.
233 */
234 err = add_host(0, host, port, &hosts, &nhosts);
235 } else if (nhosts == 0) {
236 /*
237 * We did get MX, but couldn't resolve any of them
238 * due to transient errors.
239 */
240 err = 1;
241 }
3021968a
SS
242 }
243
244 if (nhosts > 0) {
c8b07ee5 245 qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
3021968a
SS
246 /* terminate list */
247 *hosts[nhosts].host = 0;
248 } else {
249 if (hosts != NULL)
250 free(hosts);
251 hosts = NULL;
252 }
253
254 *he = hosts;
255 return (err);
256
257 free(ans);
258 if (hosts != NULL)
259 free(hosts);
260 return (err);
261}
262
263#if defined(TESTING)
264int
265main(int argc, char **argv)
266{
267 struct mx_hostentry *he, *p;
268 int err;
269
270 err = dns_get_mx_list(argv[1], 53, &he, 0);
271 if (err)
272 return (err);
273
274 for (p = he; *p->host != 0; p++) {
275 printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
276 }
277
278 return (0);
279}
280#endif