Deleted Added
full compact
cap_dns.c (297982) cap_dns.c (301572)
1/*-
2 * Copyright (c) 2012-2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
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 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2012-2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
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 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/lib/libcasper/services/cap_dns/cap_dns.c 297982 2016-04-14 18:27:10Z oshogbo $");
31__FBSDID("$FreeBSD: head/lib/libcasper/services/cap_dns/cap_dns.c 301572 2016-06-08 02:03:53Z oshogbo $");
32
33#include <sys/dnv.h>
34#include <sys/nv.h>
35#include <netinet/in.h>
36
37#include <assert.h>
38#include <errno.h>
39#include <netdb.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <libcasper.h>
45#include <libcasper_service.h>
46
47#include "cap_dns.h"
48
49static struct hostent hent;
50
51static void
52hostent_free(struct hostent *hp)
53{
54 unsigned int ii;
55
56 free(hp->h_name);
57 hp->h_name = NULL;
58 if (hp->h_aliases != NULL) {
59 for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
60 free(hp->h_aliases[ii]);
61 free(hp->h_aliases);
62 hp->h_aliases = NULL;
63 }
64 if (hp->h_addr_list != NULL) {
65 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
66 free(hp->h_addr_list[ii]);
67 free(hp->h_addr_list);
68 hp->h_addr_list = NULL;
69 }
70}
71
72static struct hostent *
73hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
74{
75 unsigned int ii, nitems;
76 char nvlname[64];
77 int n;
78
79 hostent_free(hp);
80
81 hp->h_name = strdup(nvlist_get_string(nvl, "name"));
82 if (hp->h_name == NULL)
83 goto fail;
84 hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
85 hp->h_length = (int)nvlist_get_number(nvl, "length");
86
87 nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
88 hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
89 if (hp->h_aliases == NULL)
90 goto fail;
91 for (ii = 0; ii < nitems; ii++) {
92 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
93 assert(n > 0 && n < (int)sizeof(nvlname));
94 hp->h_aliases[ii] =
95 strdup(nvlist_get_string(nvl, nvlname));
96 if (hp->h_aliases[ii] == NULL)
97 goto fail;
98 }
99 hp->h_aliases[ii] = NULL;
100
101 nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
102 hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
103 if (hp->h_addr_list == NULL)
104 goto fail;
105 for (ii = 0; ii < nitems; ii++) {
106 hp->h_addr_list[ii] = malloc(hp->h_length);
107 if (hp->h_addr_list[ii] == NULL)
108 goto fail;
109 n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
110 assert(n > 0 && n < (int)sizeof(nvlname));
111 bcopy(nvlist_get_binary(nvl, nvlname, NULL),
112 hp->h_addr_list[ii], hp->h_length);
113 }
114 hp->h_addr_list[ii] = NULL;
115
116 return (hp);
117fail:
118 hostent_free(hp);
119 h_errno = NO_RECOVERY;
120 return (NULL);
121}
122
123struct hostent *
124cap_gethostbyname(cap_channel_t *chan, const char *name)
125{
126
127 return (cap_gethostbyname2(chan, name, AF_INET));
128}
129
130struct hostent *
131cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
132{
133 struct hostent *hp;
134 nvlist_t *nvl;
135
136 nvl = nvlist_create(0);
137 nvlist_add_string(nvl, "cmd", "gethostbyname");
138 nvlist_add_number(nvl, "family", (uint64_t)type);
139 nvlist_add_string(nvl, "name", name);
140 nvl = cap_xfer_nvlist(chan, nvl, 0);
141 if (nvl == NULL) {
142 h_errno = NO_RECOVERY;
143 return (NULL);
144 }
145 if (nvlist_get_number(nvl, "error") != 0) {
146 h_errno = (int)nvlist_get_number(nvl, "error");
147 nvlist_destroy(nvl);
148 return (NULL);
149 }
150
151 hp = hostent_unpack(nvl, &hent);
152 nvlist_destroy(nvl);
153 return (hp);
154}
155
156struct hostent *
157cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
158 int type)
159{
160 struct hostent *hp;
161 nvlist_t *nvl;
162
163 nvl = nvlist_create(0);
164 nvlist_add_string(nvl, "cmd", "gethostbyaddr");
165 nvlist_add_binary(nvl, "addr", addr, (size_t)len);
166 nvlist_add_number(nvl, "family", (uint64_t)type);
167 nvl = cap_xfer_nvlist(chan, nvl, 0);
168 if (nvl == NULL) {
169 h_errno = NO_RECOVERY;
170 return (NULL);
171 }
172 if (nvlist_get_number(nvl, "error") != 0) {
173 h_errno = (int)nvlist_get_number(nvl, "error");
174 nvlist_destroy(nvl);
175 return (NULL);
176 }
177 hp = hostent_unpack(nvl, &hent);
178 nvlist_destroy(nvl);
179 return (hp);
180}
181
182static struct addrinfo *
183addrinfo_unpack(const nvlist_t *nvl)
184{
185 struct addrinfo *ai;
186 const void *addr;
187 size_t addrlen;
188 const char *canonname;
189
190 addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
191 ai = malloc(sizeof(*ai) + addrlen);
192 if (ai == NULL)
193 return (NULL);
194 ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
195 ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
196 ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
197 ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
198 ai->ai_addrlen = (socklen_t)addrlen;
199 canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
200 if (canonname != NULL) {
201 ai->ai_canonname = strdup(canonname);
202 if (ai->ai_canonname == NULL) {
203 free(ai);
204 return (NULL);
205 }
206 } else {
207 ai->ai_canonname = NULL;
208 }
209 ai->ai_addr = (void *)(ai + 1);
210 bcopy(addr, ai->ai_addr, addrlen);
211 ai->ai_next = NULL;
212
213 return (ai);
214}
215
216int
217cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
218 const struct addrinfo *hints, struct addrinfo **res)
219{
220 struct addrinfo *firstai, *prevai, *curai;
221 unsigned int ii;
222 const nvlist_t *nvlai;
223 char nvlname[64];
224 nvlist_t *nvl;
225 int error, n;
226
227 nvl = nvlist_create(0);
228 nvlist_add_string(nvl, "cmd", "getaddrinfo");
229 if (hostname != NULL)
230 nvlist_add_string(nvl, "hostname", hostname);
231 if (servname != NULL)
232 nvlist_add_string(nvl, "servname", servname);
233 if (hints != NULL) {
234 nvlist_add_number(nvl, "hints.ai_flags",
235 (uint64_t)hints->ai_flags);
236 nvlist_add_number(nvl, "hints.ai_family",
237 (uint64_t)hints->ai_family);
238 nvlist_add_number(nvl, "hints.ai_socktype",
239 (uint64_t)hints->ai_socktype);
240 nvlist_add_number(nvl, "hints.ai_protocol",
241 (uint64_t)hints->ai_protocol);
242 }
243 nvl = cap_xfer_nvlist(chan, nvl, 0);
244 if (nvl == NULL)
245 return (EAI_MEMORY);
246 if (nvlist_get_number(nvl, "error") != 0) {
247 error = (int)nvlist_get_number(nvl, "error");
248 nvlist_destroy(nvl);
249 return (error);
250 }
251
252 nvlai = NULL;
253 firstai = prevai = curai = NULL;
254 for (ii = 0; ; ii++) {
255 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
256 assert(n > 0 && n < (int)sizeof(nvlname));
257 if (!nvlist_exists_nvlist(nvl, nvlname))
258 break;
259 nvlai = nvlist_get_nvlist(nvl, nvlname);
260 curai = addrinfo_unpack(nvlai);
261 if (curai == NULL)
262 break;
263 if (prevai != NULL)
264 prevai->ai_next = curai;
265 else if (firstai == NULL)
266 firstai = curai;
267 prevai = curai;
268 }
269 nvlist_destroy(nvl);
270 if (curai == NULL && nvlai != NULL) {
271 if (firstai == NULL)
272 freeaddrinfo(firstai);
273 return (EAI_MEMORY);
274 }
275
276 *res = firstai;
277 return (0);
278}
279
280int
281cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
282 char *host, size_t hostlen, char *serv, size_t servlen, int flags)
283{
284 nvlist_t *nvl;
285 int error;
286
287 nvl = nvlist_create(0);
288 nvlist_add_string(nvl, "cmd", "getnameinfo");
289 nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
290 nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
291 nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
292 nvlist_add_number(nvl, "flags", (uint64_t)flags);
293 nvl = cap_xfer_nvlist(chan, nvl, 0);
294 if (nvl == NULL)
295 return (EAI_MEMORY);
296 if (nvlist_get_number(nvl, "error") != 0) {
297 error = (int)nvlist_get_number(nvl, "error");
298 nvlist_destroy(nvl);
299 return (error);
300 }
301
302 if (host != NULL && nvlist_exists_string(nvl, "host"))
303 strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
304 if (serv != NULL && nvlist_exists_string(nvl, "serv"))
305 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
306 nvlist_destroy(nvl);
307 return (0);
308}
309
310static void
311limit_remove(nvlist_t *limits, const char *prefix)
312{
313 const char *name;
314 size_t prefixlen;
315 void *cookie;
316
317 prefixlen = strlen(prefix);
318again:
319 cookie = NULL;
320 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
321 if (strncmp(name, prefix, prefixlen) == 0) {
322 nvlist_free(limits, name);
323 goto again;
324 }
325 }
326}
327
328int
329cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
330 size_t ntypes)
331{
332 nvlist_t *limits;
333 unsigned int i;
334 char nvlname[64];
335 int n;
336
337 if (cap_limit_get(chan, &limits) < 0)
338 return (-1);
339 if (limits == NULL)
340 limits = nvlist_create(0);
341 else
342 limit_remove(limits, "type");
343 for (i = 0; i < ntypes; i++) {
344 n = snprintf(nvlname, sizeof(nvlname), "type%u", i);
345 assert(n > 0 && n < (int)sizeof(nvlname));
346 nvlist_add_string(limits, nvlname, types[i]);
347 }
348 return (cap_limit_set(chan, limits));
349}
350
351int
352cap_dns_family_limit(cap_channel_t *chan, const int *families,
353 size_t nfamilies)
354{
355 nvlist_t *limits;
356 unsigned int i;
357 char nvlname[64];
358 int n;
359
360 if (cap_limit_get(chan, &limits) < 0)
361 return (-1);
362 if (limits == NULL)
363 limits = nvlist_create(0);
364 else
365 limit_remove(limits, "family");
366 for (i = 0; i < nfamilies; i++) {
367 n = snprintf(nvlname, sizeof(nvlname), "family%u", i);
368 assert(n > 0 && n < (int)sizeof(nvlname));
369 nvlist_add_number(limits, nvlname, (uint64_t)families[i]);
370 }
371 return (cap_limit_set(chan, limits));
372}
373
374/*
375 * Service functions.
376 */
377static bool
378dns_allowed_type(const nvlist_t *limits, const char *type)
379{
380 const char *name;
381 bool notypes;
382 void *cookie;
383
384 if (limits == NULL)
385 return (true);
386
387 notypes = true;
388 cookie = NULL;
389 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
390 if (strncmp(name, "type", sizeof("type") - 1) != 0)
391 continue;
392 notypes = false;
393 if (strcmp(nvlist_get_string(limits, name), type) == 0)
394 return (true);
395 }
396
397 /* If there are no types at all, allow any type. */
398 if (notypes)
399 return (true);
400
401 return (false);
402}
403
404static bool
405dns_allowed_family(const nvlist_t *limits, int family)
406{
407 const char *name;
408 bool nofamilies;
409 void *cookie;
410
411 if (limits == NULL)
412 return (true);
413
414 nofamilies = true;
415 cookie = NULL;
416 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
417 if (strncmp(name, "family", sizeof("family") - 1) != 0)
418 continue;
419 nofamilies = false;
420 if (family == AF_UNSPEC)
421 continue;
422 if (nvlist_get_number(limits, name) == (uint64_t)family)
423 return (true);
424 }
425
426 /* If there are no families at all, allow any family. */
427 if (nofamilies)
428 return (true);
429
430 return (false);
431}
432
433static void
434hostent_pack(const struct hostent *hp, nvlist_t *nvl)
435{
436 unsigned int ii;
437 char nvlname[64];
438 int n;
439
440 nvlist_add_string(nvl, "name", hp->h_name);
441 nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
442 nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
443
444 if (hp->h_aliases == NULL) {
445 nvlist_add_number(nvl, "naliases", 0);
446 } else {
447 for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
448 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
449 assert(n > 0 && n < (int)sizeof(nvlname));
450 nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
451 }
452 nvlist_add_number(nvl, "naliases", (uint64_t)ii);
453 }
454
455 if (hp->h_addr_list == NULL) {
456 nvlist_add_number(nvl, "naddrs", 0);
457 } else {
458 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
459 n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
460 assert(n > 0 && n < (int)sizeof(nvlname));
461 nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
462 (size_t)hp->h_length);
463 }
464 nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
465 }
466}
467
468static int
469dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
470 nvlist_t *nvlout)
471{
472 struct hostent *hp;
473 int family;
474
475 if (!dns_allowed_type(limits, "NAME"))
476 return (NO_RECOVERY);
477
478 family = (int)nvlist_get_number(nvlin, "family");
479
480 if (!dns_allowed_family(limits, family))
481 return (NO_RECOVERY);
482
483 hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
484 if (hp == NULL)
485 return (h_errno);
486 hostent_pack(hp, nvlout);
487 return (0);
488}
489
490static int
491dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
492 nvlist_t *nvlout)
493{
494 struct hostent *hp;
495 const void *addr;
496 size_t addrsize;
497 int family;
498
499 if (!dns_allowed_type(limits, "ADDR"))
500 return (NO_RECOVERY);
501
502 family = (int)nvlist_get_number(nvlin, "family");
503
504 if (!dns_allowed_family(limits, family))
505 return (NO_RECOVERY);
506
507 addr = nvlist_get_binary(nvlin, "addr", &addrsize);
508 hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
509 if (hp == NULL)
510 return (h_errno);
511 hostent_pack(hp, nvlout);
512 return (0);
513}
514
515static int
516dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
517{
518 struct sockaddr_storage sast;
519 const void *sabin;
520 char *host, *serv;
521 size_t sabinsize, hostlen, servlen;
522 socklen_t salen;
523 int error, flags;
524
525 if (!dns_allowed_type(limits, "NAME"))
526 return (NO_RECOVERY);
527
528 error = 0;
529 host = serv = NULL;
530 memset(&sast, 0, sizeof(sast));
531
532 hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
533 servlen = (size_t)nvlist_get_number(nvlin, "servlen");
534
535 if (hostlen > 0) {
536 host = calloc(1, hostlen + 1);
537 if (host == NULL) {
538 error = EAI_MEMORY;
539 goto out;
540 }
541 }
542 if (servlen > 0) {
543 serv = calloc(1, servlen + 1);
544 if (serv == NULL) {
545 error = EAI_MEMORY;
546 goto out;
547 }
548 }
549
550 sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
551 if (sabinsize > sizeof(sast)) {
552 error = EAI_FAIL;
553 goto out;
554 }
555
556 memcpy(&sast, sabin, sabinsize);
557 salen = (socklen_t)sabinsize;
558
559 if ((sast.ss_family != AF_INET ||
560 salen != sizeof(struct sockaddr_in)) &&
561 (sast.ss_family != AF_INET6 ||
562 salen != sizeof(struct sockaddr_in6))) {
563 error = EAI_FAIL;
564 goto out;
565 }
566
567 if (!dns_allowed_family(limits, (int)sast.ss_family)) {
568 error = NO_RECOVERY;
569 goto out;
570 }
571
572 flags = (int)nvlist_get_number(nvlin, "flags");
573
574 error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
575 serv, servlen, flags);
576 if (error != 0)
577 goto out;
578
579 if (host != NULL)
580 nvlist_move_string(nvlout, "host", host);
581 if (serv != NULL)
582 nvlist_move_string(nvlout, "serv", serv);
583out:
584 if (error != 0) {
585 free(host);
586 free(serv);
587 }
588 return (error);
589}
590
591static nvlist_t *
592addrinfo_pack(const struct addrinfo *ai)
593{
594 nvlist_t *nvl;
595
596 nvl = nvlist_create(0);
597 nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
598 nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
599 nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
600 nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
601 nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
602 if (ai->ai_canonname != NULL)
603 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
604
605 return (nvl);
606}
607
608static int
609dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
610{
611 struct addrinfo hints, *hintsp, *res, *cur;
612 const char *hostname, *servname;
613 char nvlname[64];
614 nvlist_t *elem;
615 unsigned int ii;
616 int error, family, n;
617
618 if (!dns_allowed_type(limits, "ADDR"))
619 return (NO_RECOVERY);
620
621 hostname = dnvlist_get_string(nvlin, "hostname", NULL);
622 servname = dnvlist_get_string(nvlin, "servname", NULL);
623 if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
624 hints.ai_flags = (int)nvlist_get_number(nvlin,
625 "hints.ai_flags");
626 hints.ai_family = (int)nvlist_get_number(nvlin,
627 "hints.ai_family");
628 hints.ai_socktype = (int)nvlist_get_number(nvlin,
629 "hints.ai_socktype");
630 hints.ai_protocol = (int)nvlist_get_number(nvlin,
631 "hints.ai_protocol");
632 hints.ai_addrlen = 0;
633 hints.ai_addr = NULL;
634 hints.ai_canonname = NULL;
635 hints.ai_next = NULL;
636 hintsp = &hints;
637 family = hints.ai_family;
638 } else {
639 hintsp = NULL;
640 family = AF_UNSPEC;
641 }
642
643 if (!dns_allowed_family(limits, family))
644 return (NO_RECOVERY);
645
646 error = getaddrinfo(hostname, servname, hintsp, &res);
647 if (error != 0)
648 goto out;
649
650 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
651 elem = addrinfo_pack(cur);
652 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
653 assert(n > 0 && n < (int)sizeof(nvlname));
654 nvlist_move_nvlist(nvlout, nvlname, elem);
655 }
656
657 freeaddrinfo(res);
658 error = 0;
659out:
660 return (error);
661}
662
663static bool
664limit_has_entry(const nvlist_t *limits, const char *prefix)
665{
666 const char *name;
667 size_t prefixlen;
668 void *cookie;
669
670 if (limits == NULL)
671 return (false);
672
673 prefixlen = strlen(prefix);
674
675 cookie = NULL;
676 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
677 if (strncmp(name, prefix, prefixlen) == 0)
678 return (true);
679 }
680
681 return (false);
682}
683
684static int
685dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
686{
687 const char *name;
688 void *cookie;
689 int nvtype;
690 bool hastype, hasfamily;
691
692 hastype = false;
693 hasfamily = false;
694
695 cookie = NULL;
696 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
697 if (nvtype == NV_TYPE_STRING) {
698 const char *type;
699
700 if (strncmp(name, "type", sizeof("type") - 1) != 0)
701 return (EINVAL);
702 type = nvlist_get_string(newlimits, name);
703 if (strcmp(type, "ADDR") != 0 &&
704 strcmp(type, "NAME") != 0) {
705 return (EINVAL);
706 }
707 if (!dns_allowed_type(oldlimits, type))
708 return (ENOTCAPABLE);
709 hastype = true;
710 } else if (nvtype == NV_TYPE_NUMBER) {
711 int family;
712
713 if (strncmp(name, "family", sizeof("family") - 1) != 0)
714 return (EINVAL);
715 family = (int)nvlist_get_number(newlimits, name);
716 if (!dns_allowed_family(oldlimits, family))
717 return (ENOTCAPABLE);
718 hasfamily = true;
719 } else {
720 return (EINVAL);
721 }
722 }
723
724 /*
725 * If the new limit doesn't mention type or family we have to
726 * check if the current limit does have those. Missing type or
727 * family in the limit means that all types or families are
728 * allowed.
729 */
730 if (!hastype) {
731 if (limit_has_entry(oldlimits, "type"))
732 return (ENOTCAPABLE);
733 }
734 if (!hasfamily) {
735 if (limit_has_entry(oldlimits, "family"))
736 return (ENOTCAPABLE);
737 }
738
739 return (0);
740}
741
742static int
743dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
744 nvlist_t *nvlout)
745{
746 int error;
747
748 if (strcmp(cmd, "gethostbyname") == 0)
749 error = dns_gethostbyname(limits, nvlin, nvlout);
750 else if (strcmp(cmd, "gethostbyaddr") == 0)
751 error = dns_gethostbyaddr(limits, nvlin, nvlout);
752 else if (strcmp(cmd, "getnameinfo") == 0)
753 error = dns_getnameinfo(limits, nvlin, nvlout);
754 else if (strcmp(cmd, "getaddrinfo") == 0)
755 error = dns_getaddrinfo(limits, nvlin, nvlout);
756 else
757 error = NO_RECOVERY;
758
759 return (error);
760}
761
32
33#include <sys/dnv.h>
34#include <sys/nv.h>
35#include <netinet/in.h>
36
37#include <assert.h>
38#include <errno.h>
39#include <netdb.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <libcasper.h>
45#include <libcasper_service.h>
46
47#include "cap_dns.h"
48
49static struct hostent hent;
50
51static void
52hostent_free(struct hostent *hp)
53{
54 unsigned int ii;
55
56 free(hp->h_name);
57 hp->h_name = NULL;
58 if (hp->h_aliases != NULL) {
59 for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
60 free(hp->h_aliases[ii]);
61 free(hp->h_aliases);
62 hp->h_aliases = NULL;
63 }
64 if (hp->h_addr_list != NULL) {
65 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
66 free(hp->h_addr_list[ii]);
67 free(hp->h_addr_list);
68 hp->h_addr_list = NULL;
69 }
70}
71
72static struct hostent *
73hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
74{
75 unsigned int ii, nitems;
76 char nvlname[64];
77 int n;
78
79 hostent_free(hp);
80
81 hp->h_name = strdup(nvlist_get_string(nvl, "name"));
82 if (hp->h_name == NULL)
83 goto fail;
84 hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
85 hp->h_length = (int)nvlist_get_number(nvl, "length");
86
87 nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
88 hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
89 if (hp->h_aliases == NULL)
90 goto fail;
91 for (ii = 0; ii < nitems; ii++) {
92 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
93 assert(n > 0 && n < (int)sizeof(nvlname));
94 hp->h_aliases[ii] =
95 strdup(nvlist_get_string(nvl, nvlname));
96 if (hp->h_aliases[ii] == NULL)
97 goto fail;
98 }
99 hp->h_aliases[ii] = NULL;
100
101 nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
102 hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
103 if (hp->h_addr_list == NULL)
104 goto fail;
105 for (ii = 0; ii < nitems; ii++) {
106 hp->h_addr_list[ii] = malloc(hp->h_length);
107 if (hp->h_addr_list[ii] == NULL)
108 goto fail;
109 n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
110 assert(n > 0 && n < (int)sizeof(nvlname));
111 bcopy(nvlist_get_binary(nvl, nvlname, NULL),
112 hp->h_addr_list[ii], hp->h_length);
113 }
114 hp->h_addr_list[ii] = NULL;
115
116 return (hp);
117fail:
118 hostent_free(hp);
119 h_errno = NO_RECOVERY;
120 return (NULL);
121}
122
123struct hostent *
124cap_gethostbyname(cap_channel_t *chan, const char *name)
125{
126
127 return (cap_gethostbyname2(chan, name, AF_INET));
128}
129
130struct hostent *
131cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
132{
133 struct hostent *hp;
134 nvlist_t *nvl;
135
136 nvl = nvlist_create(0);
137 nvlist_add_string(nvl, "cmd", "gethostbyname");
138 nvlist_add_number(nvl, "family", (uint64_t)type);
139 nvlist_add_string(nvl, "name", name);
140 nvl = cap_xfer_nvlist(chan, nvl, 0);
141 if (nvl == NULL) {
142 h_errno = NO_RECOVERY;
143 return (NULL);
144 }
145 if (nvlist_get_number(nvl, "error") != 0) {
146 h_errno = (int)nvlist_get_number(nvl, "error");
147 nvlist_destroy(nvl);
148 return (NULL);
149 }
150
151 hp = hostent_unpack(nvl, &hent);
152 nvlist_destroy(nvl);
153 return (hp);
154}
155
156struct hostent *
157cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
158 int type)
159{
160 struct hostent *hp;
161 nvlist_t *nvl;
162
163 nvl = nvlist_create(0);
164 nvlist_add_string(nvl, "cmd", "gethostbyaddr");
165 nvlist_add_binary(nvl, "addr", addr, (size_t)len);
166 nvlist_add_number(nvl, "family", (uint64_t)type);
167 nvl = cap_xfer_nvlist(chan, nvl, 0);
168 if (nvl == NULL) {
169 h_errno = NO_RECOVERY;
170 return (NULL);
171 }
172 if (nvlist_get_number(nvl, "error") != 0) {
173 h_errno = (int)nvlist_get_number(nvl, "error");
174 nvlist_destroy(nvl);
175 return (NULL);
176 }
177 hp = hostent_unpack(nvl, &hent);
178 nvlist_destroy(nvl);
179 return (hp);
180}
181
182static struct addrinfo *
183addrinfo_unpack(const nvlist_t *nvl)
184{
185 struct addrinfo *ai;
186 const void *addr;
187 size_t addrlen;
188 const char *canonname;
189
190 addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
191 ai = malloc(sizeof(*ai) + addrlen);
192 if (ai == NULL)
193 return (NULL);
194 ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
195 ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
196 ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
197 ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
198 ai->ai_addrlen = (socklen_t)addrlen;
199 canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
200 if (canonname != NULL) {
201 ai->ai_canonname = strdup(canonname);
202 if (ai->ai_canonname == NULL) {
203 free(ai);
204 return (NULL);
205 }
206 } else {
207 ai->ai_canonname = NULL;
208 }
209 ai->ai_addr = (void *)(ai + 1);
210 bcopy(addr, ai->ai_addr, addrlen);
211 ai->ai_next = NULL;
212
213 return (ai);
214}
215
216int
217cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
218 const struct addrinfo *hints, struct addrinfo **res)
219{
220 struct addrinfo *firstai, *prevai, *curai;
221 unsigned int ii;
222 const nvlist_t *nvlai;
223 char nvlname[64];
224 nvlist_t *nvl;
225 int error, n;
226
227 nvl = nvlist_create(0);
228 nvlist_add_string(nvl, "cmd", "getaddrinfo");
229 if (hostname != NULL)
230 nvlist_add_string(nvl, "hostname", hostname);
231 if (servname != NULL)
232 nvlist_add_string(nvl, "servname", servname);
233 if (hints != NULL) {
234 nvlist_add_number(nvl, "hints.ai_flags",
235 (uint64_t)hints->ai_flags);
236 nvlist_add_number(nvl, "hints.ai_family",
237 (uint64_t)hints->ai_family);
238 nvlist_add_number(nvl, "hints.ai_socktype",
239 (uint64_t)hints->ai_socktype);
240 nvlist_add_number(nvl, "hints.ai_protocol",
241 (uint64_t)hints->ai_protocol);
242 }
243 nvl = cap_xfer_nvlist(chan, nvl, 0);
244 if (nvl == NULL)
245 return (EAI_MEMORY);
246 if (nvlist_get_number(nvl, "error") != 0) {
247 error = (int)nvlist_get_number(nvl, "error");
248 nvlist_destroy(nvl);
249 return (error);
250 }
251
252 nvlai = NULL;
253 firstai = prevai = curai = NULL;
254 for (ii = 0; ; ii++) {
255 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
256 assert(n > 0 && n < (int)sizeof(nvlname));
257 if (!nvlist_exists_nvlist(nvl, nvlname))
258 break;
259 nvlai = nvlist_get_nvlist(nvl, nvlname);
260 curai = addrinfo_unpack(nvlai);
261 if (curai == NULL)
262 break;
263 if (prevai != NULL)
264 prevai->ai_next = curai;
265 else if (firstai == NULL)
266 firstai = curai;
267 prevai = curai;
268 }
269 nvlist_destroy(nvl);
270 if (curai == NULL && nvlai != NULL) {
271 if (firstai == NULL)
272 freeaddrinfo(firstai);
273 return (EAI_MEMORY);
274 }
275
276 *res = firstai;
277 return (0);
278}
279
280int
281cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
282 char *host, size_t hostlen, char *serv, size_t servlen, int flags)
283{
284 nvlist_t *nvl;
285 int error;
286
287 nvl = nvlist_create(0);
288 nvlist_add_string(nvl, "cmd", "getnameinfo");
289 nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
290 nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
291 nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
292 nvlist_add_number(nvl, "flags", (uint64_t)flags);
293 nvl = cap_xfer_nvlist(chan, nvl, 0);
294 if (nvl == NULL)
295 return (EAI_MEMORY);
296 if (nvlist_get_number(nvl, "error") != 0) {
297 error = (int)nvlist_get_number(nvl, "error");
298 nvlist_destroy(nvl);
299 return (error);
300 }
301
302 if (host != NULL && nvlist_exists_string(nvl, "host"))
303 strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
304 if (serv != NULL && nvlist_exists_string(nvl, "serv"))
305 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
306 nvlist_destroy(nvl);
307 return (0);
308}
309
310static void
311limit_remove(nvlist_t *limits, const char *prefix)
312{
313 const char *name;
314 size_t prefixlen;
315 void *cookie;
316
317 prefixlen = strlen(prefix);
318again:
319 cookie = NULL;
320 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
321 if (strncmp(name, prefix, prefixlen) == 0) {
322 nvlist_free(limits, name);
323 goto again;
324 }
325 }
326}
327
328int
329cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
330 size_t ntypes)
331{
332 nvlist_t *limits;
333 unsigned int i;
334 char nvlname[64];
335 int n;
336
337 if (cap_limit_get(chan, &limits) < 0)
338 return (-1);
339 if (limits == NULL)
340 limits = nvlist_create(0);
341 else
342 limit_remove(limits, "type");
343 for (i = 0; i < ntypes; i++) {
344 n = snprintf(nvlname, sizeof(nvlname), "type%u", i);
345 assert(n > 0 && n < (int)sizeof(nvlname));
346 nvlist_add_string(limits, nvlname, types[i]);
347 }
348 return (cap_limit_set(chan, limits));
349}
350
351int
352cap_dns_family_limit(cap_channel_t *chan, const int *families,
353 size_t nfamilies)
354{
355 nvlist_t *limits;
356 unsigned int i;
357 char nvlname[64];
358 int n;
359
360 if (cap_limit_get(chan, &limits) < 0)
361 return (-1);
362 if (limits == NULL)
363 limits = nvlist_create(0);
364 else
365 limit_remove(limits, "family");
366 for (i = 0; i < nfamilies; i++) {
367 n = snprintf(nvlname, sizeof(nvlname), "family%u", i);
368 assert(n > 0 && n < (int)sizeof(nvlname));
369 nvlist_add_number(limits, nvlname, (uint64_t)families[i]);
370 }
371 return (cap_limit_set(chan, limits));
372}
373
374/*
375 * Service functions.
376 */
377static bool
378dns_allowed_type(const nvlist_t *limits, const char *type)
379{
380 const char *name;
381 bool notypes;
382 void *cookie;
383
384 if (limits == NULL)
385 return (true);
386
387 notypes = true;
388 cookie = NULL;
389 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
390 if (strncmp(name, "type", sizeof("type") - 1) != 0)
391 continue;
392 notypes = false;
393 if (strcmp(nvlist_get_string(limits, name), type) == 0)
394 return (true);
395 }
396
397 /* If there are no types at all, allow any type. */
398 if (notypes)
399 return (true);
400
401 return (false);
402}
403
404static bool
405dns_allowed_family(const nvlist_t *limits, int family)
406{
407 const char *name;
408 bool nofamilies;
409 void *cookie;
410
411 if (limits == NULL)
412 return (true);
413
414 nofamilies = true;
415 cookie = NULL;
416 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
417 if (strncmp(name, "family", sizeof("family") - 1) != 0)
418 continue;
419 nofamilies = false;
420 if (family == AF_UNSPEC)
421 continue;
422 if (nvlist_get_number(limits, name) == (uint64_t)family)
423 return (true);
424 }
425
426 /* If there are no families at all, allow any family. */
427 if (nofamilies)
428 return (true);
429
430 return (false);
431}
432
433static void
434hostent_pack(const struct hostent *hp, nvlist_t *nvl)
435{
436 unsigned int ii;
437 char nvlname[64];
438 int n;
439
440 nvlist_add_string(nvl, "name", hp->h_name);
441 nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
442 nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
443
444 if (hp->h_aliases == NULL) {
445 nvlist_add_number(nvl, "naliases", 0);
446 } else {
447 for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
448 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
449 assert(n > 0 && n < (int)sizeof(nvlname));
450 nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
451 }
452 nvlist_add_number(nvl, "naliases", (uint64_t)ii);
453 }
454
455 if (hp->h_addr_list == NULL) {
456 nvlist_add_number(nvl, "naddrs", 0);
457 } else {
458 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
459 n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
460 assert(n > 0 && n < (int)sizeof(nvlname));
461 nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
462 (size_t)hp->h_length);
463 }
464 nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
465 }
466}
467
468static int
469dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
470 nvlist_t *nvlout)
471{
472 struct hostent *hp;
473 int family;
474
475 if (!dns_allowed_type(limits, "NAME"))
476 return (NO_RECOVERY);
477
478 family = (int)nvlist_get_number(nvlin, "family");
479
480 if (!dns_allowed_family(limits, family))
481 return (NO_RECOVERY);
482
483 hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
484 if (hp == NULL)
485 return (h_errno);
486 hostent_pack(hp, nvlout);
487 return (0);
488}
489
490static int
491dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
492 nvlist_t *nvlout)
493{
494 struct hostent *hp;
495 const void *addr;
496 size_t addrsize;
497 int family;
498
499 if (!dns_allowed_type(limits, "ADDR"))
500 return (NO_RECOVERY);
501
502 family = (int)nvlist_get_number(nvlin, "family");
503
504 if (!dns_allowed_family(limits, family))
505 return (NO_RECOVERY);
506
507 addr = nvlist_get_binary(nvlin, "addr", &addrsize);
508 hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
509 if (hp == NULL)
510 return (h_errno);
511 hostent_pack(hp, nvlout);
512 return (0);
513}
514
515static int
516dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
517{
518 struct sockaddr_storage sast;
519 const void *sabin;
520 char *host, *serv;
521 size_t sabinsize, hostlen, servlen;
522 socklen_t salen;
523 int error, flags;
524
525 if (!dns_allowed_type(limits, "NAME"))
526 return (NO_RECOVERY);
527
528 error = 0;
529 host = serv = NULL;
530 memset(&sast, 0, sizeof(sast));
531
532 hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
533 servlen = (size_t)nvlist_get_number(nvlin, "servlen");
534
535 if (hostlen > 0) {
536 host = calloc(1, hostlen + 1);
537 if (host == NULL) {
538 error = EAI_MEMORY;
539 goto out;
540 }
541 }
542 if (servlen > 0) {
543 serv = calloc(1, servlen + 1);
544 if (serv == NULL) {
545 error = EAI_MEMORY;
546 goto out;
547 }
548 }
549
550 sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
551 if (sabinsize > sizeof(sast)) {
552 error = EAI_FAIL;
553 goto out;
554 }
555
556 memcpy(&sast, sabin, sabinsize);
557 salen = (socklen_t)sabinsize;
558
559 if ((sast.ss_family != AF_INET ||
560 salen != sizeof(struct sockaddr_in)) &&
561 (sast.ss_family != AF_INET6 ||
562 salen != sizeof(struct sockaddr_in6))) {
563 error = EAI_FAIL;
564 goto out;
565 }
566
567 if (!dns_allowed_family(limits, (int)sast.ss_family)) {
568 error = NO_RECOVERY;
569 goto out;
570 }
571
572 flags = (int)nvlist_get_number(nvlin, "flags");
573
574 error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
575 serv, servlen, flags);
576 if (error != 0)
577 goto out;
578
579 if (host != NULL)
580 nvlist_move_string(nvlout, "host", host);
581 if (serv != NULL)
582 nvlist_move_string(nvlout, "serv", serv);
583out:
584 if (error != 0) {
585 free(host);
586 free(serv);
587 }
588 return (error);
589}
590
591static nvlist_t *
592addrinfo_pack(const struct addrinfo *ai)
593{
594 nvlist_t *nvl;
595
596 nvl = nvlist_create(0);
597 nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
598 nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
599 nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
600 nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
601 nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
602 if (ai->ai_canonname != NULL)
603 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
604
605 return (nvl);
606}
607
608static int
609dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
610{
611 struct addrinfo hints, *hintsp, *res, *cur;
612 const char *hostname, *servname;
613 char nvlname[64];
614 nvlist_t *elem;
615 unsigned int ii;
616 int error, family, n;
617
618 if (!dns_allowed_type(limits, "ADDR"))
619 return (NO_RECOVERY);
620
621 hostname = dnvlist_get_string(nvlin, "hostname", NULL);
622 servname = dnvlist_get_string(nvlin, "servname", NULL);
623 if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
624 hints.ai_flags = (int)nvlist_get_number(nvlin,
625 "hints.ai_flags");
626 hints.ai_family = (int)nvlist_get_number(nvlin,
627 "hints.ai_family");
628 hints.ai_socktype = (int)nvlist_get_number(nvlin,
629 "hints.ai_socktype");
630 hints.ai_protocol = (int)nvlist_get_number(nvlin,
631 "hints.ai_protocol");
632 hints.ai_addrlen = 0;
633 hints.ai_addr = NULL;
634 hints.ai_canonname = NULL;
635 hints.ai_next = NULL;
636 hintsp = &hints;
637 family = hints.ai_family;
638 } else {
639 hintsp = NULL;
640 family = AF_UNSPEC;
641 }
642
643 if (!dns_allowed_family(limits, family))
644 return (NO_RECOVERY);
645
646 error = getaddrinfo(hostname, servname, hintsp, &res);
647 if (error != 0)
648 goto out;
649
650 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
651 elem = addrinfo_pack(cur);
652 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
653 assert(n > 0 && n < (int)sizeof(nvlname));
654 nvlist_move_nvlist(nvlout, nvlname, elem);
655 }
656
657 freeaddrinfo(res);
658 error = 0;
659out:
660 return (error);
661}
662
663static bool
664limit_has_entry(const nvlist_t *limits, const char *prefix)
665{
666 const char *name;
667 size_t prefixlen;
668 void *cookie;
669
670 if (limits == NULL)
671 return (false);
672
673 prefixlen = strlen(prefix);
674
675 cookie = NULL;
676 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
677 if (strncmp(name, prefix, prefixlen) == 0)
678 return (true);
679 }
680
681 return (false);
682}
683
684static int
685dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
686{
687 const char *name;
688 void *cookie;
689 int nvtype;
690 bool hastype, hasfamily;
691
692 hastype = false;
693 hasfamily = false;
694
695 cookie = NULL;
696 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
697 if (nvtype == NV_TYPE_STRING) {
698 const char *type;
699
700 if (strncmp(name, "type", sizeof("type") - 1) != 0)
701 return (EINVAL);
702 type = nvlist_get_string(newlimits, name);
703 if (strcmp(type, "ADDR") != 0 &&
704 strcmp(type, "NAME") != 0) {
705 return (EINVAL);
706 }
707 if (!dns_allowed_type(oldlimits, type))
708 return (ENOTCAPABLE);
709 hastype = true;
710 } else if (nvtype == NV_TYPE_NUMBER) {
711 int family;
712
713 if (strncmp(name, "family", sizeof("family") - 1) != 0)
714 return (EINVAL);
715 family = (int)nvlist_get_number(newlimits, name);
716 if (!dns_allowed_family(oldlimits, family))
717 return (ENOTCAPABLE);
718 hasfamily = true;
719 } else {
720 return (EINVAL);
721 }
722 }
723
724 /*
725 * If the new limit doesn't mention type or family we have to
726 * check if the current limit does have those. Missing type or
727 * family in the limit means that all types or families are
728 * allowed.
729 */
730 if (!hastype) {
731 if (limit_has_entry(oldlimits, "type"))
732 return (ENOTCAPABLE);
733 }
734 if (!hasfamily) {
735 if (limit_has_entry(oldlimits, "family"))
736 return (ENOTCAPABLE);
737 }
738
739 return (0);
740}
741
742static int
743dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
744 nvlist_t *nvlout)
745{
746 int error;
747
748 if (strcmp(cmd, "gethostbyname") == 0)
749 error = dns_gethostbyname(limits, nvlin, nvlout);
750 else if (strcmp(cmd, "gethostbyaddr") == 0)
751 error = dns_gethostbyaddr(limits, nvlin, nvlout);
752 else if (strcmp(cmd, "getnameinfo") == 0)
753 error = dns_getnameinfo(limits, nvlin, nvlout);
754 else if (strcmp(cmd, "getaddrinfo") == 0)
755 error = dns_getaddrinfo(limits, nvlin, nvlout);
756 else
757 error = NO_RECOVERY;
758
759 return (error);
760}
761
762CREATE_SERVICE("system.dns", dns_limit, dns_command);
762CREATE_SERVICE("system.dns", dns_limit, dns_command, 0);