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