2 * Copyright (c) 2012-2013 The FreeBSD Foundation
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
35 #include <netinet/in.h>
44 #include <libcasper.h>
45 #include <libcasper_service.h>
49 static struct hostent hent;
52 hostent_free(struct hostent *hp)
58 if (hp->h_aliases != NULL) {
59 for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
60 free(hp->h_aliases[ii]);
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;
72 static struct hostent *
73 hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
75 unsigned int ii, nitems;
81 hp->h_name = strdup(nvlist_get_string(nvl, "name"));
82 if (hp->h_name == NULL)
84 hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
85 hp->h_length = (int)nvlist_get_number(nvl, "length");
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)
91 for (ii = 0; ii < nitems; ii++) {
92 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
93 assert(n > 0 && n < (int)sizeof(nvlname));
95 strdup(nvlist_get_string(nvl, nvlname));
96 if (hp->h_aliases[ii] == NULL)
99 hp->h_aliases[ii] = NULL;
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)
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)
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);
114 hp->h_addr_list[ii] = NULL;
119 h_errno = NO_RECOVERY;
124 cap_gethostbyname(cap_channel_t *chan, const char *name)
127 return (cap_gethostbyname2(chan, name, AF_INET));
131 cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
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);
142 h_errno = NO_RECOVERY;
145 if (nvlist_get_number(nvl, "error") != 0) {
146 h_errno = (int)nvlist_get_number(nvl, "error");
151 hp = hostent_unpack(nvl, &hent);
157 cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
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);
169 h_errno = NO_RECOVERY;
172 if (nvlist_get_number(nvl, "error") != 0) {
173 h_errno = (int)nvlist_get_number(nvl, "error");
177 hp = hostent_unpack(nvl, &hent);
182 static struct addrinfo *
183 addrinfo_unpack(const nvlist_t *nvl)
188 const char *canonname;
190 addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
191 ai = malloc(sizeof(*ai) + addrlen);
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) {
207 ai->ai_canonname = NULL;
209 ai->ai_addr = (void *)(ai + 1);
210 bcopy(addr, ai->ai_addr, addrlen);
217 cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
218 const struct addrinfo *hints, struct addrinfo **res)
220 struct addrinfo *firstai, *prevai, *curai;
222 const nvlist_t *nvlai;
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);
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);
243 nvl = cap_xfer_nvlist(chan, nvl, 0);
246 if (nvlist_get_number(nvl, "error") != 0) {
247 error = (int)nvlist_get_number(nvl, "error");
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))
259 nvlai = nvlist_get_nvlist(nvl, nvlname);
260 curai = addrinfo_unpack(nvlai);
264 prevai->ai_next = curai;
265 else if (firstai == NULL)
270 if (curai == NULL && nvlai != NULL) {
272 freeaddrinfo(firstai);
281 cap_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)
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);
296 if (nvlist_get_number(nvl, "error") != 0) {
297 error = (int)nvlist_get_number(nvl, "error");
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);
311 limit_remove(nvlist_t *limits, const char *prefix)
317 prefixlen = strlen(prefix);
320 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
321 if (strncmp(name, prefix, prefixlen) == 0) {
322 nvlist_free(limits, name);
329 cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
337 if (cap_limit_get(chan, &limits) < 0)
340 limits = nvlist_create(0);
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]);
348 return (cap_limit_set(chan, limits));
352 cap_dns_family_limit(cap_channel_t *chan, const int *families,
360 if (cap_limit_get(chan, &limits) < 0)
363 limits = nvlist_create(0);
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]);
371 return (cap_limit_set(chan, limits));
378 dns_allowed_type(const nvlist_t *limits, const char *type)
389 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
390 if (strncmp(name, "type", sizeof("type") - 1) != 0)
393 if (strcmp(nvlist_get_string(limits, name), type) == 0)
397 /* If there are no types at all, allow any type. */
405 dns_allowed_family(const nvlist_t *limits, int family)
416 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
417 if (strncmp(name, "family", sizeof("family") - 1) != 0)
420 if (family == AF_UNSPEC)
422 if (nvlist_get_number(limits, name) == (uint64_t)family)
426 /* If there are no families at all, allow any family. */
434 hostent_pack(const struct hostent *hp, nvlist_t *nvl)
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);
444 if (hp->h_aliases == NULL) {
445 nvlist_add_number(nvl, "naliases", 0);
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]);
452 nvlist_add_number(nvl, "naliases", (uint64_t)ii);
455 if (hp->h_addr_list == NULL) {
456 nvlist_add_number(nvl, "naddrs", 0);
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);
464 nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
469 dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
475 if (!dns_allowed_type(limits, "NAME"))
476 return (NO_RECOVERY);
478 family = (int)nvlist_get_number(nvlin, "family");
480 if (!dns_allowed_family(limits, family))
481 return (NO_RECOVERY);
483 hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
486 hostent_pack(hp, nvlout);
491 dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
499 if (!dns_allowed_type(limits, "ADDR"))
500 return (NO_RECOVERY);
502 family = (int)nvlist_get_number(nvlin, "family");
504 if (!dns_allowed_family(limits, family))
505 return (NO_RECOVERY);
507 addr = nvlist_get_binary(nvlin, "addr", &addrsize);
508 hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
511 hostent_pack(hp, nvlout);
516 dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
518 struct sockaddr_storage sast;
521 size_t sabinsize, hostlen, servlen;
525 if (!dns_allowed_type(limits, "NAME"))
526 return (NO_RECOVERY);
530 memset(&sast, 0, sizeof(sast));
532 hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
533 servlen = (size_t)nvlist_get_number(nvlin, "servlen");
536 host = calloc(1, hostlen + 1);
543 serv = calloc(1, servlen + 1);
550 sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
551 if (sabinsize > sizeof(sast)) {
556 memcpy(&sast, sabin, sabinsize);
557 salen = (socklen_t)sabinsize;
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))) {
567 if (!dns_allowed_family(limits, (int)sast.ss_family)) {
572 flags = (int)nvlist_get_number(nvlin, "flags");
574 error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
575 serv, servlen, flags);
580 nvlist_move_string(nvlout, "host", host);
582 nvlist_move_string(nvlout, "serv", serv);
592 addrinfo_pack(const struct addrinfo *ai)
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);
609 dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
611 struct addrinfo hints, *hintsp, *res, *cur;
612 const char *hostname, *servname;
616 int error, family, n;
618 if (!dns_allowed_type(limits, "ADDR"))
619 return (NO_RECOVERY);
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,
626 hints.ai_family = (int)nvlist_get_number(nvlin,
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;
636 family = hints.ai_family;
642 if (!dns_allowed_family(limits, family))
643 return (NO_RECOVERY);
645 error = getaddrinfo(hostname, servname, hintsp, &res);
649 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
650 elem = addrinfo_pack(cur);
651 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
652 assert(n > 0 && n < (int)sizeof(nvlname));
653 nvlist_move_nvlist(nvlout, nvlname, elem);
663 limit_has_entry(const nvlist_t *limits, const char *prefix)
672 prefixlen = strlen(prefix);
675 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
676 if (strncmp(name, prefix, prefixlen) == 0)
684 dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
689 bool hastype, hasfamily;
695 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
696 if (nvtype == NV_TYPE_STRING) {
699 if (strncmp(name, "type", sizeof("type") - 1) != 0)
701 type = nvlist_get_string(newlimits, name);
702 if (strcmp(type, "ADDR") != 0 &&
703 strcmp(type, "NAME") != 0) {
706 if (!dns_allowed_type(oldlimits, type))
707 return (ENOTCAPABLE);
709 } else if (nvtype == NV_TYPE_NUMBER) {
712 if (strncmp(name, "family", sizeof("family") - 1) != 0)
714 family = (int)nvlist_get_number(newlimits, name);
715 if (!dns_allowed_family(oldlimits, family))
716 return (ENOTCAPABLE);
724 * If the new limit doesn't mention type or family we have to
725 * check if the current limit does have those. Missing type or
726 * family in the limit means that all types or families are
730 if (limit_has_entry(oldlimits, "type"))
731 return (ENOTCAPABLE);
734 if (limit_has_entry(oldlimits, "family"))
735 return (ENOTCAPABLE);
742 dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
747 if (strcmp(cmd, "gethostbyname") == 0)
748 error = dns_gethostbyname(limits, nvlin, nvlout);
749 else if (strcmp(cmd, "gethostbyaddr") == 0)
750 error = dns_gethostbyaddr(limits, nvlin, nvlout);
751 else if (strcmp(cmd, "getnameinfo") == 0)
752 error = dns_getnameinfo(limits, nvlin, nvlout);
753 else if (strcmp(cmd, "getaddrinfo") == 0)
754 error = dns_getaddrinfo(limits, nvlin, nvlout);
761 CREATE_SERVICE("system.dns", dns_limit, dns_command);