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