Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/dns_utils.c
2065 views
1
/*-
2
* Copyright (c) 2012-2014 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer,
11
* without modification, immediately at the beginning of the file.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
#include <pkg_config.h>
29
30
#include <sys/stat.h> /* for private.utils.h */
31
32
#include <string.h>
33
#include <netinet/in.h>
34
#ifdef HAVE_LDNS
35
#include <ldns/ldns.h>
36
#else
37
#define BIND_8_COMPAT
38
#include <arpa/nameser.h>
39
#include <resolv.h>
40
#endif
41
#include <netdb.h>
42
43
#ifndef NS_QFIXEDSZ
44
#define NS_QFIXEDSZ 4 /*%< #/bytes of fixed data in query */
45
#endif
46
47
#ifndef NS_INT16SZ
48
#define NS_INT16SZ 2 /*%< #/bytes of data in a u_int16_t */
49
#endif
50
51
#ifndef NS_INT32SZ
52
#define NS_INT32SZ 4 /*%< #/bytes of data in a u_int32_t */
53
#endif
54
55
/*%
56
* Inline versions of get/put short/long. Pointer is advanced.
57
*/
58
#ifndef NS_GET16
59
#define NS_GET16(s, cp) do { \
60
register const u_char *t_cp = (const u_char *)(cp); \
61
(s) = ((u_int16_t)t_cp[0] << 8) \
62
| ((u_int16_t)t_cp[1]) \
63
; \
64
(cp) += NS_INT16SZ; \
65
} while (0)
66
#endif
67
68
#ifndef NS_GET32
69
#define NS_GET32(l, cp) do { \
70
register const u_char *t_cp = (const u_char *)(cp); \
71
(l) = ((u_int32_t)t_cp[0] << 24) \
72
| ((u_int32_t)t_cp[1] << 16) \
73
| ((u_int32_t)t_cp[2] << 8) \
74
| ((u_int32_t)t_cp[3]) \
75
; \
76
(cp) += NS_INT32SZ; \
77
} while (0)
78
#endif
79
80
#include <bsd_compat.h>
81
#include "private/utils.h"
82
#include "xmalloc.h"
83
#include "pkg.h"
84
85
#ifndef HAVE_LDNS
86
typedef union {
87
HEADER hdr;
88
unsigned char buf[1024];
89
} query_t;
90
#endif
91
92
static int
93
srv_priority_cmp(const void *a, const void *b)
94
{
95
const struct dns_srvinfo *da, *db;
96
#ifdef HAVE_LDNS
97
da = (const struct dns_srvinfo *)a;
98
db = (const struct dns_srvinfo *)b;
99
#else
100
da = *(struct dns_srvinfo * const *)a;
101
db = *(struct dns_srvinfo * const *)b;
102
#endif
103
104
return ((da->priority > db->priority) - (da->priority < db->priority));
105
}
106
107
static int
108
srv_final_cmp(const void *a, const void *b)
109
{
110
const struct dns_srvinfo *da, *db;
111
int res;
112
#ifdef HAVE_LDNS
113
da = (const struct dns_srvinfo *)a;
114
db = (const struct dns_srvinfo *)b;
115
#else
116
da = *(struct dns_srvinfo * const *)a;
117
db = *(struct dns_srvinfo * const *)b;
118
#endif
119
120
res = ((da->priority > db->priority) - (da->priority < db->priority));
121
if (res == 0)
122
res = ((db->finalweight > da->finalweight) - (db->finalweight < da->finalweight));
123
124
return (res);
125
}
126
127
#ifndef HAVE_LDNS
128
static void
129
compute_weight(struct dns_srvinfo **d, int first, int last)
130
{
131
int i, j;
132
int totalweight = 0;
133
int *chosen;
134
135
for (i = 0; i <= last; i++)
136
totalweight += d[i]->weight;
137
138
if (totalweight == 0)
139
return;
140
141
chosen = xmalloc(sizeof(int) * (last - first + 1));
142
143
for (i = 0; i <= last; i++) {
144
for (;;) {
145
chosen[i] = random() % (d[i]->weight * 100 / totalweight);
146
for (j = 0; j < i; j++) {
147
if (chosen[i] == chosen[j])
148
break;
149
}
150
if (j == i) {
151
d[i]->finalweight = chosen[i];
152
break;
153
}
154
}
155
}
156
157
free(chosen);
158
}
159
160
struct dns_srvinfo *
161
dns_getsrvinfo(const char *zone)
162
{
163
char host[MAXHOSTNAMELEN];
164
query_t q;
165
int len, qdcount, ancount, n, i;
166
struct dns_srvinfo **res, *first;
167
unsigned char *end, *p;
168
unsigned int type, class, ttl, priority, weight, port;
169
int f, l;
170
171
if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 ||
172
len < (int)sizeof(HEADER))
173
return (NULL);
174
175
qdcount = ntohs(q.hdr.qdcount);
176
ancount = ntohs(q.hdr.ancount);
177
178
end = q.buf + len;
179
p = q.buf + sizeof(HEADER);
180
181
while(qdcount > 0 && p < end) {
182
qdcount--;
183
if((len = dn_expand(q.buf, end, p, host, sizeof(host))) < 0)
184
return (NULL);
185
p += len + NS_QFIXEDSZ;
186
}
187
188
res = xcalloc(ancount, sizeof(struct dns_srvinfo *));
189
n = 0;
190
while (ancount > 0 && p < end) {
191
ancount--;
192
len = dn_expand(q.buf, end, p, host, sizeof(host));
193
if (len < 0) {
194
for (i = 0; i < n; i++)
195
free(res[i]);
196
free(res);
197
return NULL;
198
}
199
200
p += len;
201
202
NS_GET16(type, p);
203
NS_GET16(class, p);
204
NS_GET32(ttl, p);
205
NS_GET16(len, p);
206
207
if (type != T_SRV) {
208
p += len;
209
continue;
210
}
211
212
NS_GET16(priority, p);
213
NS_GET16(weight, p);
214
NS_GET16(port, p);
215
216
len = dn_expand(q.buf, end, p, host, sizeof(host));
217
if (len < 0) {
218
for (i = 0; i < n; i++)
219
free(res[i]);
220
free(res);
221
return NULL;
222
}
223
224
res[n] = xmalloc(sizeof(struct dns_srvinfo));
225
res[n]->type = type;
226
res[n]->class = class;
227
res[n]->ttl = ttl;
228
res[n]->priority = priority;
229
res[n]->weight = weight;
230
res[n]->port = port;
231
res[n]->next = NULL;
232
res[n]->finalweight = 0;
233
strlcpy(res[n]->host, host, sizeof(res[n]->host));
234
235
p += len;
236
n++;
237
}
238
239
/* order by priority */
240
qsort(res, n, sizeof(res[0]), srv_priority_cmp);
241
242
priority = 0;
243
f = 0;
244
l = 0;
245
for (i = 0; i < n; i++) {
246
if (res[i]->priority != priority) {
247
if (f != l)
248
compute_weight(res, f, l);
249
f = i;
250
priority = res[i]->priority;
251
}
252
l = i;
253
}
254
255
qsort(res, n, sizeof(res[0]), srv_final_cmp);
256
257
for (i = 0; i < n - 1; i++)
258
res[i]->next = res[i + 1];
259
260
/* Sort against priority then weight */
261
262
first = res[0];
263
free(res);
264
265
return (first);
266
}
267
268
int
269
set_nameserver(const char *nsname) {
270
#ifndef HAVE___RES_SETSERVERS
271
return (-1);
272
#else
273
struct __res_state res;
274
union res_sockaddr_union u[MAXNS];
275
struct addrinfo *answer = NULL;
276
struct addrinfo *cur = NULL;
277
struct addrinfo hint;
278
int nscount = 0;
279
280
memset(u, 0, sizeof(u));
281
memset(&hint, 0, sizeof(hint));
282
memset(&res, 0, sizeof(res));
283
hint.ai_socktype = SOCK_DGRAM;
284
hint.ai_flags = AI_NUMERICHOST;
285
286
if (res_ninit(&res) == -1)
287
return (-1);
288
289
if (getaddrinfo(nsname, NULL, &hint, &answer) == 0) {
290
for (cur = answer; cur != NULL; cur = cur->ai_next) {
291
if (nscount == MAXNS)
292
break;
293
switch (cur->ai_addr->sa_family) {
294
case AF_INET6:
295
u[nscount].sin6 = *(struct sockaddr_in6*)(void *)cur->ai_addr;
296
u[nscount++].sin6.sin6_port = htons(53);
297
break;
298
case AF_INET:
299
u[nscount].sin = *(struct sockaddr_in*)(void *)cur->ai_addr;
300
u[nscount++].sin.sin_port = htons(53);
301
break;
302
}
303
}
304
if (nscount != 0)
305
res_setservers(&res, u, nscount);
306
freeaddrinfo(answer);
307
}
308
if (nscount == 0)
309
return (-1);
310
311
_res = res;
312
313
return (0);
314
#endif
315
}
316
#else
317
318
static ldns_resolver *lres = NULL;
319
320
static void
321
compute_weight(struct dns_srvinfo *d, int first, int last)
322
{
323
int i, j;
324
int totalweight = 0;
325
int *chosen;
326
327
for (i = 0; i <= last; i++)
328
totalweight += d[i].weight;
329
330
if (totalweight == 0)
331
return;
332
333
chosen = xmalloc(sizeof(int) * (last - first + 1));
334
335
for (i = 0; i <= last; i++) {
336
for (;;) {
337
chosen[i] = random() % (d[i].weight * 100 / totalweight);
338
for (j = 0; j < i; j++) {
339
if (chosen[i] == chosen[j])
340
break;
341
}
342
if (j == i) {
343
d[i].finalweight = chosen[i];
344
break;
345
}
346
}
347
}
348
349
free(chosen);
350
}
351
352
struct dns_srvinfo *
353
dns_getsrvinfo(const char *zone)
354
{
355
ldns_rdf *domain;
356
ldns_pkt *p;
357
ldns_rr_list *srv;
358
struct dns_srvinfo *res;
359
int ancount, i;
360
int f, l, priority;
361
362
if (lres == NULL)
363
if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
364
return (NULL);
365
366
domain = ldns_dname_new_frm_str(zone);
367
if (domain == NULL)
368
return (NULL);
369
370
p = ldns_resolver_query(lres, domain,
371
LDNS_RR_TYPE_SRV,
372
LDNS_RR_CLASS_IN,
373
LDNS_RD);
374
375
ldns_rdf_deep_free(domain);
376
377
if (p == NULL)
378
return (NULL);
379
380
srv = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SRV, LDNS_SECTION_ANSWER);
381
ldns_pkt_free(p);
382
383
if (srv == NULL)
384
return (NULL);
385
386
ancount = ldns_rr_list_rr_count(srv);
387
res = xcalloc(ancount, sizeof(struct dns_srvinfo));
388
389
for (i = 0; i < ancount; i ++) {
390
ldns_rr *rr;
391
392
rr = ldns_rr_list_rr(srv, i);
393
if (rr != NULL) {
394
char *host;
395
res[i].class = ldns_rr_get_class(rr);
396
res[i].ttl = ldns_rr_ttl(rr);
397
res[i].priority = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0));
398
res[i].weight = ldns_rdf2native_int16(ldns_rr_rdf(rr, 1));
399
res[i].port = ldns_rdf2native_int16(ldns_rr_rdf(rr, 2));
400
host = ldns_rdf2str(ldns_rr_rdf(rr, 3));
401
strlcpy(res[i].host, host, sizeof(res[i].host));
402
free(host);
403
}
404
}
405
406
ldns_rr_list_deep_free(srv);
407
408
/* order by priority */
409
qsort(res, ancount, sizeof(res[0]), srv_priority_cmp);
410
411
priority = 0;
412
f = 0;
413
l = 0;
414
for (i = 0; i < ancount; i++) {
415
if (res[i].priority != priority) {
416
if (f != l)
417
compute_weight(res, f, l);
418
f = i;
419
priority = res[i].priority;
420
}
421
l = i;
422
}
423
424
/* Sort against priority then weight */
425
qsort(res, ancount, sizeof(res[0]), srv_final_cmp);
426
427
for (i = 0; i < ancount - 1; i++)
428
res[i].next = &res[i + 1];
429
430
return (res);
431
}
432
433
int
434
set_nameserver(const char *nsname)
435
{
436
/*
437
* XXX: can we use the system resolver to resolve this name ??
438
* The current code does this, but it is unlikely a good solution
439
* So here we allow IP addresses only
440
*/
441
ldns_rdf *rdf;
442
443
rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, nsname);
444
if (rdf == NULL)
445
rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, nsname);
446
447
if (rdf == NULL)
448
return (EPKG_FATAL);
449
450
if (lres == NULL)
451
if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
452
return (EPKG_FATAL);
453
454
if (ldns_resolver_push_nameserver(lres, rdf) != LDNS_STATUS_OK)
455
return (EPKG_FATAL);
456
457
return (EPKG_OK);
458
}
459
#endif
460
461