]> git.ipfire.org Git - people/ms/dma.git/blob - dns.c
Make add_host() really return an error code.
[people/ms/dma.git] / 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 (err == EAI_AGAIN ? 1 : -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 (0);
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 unsigned char *cp;
124 unsigned 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 have_mx = 0;
131 int err;
132 int i;
133
134 res_init();
135 searchhost = host;
136 cname_recurse = 0;
137
138 anssz = 65536;
139 ans = malloc(anssz);
140 if (ans == NULL)
141 return (1);
142
143 if (no_mx)
144 goto out;
145
146 repeat:
147 err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
148 if (err < 0) {
149 switch (h_errno) {
150 case NO_DATA:
151 /*
152 * Host exists, but no MX (or CNAME) entry.
153 * Not an error, use host name instead.
154 */
155 goto out;
156 case TRY_AGAIN:
157 /* transient error */
158 goto transerr;
159 case NO_RECOVERY:
160 case HOST_NOT_FOUND:
161 default:
162 errno = ENOENT;
163 goto err;
164 }
165 }
166
167 if (!ns_initparse(ans, anssz, &msg))
168 goto transerr;
169
170 switch (ns_msg_getflag(msg, ns_f_rcode)) {
171 case ns_r_noerror:
172 break;
173 case ns_r_nxdomain:
174 goto err;
175 default:
176 goto transerr;
177 }
178
179 for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
180 if (ns_parserr(&msg, ns_s_an, i, &rr))
181 goto transerr;
182
183 cp = ns_rr_rdata(rr);
184
185 switch (ns_rr_type(rr)) {
186 case ns_t_mx:
187 have_mx = 1;
188 pref = ns_get16(cp);
189 cp += 2;
190 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
191 cp, outname, sizeof(outname));
192 if (err < 0)
193 goto transerr;
194
195 err = add_host(pref, outname, port, &hosts, &nhosts);
196 if (err == -1)
197 goto err;
198 break;
199
200 case ns_t_cname:
201 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
202 cp, outname, sizeof(outname));
203 if (err < 0)
204 goto transerr;
205
206 /* Prevent a CNAME loop */
207 if (cname_recurse++ > 10)
208 goto err;
209
210 searchhost = outname;
211 goto repeat;
212
213 default:
214 break;
215 }
216 }
217
218 out:
219 err = 0;
220 if (0) {
221 transerr:
222 if (nhosts == 0)
223 err = 1;
224 }
225 if (0) {
226 err:
227 err = -1;
228 }
229
230 free(ans);
231
232 if (err == 0) {
233 if (!have_mx) {
234 /*
235 * If we didn't find any MX, use the hostname instead.
236 */
237 err = add_host(0, host, port, &hosts, &nhosts);
238 } else if (nhosts == 0) {
239 /*
240 * We did get MX, but couldn't resolve any of them
241 * due to transient errors.
242 */
243 err = 1;
244 }
245 }
246
247 if (nhosts > 0) {
248 qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
249 /* terminate list */
250 *hosts[nhosts].host = 0;
251 } else {
252 if (hosts != NULL)
253 free(hosts);
254 hosts = NULL;
255 }
256
257 *he = hosts;
258 return (err);
259
260 free(ans);
261 if (hosts != NULL)
262 free(hosts);
263 return (err);
264 }
265
266 #if defined(TESTING)
267 int
268 main(int argc, char **argv)
269 {
270 struct mx_hostentry *he, *p;
271 int err;
272
273 err = dns_get_mx_list(argv[1], 53, &he, 0);
274 if (err)
275 return (err);
276
277 for (p = he; *p->host != 0; p++) {
278 printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
279 }
280
281 return (0);
282 }
283 #endif