Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/dma/dns.c
39475 views
1
/*
2
* Copyright (c) 2008-2014, Simon Schubert <[email protected]>.
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 <[email protected]>.
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 <sys/param.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#include <arpa/nameser.h>
41
#include <errno.h>
42
#include <netdb.h>
43
#include <resolv.h>
44
#include <string.h>
45
#include <stdlib.h>
46
47
#include "dma.h"
48
49
static int
50
sort_pref(const void *a, const void *b)
51
{
52
const struct mx_hostentry *ha = a, *hb = b;
53
int v;
54
55
/* sort increasing by preference primarily */
56
v = ha->pref - hb->pref;
57
if (v != 0)
58
return (v);
59
60
/* sort PF_INET6 before PF_INET */
61
v = - (ha->ai.ai_family - hb->ai.ai_family);
62
return (v);
63
}
64
65
static int
66
add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps)
67
{
68
struct addrinfo hints, *res, *res0 = NULL;
69
char servname[10];
70
struct mx_hostentry *p;
71
const int count_inc = 10;
72
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
switch (getaddrinfo(host, servname, &hints, &res0)) {
80
case 0:
81
break;
82
case EAI_AGAIN:
83
case EAI_NONAME:
84
/*
85
* EAI_NONAME gets returned for:
86
* SMARTHOST set but DNS server not reachable -> defer
87
* SMARTHOST set but DNS server returns "host does not exist"
88
* -> buggy configuration
89
* -> either defer or bounce would be ok -> defer
90
* MX entry was returned by DNS server but name doesn't resolve
91
* -> hopefully transient situation -> defer
92
* all other DNS problems should have been caught earlier
93
* in dns_get_mx_list().
94
*/
95
goto out;
96
default:
97
return(-1);
98
}
99
100
for (res = res0; res != NULL; res = res->ai_next) {
101
if (*ps + 1 >= roundup(*ps, count_inc)) {
102
size_t newsz = roundup(*ps + 2, count_inc);
103
*he = reallocf(*he, newsz * sizeof(**he));
104
if (*he == NULL)
105
goto out;
106
}
107
108
p = &(*he)[*ps];
109
strlcpy(p->host, host, sizeof(p->host));
110
p->pref = pref;
111
p->ai = *res;
112
p->ai.ai_addr = NULL;
113
bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen);
114
115
getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen,
116
p->addr, sizeof(p->addr),
117
NULL, 0, NI_NUMERICHOST);
118
119
(*ps)++;
120
}
121
freeaddrinfo(res0);
122
123
return (0);
124
125
out:
126
if (res0 != NULL)
127
freeaddrinfo(res0);
128
return (1);
129
}
130
131
int
132
dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx)
133
{
134
char outname[MAXDNAME];
135
ns_msg msg;
136
ns_rr rr;
137
const char *searchhost;
138
const unsigned char *cp;
139
unsigned char *ans;
140
struct mx_hostentry *hosts = NULL;
141
size_t nhosts = 0;
142
size_t anssz;
143
int pref;
144
int cname_recurse;
145
int have_mx = 0;
146
int err;
147
int i;
148
149
res_init();
150
searchhost = host;
151
cname_recurse = 0;
152
153
anssz = 65536;
154
ans = malloc(anssz);
155
if (ans == NULL)
156
return (1);
157
158
if (no_mx)
159
goto out;
160
161
repeat:
162
err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
163
if (err < 0) {
164
switch (h_errno) {
165
case NO_DATA:
166
/*
167
* Host exists, but no MX (or CNAME) entry.
168
* Not an error, use host name instead.
169
*/
170
goto out;
171
case TRY_AGAIN:
172
/* transient error */
173
goto transerr;
174
case NO_RECOVERY:
175
case HOST_NOT_FOUND:
176
default:
177
errno = ENOENT;
178
goto err;
179
}
180
}
181
182
if (!ns_initparse(ans, anssz, &msg))
183
goto transerr;
184
185
switch (ns_msg_getflag(msg, ns_f_rcode)) {
186
case ns_r_noerror:
187
break;
188
case ns_r_nxdomain:
189
goto err;
190
default:
191
goto transerr;
192
}
193
194
for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
195
if (ns_parserr(&msg, ns_s_an, i, &rr))
196
goto transerr;
197
198
cp = ns_rr_rdata(rr);
199
200
switch (ns_rr_type(rr)) {
201
case ns_t_mx:
202
have_mx = 1;
203
pref = ns_get16(cp);
204
cp += 2;
205
err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
206
cp, outname, sizeof(outname));
207
if (err < 0)
208
goto transerr;
209
210
err = add_host(pref, outname, port, &hosts, &nhosts);
211
if (err == -1)
212
goto err;
213
break;
214
215
case ns_t_cname:
216
err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
217
cp, outname, sizeof(outname));
218
if (err < 0)
219
goto transerr;
220
221
/* Prevent a CNAME loop */
222
if (cname_recurse++ > 10)
223
goto err;
224
225
searchhost = outname;
226
goto repeat;
227
228
default:
229
break;
230
}
231
}
232
233
out:
234
err = 0;
235
if (0) {
236
transerr:
237
if (nhosts == 0)
238
err = 1;
239
}
240
if (0) {
241
err:
242
err = -1;
243
}
244
245
free(ans);
246
247
if (err == 0) {
248
if (!have_mx) {
249
/*
250
* If we didn't find any MX, use the hostname instead.
251
*/
252
err = add_host(0, host, port, &hosts, &nhosts);
253
} else if (nhosts == 0) {
254
/*
255
* We did get MX, but couldn't resolve any of them
256
* due to transient errors.
257
*/
258
err = 1;
259
}
260
}
261
262
if (nhosts > 0) {
263
qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
264
/* terminate list */
265
*hosts[nhosts].host = 0;
266
} else {
267
if (hosts != NULL)
268
free(hosts);
269
hosts = NULL;
270
}
271
272
*he = hosts;
273
return (err);
274
}
275
276
#if defined(TESTING)
277
int
278
main(int argc, char **argv)
279
{
280
struct mx_hostentry *he, *p;
281
int err;
282
283
err = dns_get_mx_list(argv[1], 53, &he, 0);
284
if (err)
285
return (err);
286
287
for (p = he; *p->host != 0; p++) {
288
printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
289
}
290
291
return (0);
292
}
293
#endif
294
295