Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2012-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
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 : 0 : 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 : 0 : da = *(struct dns_srvinfo * const *)a;
101 : 0 : db = *(struct dns_srvinfo * const *)b;
102 : : #endif
103 : :
104 : 0 : return ((da->priority > db->priority) - (da->priority < db->priority));
105 : : }
106 : :
107 : : static int
108 : 0 : 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 : 0 : da = *(struct dns_srvinfo * const *)a;
117 : 0 : db = *(struct dns_srvinfo * const *)b;
118 : : #endif
119 : :
120 : 0 : res = ((da->priority > db->priority) - (da->priority < db->priority));
121 [ # # ]: 0 : if (res == 0)
122 : 0 : res = ((db->finalweight > da->finalweight) - (db->finalweight < da->finalweight));
123 : :
124 : 0 : return (res);
125 : : }
126 : :
127 : : #ifndef HAVE_LDNS
128 : : static void
129 : 0 : compute_weight(struct dns_srvinfo **d, int first, int last)
130 : : {
131 : : int i, j;
132 : 0 : int totalweight = 0;
133 : : int *chosen;
134 : :
135 [ # # ]: 0 : for (i = 0; i <= last; i++)
136 : 0 : totalweight += d[i]->weight;
137 : :
138 [ # # ]: 0 : if (totalweight == 0)
139 : 0 : return;
140 : :
141 : 0 : chosen = xmalloc(sizeof(int) * (last - first + 1));
142 : :
143 [ # # ]: 0 : for (i = 0; i <= last; i++) {
144 : 0 : for (;;) {
145 : 0 : chosen[i] = random() % (d[i]->weight * 100 / totalweight);
146 [ # # ]: 0 : for (j = 0; j < i; j++) {
147 [ # # ]: 0 : if (chosen[i] == chosen[j])
148 : 0 : break;
149 : 0 : }
150 [ # # ]: 0 : if (j == i) {
151 : 0 : d[i]->finalweight = chosen[i];
152 : 0 : break;
153 : : }
154 : : }
155 : 0 : }
156 : :
157 : 0 : free(chosen);
158 : 0 : }
159 : :
160 : : struct dns_srvinfo *
161 : 4 : 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 [ - + # # ]: 4 : if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 ||
172 : 0 : len < (int)sizeof(HEADER))
173 : 4 : return (NULL);
174 : :
175 : 0 : qdcount = ntohs(q.hdr.qdcount);
176 : 0 : ancount = ntohs(q.hdr.ancount);
177 : :
178 : 0 : end = q.buf + len;
179 : 0 : p = q.buf + sizeof(HEADER);
180 : :
181 [ # # # # ]: 0 : while(qdcount > 0 && p < end) {
182 : 0 : qdcount--;
183 [ # # ]: 0 : if((len = dn_expand(q.buf, end, p, host, sizeof(host))) < 0)
184 : 0 : return (NULL);
185 : 0 : p += len + NS_QFIXEDSZ;
186 : : }
187 : :
188 : 0 : res = xcalloc(ancount, sizeof(struct dns_srvinfo *));
189 : 0 : n = 0;
190 [ # # # # ]: 0 : while (ancount > 0 && p < end) {
191 : 0 : ancount--;
192 : 0 : len = dn_expand(q.buf, end, p, host, sizeof(host));
193 [ # # ]: 0 : if (len < 0) {
194 [ # # ]: 0 : for (i = 0; i < n; i++)
195 : 0 : free(res[i]);
196 : 0 : free(res);
197 : 0 : return NULL;
198 : : }
199 : :
200 : 0 : p += len;
201 : :
202 : 0 : NS_GET16(type, p);
203 : 0 : NS_GET16(class, p);
204 : 0 : NS_GET32(ttl, p);
205 : 0 : NS_GET16(len, p);
206 : :
207 [ # # ]: 0 : if (type != T_SRV) {
208 : 0 : p += len;
209 : 0 : continue;
210 : : }
211 : :
212 : 0 : NS_GET16(priority, p);
213 : 0 : NS_GET16(weight, p);
214 : 0 : NS_GET16(port, p);
215 : :
216 : 0 : len = dn_expand(q.buf, end, p, host, sizeof(host));
217 [ # # ]: 0 : if (len < 0) {
218 [ # # ]: 0 : for (i = 0; i < n; i++)
219 : 0 : free(res[i]);
220 : 0 : free(res);
221 : 0 : return NULL;
222 : : }
223 : :
224 : 0 : res[n] = xmalloc(sizeof(struct dns_srvinfo));
225 [ # # ]: 0 : if (res[n] == NULL) {
226 [ # # ]: 0 : for (i = 0; i < n; i++)
227 : 0 : free(res[i]);
228 : 0 : free(res);
229 : 0 : return NULL;
230 : : }
231 : 0 : res[n]->type = type;
232 : 0 : res[n]->class = class;
233 : 0 : res[n]->ttl = ttl;
234 : 0 : res[n]->priority = priority;
235 : 0 : res[n]->weight = weight;
236 : 0 : res[n]->port = port;
237 : 0 : res[n]->next = NULL;
238 : 0 : res[n]->finalweight = 0;
239 : 0 : strlcpy(res[n]->host, host, sizeof(res[n]->host));
240 : :
241 : 0 : p += len;
242 : 0 : n++;
243 : : }
244 : :
245 : : /* order by priority */
246 : 0 : qsort(res, n, sizeof(res[0]), srv_priority_cmp);
247 : :
248 : 0 : priority = 0;
249 : 0 : f = 0;
250 : 0 : l = 0;
251 [ # # ]: 0 : for (i = 0; i < n; i++) {
252 [ # # ]: 0 : if (res[i]->priority != priority) {
253 [ # # ]: 0 : if (f != l)
254 : 0 : compute_weight(res, f, l);
255 : 0 : f = i;
256 : 0 : priority = res[i]->priority;
257 : 0 : }
258 : 0 : l = i;
259 : 0 : }
260 : :
261 : 0 : qsort(res, n, sizeof(res[0]), srv_final_cmp);
262 : :
263 [ # # ]: 0 : for (i = 0; i < n - 1; i++)
264 : 0 : res[i]->next = res[i + 1];
265 : :
266 : : /* Sort against priority then weight */
267 : :
268 : 0 : first = res[0];
269 : 0 : free(res);
270 : :
271 : 0 : return (first);
272 : 4 : }
273 : :
274 : : int
275 : 16 : set_nameserver(const char *nsname) {
276 : : #ifndef HAVE___RES_SETSERVERS
277 : : return (-1);
278 : : #else
279 : : struct __res_state res;
280 : : union res_sockaddr_union u[MAXNS];
281 : 16 : struct addrinfo *answer = NULL;
282 : 16 : struct addrinfo *cur = NULL;
283 : : struct addrinfo hint;
284 : 16 : int nscount = 0;
285 : :
286 : 16 : memset(u, 0, sizeof(u));
287 : 16 : memset(&hint, 0, sizeof(hint));
288 : 16 : memset(&res, 0, sizeof(res));
289 : 16 : hint.ai_socktype = SOCK_DGRAM;
290 : 16 : hint.ai_flags = AI_NUMERICHOST;
291 : :
292 [ - + ]: 16 : if (res_ninit(&res) == -1)
293 : 0 : return (-1);
294 : :
295 [ + + ]: 16 : if (getaddrinfo(nsname, NULL, &hint, &answer) == 0) {
296 [ + + ]: 16 : for (cur = answer; cur != NULL; cur = cur->ai_next) {
297 [ - + ]: 8 : if (nscount == MAXNS)
298 : 0 : break;
299 [ - - + ]: 8 : switch (cur->ai_addr->sa_family) {
300 : : case AF_INET6:
301 : 0 : u[nscount].sin6 = *(struct sockaddr_in6*)(void *)cur->ai_addr;
302 : 0 : u[nscount++].sin6.sin6_port = htons(53);
303 : 0 : break;
304 : : case AF_INET:
305 : 8 : u[nscount].sin = *(struct sockaddr_in*)(void *)cur->ai_addr;
306 : 8 : u[nscount++].sin.sin_port = htons(53);
307 : 8 : break;
308 : : }
309 : 8 : }
310 [ - + ]: 8 : if (nscount != 0)
311 : 8 : res_setservers(&res, u, nscount);
312 : 8 : freeaddrinfo(answer);
313 : 8 : }
314 [ + + ]: 16 : if (nscount == 0)
315 : 8 : return (-1);
316 : :
317 : 8 : _res = res;
318 : :
319 : 8 : return (0);
320 : : #endif
321 : 16 : }
322 : : #else
323 : :
324 : : static ldns_resolver *lres = NULL;
325 : :
326 : : static void
327 : : compute_weight(struct dns_srvinfo *d, int first, int last)
328 : : {
329 : : int i, j;
330 : : int totalweight = 0;
331 : : int *chosen;
332 : :
333 : : for (i = 0; i <= last; i++)
334 : : totalweight += d[i].weight;
335 : :
336 : : if (totalweight == 0)
337 : : return;
338 : :
339 : : chosen = xmalloc(sizeof(int) * (last - first + 1));
340 : :
341 : : for (i = 0; i <= last; i++) {
342 : : for (;;) {
343 : : chosen[i] = random() % (d[i].weight * 100 / totalweight);
344 : : for (j = 0; j < i; j++) {
345 : : if (chosen[i] == chosen[j])
346 : : break;
347 : : }
348 : : if (j == i) {
349 : : d[i].finalweight = chosen[i];
350 : : break;
351 : : }
352 : : }
353 : : }
354 : :
355 : : free(chosen);
356 : : }
357 : :
358 : : struct dns_srvinfo *
359 : : dns_getsrvinfo(const char *zone)
360 : : {
361 : : ldns_rdf *domain;
362 : : ldns_pkt *p;
363 : : ldns_rr_list *srv;
364 : : struct dns_srvinfo *res;
365 : : int ancount, i;
366 : : int f, l, priority;
367 : :
368 : : if (lres == NULL)
369 : : if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
370 : : return (NULL);
371 : :
372 : : domain = ldns_dname_new_frm_str(zone);
373 : : if (domain == NULL)
374 : : return (NULL);
375 : :
376 : : p = ldns_resolver_query(lres, domain,
377 : : LDNS_RR_TYPE_SRV,
378 : : LDNS_RR_CLASS_IN,
379 : : LDNS_RD);
380 : :
381 : : ldns_rdf_deep_free(domain);
382 : :
383 : : if (p == NULL)
384 : : return (NULL);
385 : :
386 : : srv = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SRV, LDNS_SECTION_ANSWER);
387 : : ldns_pkt_free(p);
388 : :
389 : : if (srv == NULL)
390 : : return (NULL);
391 : :
392 : : ancount = ldns_rr_list_rr_count(srv);
393 : : res = xcalloc(ancount, sizeof(struct dns_srvinfo));
394 : :
395 : : for (i = 0; i < ancount; i ++) {
396 : : ldns_rr *rr;
397 : :
398 : : rr = ldns_rr_list_rr(srv, i);
399 : : if (rr != NULL) {
400 : : char *host;
401 : : res[i].class = ldns_rr_get_class(rr);
402 : : res[i].ttl = ldns_rr_ttl(rr);
403 : : res[i].priority = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0));
404 : : res[i].weight = ldns_rdf2native_int16(ldns_rr_rdf(rr, 1));
405 : : res[i].port = ldns_rdf2native_int16(ldns_rr_rdf(rr, 2));
406 : : host = ldns_rdf2str(ldns_rr_rdf(rr, 3));
407 : : strlcpy(res[i].host, host, sizeof(res[i].host));
408 : : free(host);
409 : : }
410 : : }
411 : :
412 : : ldns_rr_list_deep_free(srv);
413 : :
414 : : /* order by priority */
415 : : qsort(res, ancount, sizeof(res[0]), srv_priority_cmp);
416 : :
417 : : priority = 0;
418 : : f = 0;
419 : : l = 0;
420 : : for (i = 0; i < ancount; i++) {
421 : : if (res[i].priority != priority) {
422 : : if (f != l)
423 : : compute_weight(res, f, l);
424 : : f = i;
425 : : priority = res[i].priority;
426 : : }
427 : : l = i;
428 : : }
429 : :
430 : : /* Sort against priority then weight */
431 : : qsort(res, ancount, sizeof(res[0]), srv_final_cmp);
432 : :
433 : : for (i = 0; i < ancount - 1; i++)
434 : : res[i].next = &res[i + 1];
435 : :
436 : : return (res);
437 : : }
438 : :
439 : : int
440 : : set_nameserver(const char *nsname)
441 : : {
442 : : /*
443 : : * XXX: can we use the system resolver to resolve this name ??
444 : : * The current code does this, but it is unlikely a good solution
445 : : * So here we allow IP addresses only
446 : : */
447 : : ldns_rdf *rdf;
448 : :
449 : : rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, nsname);
450 : : if (rdf == NULL)
451 : : rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, nsname);
452 : :
453 : : if (rdf == NULL)
454 : : return (EPKG_FATAL);
455 : :
456 : : if (lres == NULL)
457 : : if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
458 : : return (EPKG_FATAL);
459 : :
460 : : if (ldns_resolver_push_nameserver(lres, rdf) != LDNS_STATUS_OK)
461 : : return (EPKG_FATAL);
462 : :
463 : : return (EPKG_OK);
464 : : }
465 : : #endif
|