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 : #include <bsd_compat.h>
44 : #include "private/utils.h"
45 : #include "pkg.h"
46 :
47 : #ifndef HAVE_LDNS
48 : typedef union {
49 : HEADER hdr;
50 : unsigned char buf[1024];
51 : } query_t;
52 : #endif
53 :
54 : static int
55 0 : srv_priority_cmp(const void *a, const void *b)
56 : {
57 : const struct dns_srvinfo *da, *db;
58 : #ifdef HAVE_LDNS
59 : da = (const struct dns_srvinfo *)a;
60 : db = (const struct dns_srvinfo *)b;
61 : #else
62 0 : da = *(struct dns_srvinfo * const *)a;
63 0 : db = *(struct dns_srvinfo * const *)b;
64 : #endif
65 :
66 0 : return ((da->priority > db->priority) - (da->priority < db->priority));
67 : }
68 :
69 : static int
70 0 : srv_final_cmp(const void *a, const void *b)
71 : {
72 : const struct dns_srvinfo *da, *db;
73 : int res;
74 : #ifdef HAVE_LDNS
75 : da = (const struct dns_srvinfo *)a;
76 : db = (const struct dns_srvinfo *)b;
77 : #else
78 0 : da = *(struct dns_srvinfo * const *)a;
79 0 : db = *(struct dns_srvinfo * const *)b;
80 : #endif
81 :
82 0 : res = ((da->priority > db->priority) - (da->priority < db->priority));
83 0 : if (res == 0)
84 0 : res = ((db->finalweight > da->finalweight) - (db->finalweight < da->finalweight));
85 :
86 0 : return (res);
87 : }
88 :
89 : #ifndef HAVE_LDNS
90 : static void
91 0 : compute_weight(struct dns_srvinfo **d, int first, int last)
92 : {
93 : int i, j;
94 0 : int totalweight = 0;
95 : int *chosen;
96 :
97 0 : for (i = 0; i <= last; i++)
98 0 : totalweight += d[i]->weight;
99 :
100 0 : if (totalweight == 0)
101 0 : return;
102 :
103 0 : chosen = malloc(sizeof(int) * (last - first + 1));
104 :
105 0 : for (i = 0; i <= last; i++) {
106 : for (;;) {
107 0 : chosen[i] = random() % (d[i]->weight * 100 / totalweight);
108 0 : for (j = 0; j < i; j++) {
109 0 : if (chosen[i] == chosen[j])
110 0 : break;
111 : }
112 0 : if (j == i) {
113 0 : d[i]->finalweight = chosen[i];
114 0 : break;
115 : }
116 0 : }
117 : }
118 :
119 0 : free(chosen);
120 : }
121 :
122 : struct dns_srvinfo *
123 0 : dns_getsrvinfo(const char *zone)
124 : {
125 : char host[MAXHOSTNAMELEN];
126 : query_t q;
127 : int len, qdcount, ancount, n, i;
128 : struct dns_srvinfo **res, *first;
129 : unsigned char *end, *p;
130 : unsigned int type, class, ttl, priority, weight, port;
131 : int f, l;
132 :
133 0 : if ((len = res_query(zone, C_IN, T_SRV, q.buf, sizeof(q.buf))) == -1 ||
134 : len < (int)sizeof(HEADER))
135 0 : return (NULL);
136 :
137 0 : qdcount = ntohs(q.hdr.qdcount);
138 0 : ancount = ntohs(q.hdr.ancount);
139 :
140 0 : end = q.buf + len;
141 0 : p = q.buf + sizeof(HEADER);
142 :
143 0 : while(qdcount > 0 && p < end) {
144 0 : qdcount--;
145 0 : if((len = dn_expand(q.buf, end, p, host, sizeof(host))) < 0)
146 0 : return (NULL);
147 0 : p += len + NS_QFIXEDSZ;
148 : }
149 :
150 0 : res = calloc(ancount, sizeof(struct dns_srvinfo *));
151 0 : if (res == NULL)
152 0 : return (NULL);
153 :
154 0 : n = 0;
155 0 : while (ancount > 0 && p < end) {
156 0 : ancount--;
157 0 : len = dn_expand(q.buf, end, p, host, sizeof(host));
158 0 : if (len < 0) {
159 0 : for (i = 0; i < n; i++)
160 0 : free(res[i]);
161 0 : free(res);
162 0 : return NULL;
163 : }
164 :
165 0 : p += len;
166 :
167 0 : NS_GET16(type, p);
168 0 : NS_GET16(class, p);
169 0 : NS_GET32(ttl, p);
170 0 : NS_GET16(len, p);
171 :
172 0 : if (type != T_SRV) {
173 0 : p += len;
174 0 : continue;
175 : }
176 :
177 0 : NS_GET16(priority, p);
178 0 : NS_GET16(weight, p);
179 0 : NS_GET16(port, p);
180 :
181 0 : len = dn_expand(q.buf, end, p, host, sizeof(host));
182 0 : if (len < 0) {
183 0 : for (i = 0; i < n; i++)
184 0 : free(res[i]);
185 0 : free(res);
186 0 : return NULL;
187 : }
188 :
189 0 : res[n] = malloc(sizeof(struct dns_srvinfo));
190 0 : if (res[n] == NULL) {
191 0 : for (i = 0; i < n; i++)
192 0 : free(res[i]);
193 0 : free(res);
194 0 : return NULL;
195 : }
196 0 : res[n]->type = type;
197 0 : res[n]->class = class;
198 0 : res[n]->ttl = ttl;
199 0 : res[n]->priority = priority;
200 0 : res[n]->weight = weight;
201 0 : res[n]->port = port;
202 0 : res[n]->next = NULL;
203 0 : res[n]->finalweight = 0;
204 0 : strlcpy(res[n]->host, host, sizeof(res[n]->host));
205 :
206 0 : p += len;
207 0 : n++;
208 : }
209 :
210 : /* order by priority */
211 0 : qsort(res, n, sizeof(res[0]), srv_priority_cmp);
212 :
213 0 : priority = 0;
214 0 : f = 0;
215 0 : l = 0;
216 0 : for (i = 0; i < n; i++) {
217 0 : if (res[i]->priority != priority) {
218 0 : if (f != l)
219 0 : compute_weight(res, f, l);
220 0 : f = i;
221 0 : priority = res[i]->priority;
222 : }
223 0 : l = i;
224 : }
225 :
226 0 : qsort(res, n, sizeof(res[0]), srv_final_cmp);
227 :
228 0 : for (i = 0; i < n - 1; i++)
229 0 : res[i]->next = res[i + 1];
230 :
231 : /* Sort against priority then weight */
232 :
233 0 : first = res[0];
234 0 : free(res);
235 :
236 0 : return (first);
237 : }
238 :
239 : int
240 0 : set_nameserver(const char *nsname) {
241 : struct __res_state res;
242 : union res_sockaddr_union u[MAXNS];
243 0 : struct addrinfo *answer = NULL;
244 0 : struct addrinfo *cur = NULL;
245 : struct addrinfo hint;
246 0 : int nscount = 0;
247 :
248 0 : memset(u, 0, sizeof(u));
249 0 : memset(&hint, 0, sizeof(hint));
250 0 : hint.ai_socktype = SOCK_DGRAM;
251 :
252 0 : if (res_ninit(&res) == -1)
253 0 : return (-1);
254 :
255 0 : if (getaddrinfo(nsname, NULL, &hint, &answer) == 0) {
256 0 : for (cur = answer; cur != NULL; cur = cur->ai_next) {
257 0 : if (nscount == MAXNS)
258 0 : break;
259 0 : switch (cur->ai_addr->sa_family) {
260 : case AF_INET6:
261 0 : u[nscount].sin6 = *(struct sockaddr_in6*)(void *)cur->ai_addr;
262 0 : u[nscount++].sin6.sin6_port = htons(53);
263 0 : break;
264 : case AF_INET:
265 0 : u[nscount].sin = *(struct sockaddr_in*)(void *)cur->ai_addr;
266 0 : u[nscount++].sin.sin_port = htons(53);
267 0 : break;
268 : }
269 : }
270 0 : if (nscount != 0)
271 0 : res_setservers(&res, u, nscount);
272 0 : freeaddrinfo(answer);
273 : }
274 0 : if (nscount == 0)
275 0 : return (-1);
276 :
277 0 : _res = res;
278 :
279 0 : return (0);
280 : }
281 : #else
282 :
283 : static ldns_resolver *lres = NULL;
284 :
285 : static void
286 : compute_weight(struct dns_srvinfo *d, int first, int last)
287 : {
288 : int i, j;
289 : int totalweight = 0;
290 : int *chosen;
291 :
292 : for (i = 0; i <= last; i++)
293 : totalweight += d[i].weight;
294 :
295 : if (totalweight == 0)
296 : return;
297 :
298 : chosen = malloc(sizeof(int) * (last - first + 1));
299 :
300 : for (i = 0; i <= last; i++) {
301 : for (;;) {
302 : chosen[i] = random() % (d[i].weight * 100 / totalweight);
303 : for (j = 0; j < i; j++) {
304 : if (chosen[i] == chosen[j])
305 : break;
306 : }
307 : if (j == i) {
308 : d[i].finalweight = chosen[i];
309 : break;
310 : }
311 : }
312 : }
313 :
314 : free(chosen);
315 : }
316 :
317 : struct dns_srvinfo *
318 : dns_getsrvinfo(const char *zone)
319 : {
320 : ldns_rdf *domain;
321 : ldns_pkt *p;
322 : ldns_rr_list *srv;
323 : struct dns_srvinfo *res;
324 : int ancount, i;
325 : int f, l, priority;
326 :
327 : if (lres == NULL)
328 : if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
329 : return (NULL);
330 :
331 : domain = ldns_dname_new_frm_str(zone);
332 : if (domain == NULL)
333 : return (NULL);
334 :
335 : p = ldns_resolver_query(lres, domain,
336 : LDNS_RR_TYPE_SRV,
337 : LDNS_RR_CLASS_IN,
338 : LDNS_RD);
339 :
340 : ldns_rdf_deep_free(domain);
341 :
342 : if (p == NULL)
343 : return (NULL);
344 :
345 : srv = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SRV, LDNS_SECTION_ANSWER);
346 : ldns_pkt_free(p);
347 :
348 : if (srv == NULL)
349 : return (NULL);
350 :
351 : ancount = ldns_rr_list_rr_count(srv);
352 : res = calloc(ancount, sizeof(struct dns_srvinfo));
353 : if (res == NULL)
354 : return (NULL);
355 :
356 : for (i = 0; i < ancount; i ++) {
357 : ldns_rr *rr;
358 :
359 : rr = ldns_rr_list_rr(srv, i);
360 : if (rr != NULL) {
361 : char *host;
362 : res[i].class = ldns_rr_get_class(rr);
363 : res[i].ttl = ldns_rr_ttl(rr);
364 : res[i].priority = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0));
365 : res[i].weight = ldns_rdf2native_int16(ldns_rr_rdf(rr, 1));
366 : res[i].port = ldns_rdf2native_int16(ldns_rr_rdf(rr, 2));
367 : host = ldns_rdf2str(ldns_rr_rdf(rr, 3));
368 : strlcpy(res[i].host, host, sizeof(res[i].host));
369 : free(host);
370 : }
371 : }
372 :
373 : ldns_rr_list_deep_free(srv);
374 :
375 : /* order by priority */
376 : qsort(res, ancount, sizeof(res[0]), srv_priority_cmp);
377 :
378 : priority = 0;
379 : f = 0;
380 : l = 0;
381 : for (i = 0; i < ancount; i++) {
382 : if (res[i].priority != priority) {
383 : if (f != l)
384 : compute_weight(res, f, l);
385 : f = i;
386 : priority = res[i].priority;
387 : }
388 : l = i;
389 : }
390 :
391 : /* Sort against priority then weight */
392 : qsort(res, ancount, sizeof(res[0]), srv_final_cmp);
393 :
394 : for (i = 0; i < ancount - 1; i++)
395 : res[i].next = &res[i + 1];
396 :
397 : return (res);
398 : }
399 :
400 : int
401 : set_nameserver(const char *nsname)
402 : {
403 : /*
404 : * XXX: can we use the system resolver to resolve this name ??
405 : * The current code does this, but it is unlikely a good solution
406 : * So here we allow IP addresses only
407 : */
408 : ldns_rdf *rdf;
409 :
410 : rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, nsname);
411 : if (rdf == NULL)
412 : rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, nsname);
413 :
414 : if (rdf == NULL)
415 : return (EPKG_FATAL);
416 :
417 : if (lres == NULL)
418 : if (ldns_resolver_new_frm_file(&lres, NULL) != LDNS_STATUS_OK)
419 : return (EPKG_FATAL);
420 :
421 : if (ldns_resolver_push_nameserver(lres, rdf) != LDNS_STATUS_OK)
422 : return (EPKG_FATAL);
423 :
424 : return (EPKG_OK);
425 : }
426 : #endif
|