]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_dns/cap_dns.c
zfs: merge openzfs/zfs@a94860a6d
[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 #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
49 static struct hostent hent;
50
51 static void
52 hostent_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
72 static struct hostent *
73 hostent_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);
117 fail:
118         hostent_free(hp);
119         h_errno = NO_RECOVERY;
120         return (NULL);
121 }
122
123 struct hostent *
124 cap_gethostbyname(cap_channel_t *chan, const char *name)
125 {
126
127         return (cap_gethostbyname2(chan, name, AF_INET));
128 }
129
130 struct hostent *
131 cap_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);
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
156 struct hostent *
157 cap_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);
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
182 static struct addrinfo *
183 addrinfo_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
216 int
217 cap_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);
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
280 int
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)
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);
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);
304         if (serv != NULL && nvlist_exists_string(nvl, "serv"))
305                 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen);
306         nvlist_destroy(nvl);
307         return (0);
308 }
309
310 static void
311 limit_remove(nvlist_t *limits, const char *prefix)
312 {
313         const char *name;
314         size_t prefixlen;
315         void *cookie;
316
317         prefixlen = strlen(prefix);
318 again:
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
328 int
329 cap_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
351 int
352 cap_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  */
377 static bool
378 dns_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
404 static bool
405 dns_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
433 static void
434 hostent_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
468 static int
469 dns_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, "NAME2ADDR") &&
476             !dns_allowed_type(limits, "NAME"))
477                 return (NO_RECOVERY);
478
479         family = (int)nvlist_get_number(nvlin, "family");
480
481         if (!dns_allowed_family(limits, family))
482                 return (NO_RECOVERY);
483
484         hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
485         if (hp == NULL)
486                 return (h_errno);
487         hostent_pack(hp, nvlout);
488         return (0);
489 }
490
491 static int
492 dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
493     nvlist_t *nvlout)
494 {
495         struct hostent *hp;
496         const void *addr;
497         size_t addrsize;
498         int family;
499
500         if (!dns_allowed_type(limits, "ADDR2NAME") &&
501             !dns_allowed_type(limits, "ADDR"))
502                 return (NO_RECOVERY);
503
504         family = (int)nvlist_get_number(nvlin, "family");
505
506         if (!dns_allowed_family(limits, family))
507                 return (NO_RECOVERY);
508
509         addr = nvlist_get_binary(nvlin, "addr", &addrsize);
510         hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
511         if (hp == NULL)
512                 return (h_errno);
513         hostent_pack(hp, nvlout);
514         return (0);
515 }
516
517 static int
518 dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
519 {
520         struct sockaddr_storage sast;
521         const void *sabin;
522         char *host, *serv;
523         size_t sabinsize, hostlen, servlen;
524         socklen_t salen;
525         int error, flags;
526
527         if (!dns_allowed_type(limits, "ADDR2NAME") &&
528             !dns_allowed_type(limits, "ADDR"))
529                 return (NO_RECOVERY);
530
531         error = 0;
532         host = serv = NULL;
533         memset(&sast, 0, sizeof(sast));
534
535         hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
536         servlen = (size_t)nvlist_get_number(nvlin, "servlen");
537
538         if (hostlen > 0) {
539                 host = calloc(1, hostlen);
540                 if (host == NULL) {
541                         error = EAI_MEMORY;
542                         goto out;
543                 }
544         }
545         if (servlen > 0) {
546                 serv = calloc(1, servlen);
547                 if (serv == NULL) {
548                         error = EAI_MEMORY;
549                         goto out;
550                 }
551         }
552
553         sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
554         if (sabinsize > sizeof(sast)) {
555                 error = EAI_FAIL;
556                 goto out;
557         }
558
559         memcpy(&sast, sabin, sabinsize);
560         salen = (socklen_t)sabinsize;
561
562         if ((sast.ss_family != AF_INET ||
563              salen != sizeof(struct sockaddr_in)) &&
564             (sast.ss_family != AF_INET6 ||
565              salen != sizeof(struct sockaddr_in6))) {
566                 error = EAI_FAIL;
567                 goto out;
568         }
569
570         if (!dns_allowed_family(limits, (int)sast.ss_family)) {
571                 error = NO_RECOVERY;
572                 goto out;
573         }
574
575         flags = (int)nvlist_get_number(nvlin, "flags");
576
577         error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
578             serv, servlen, flags);
579         if (error != 0)
580                 goto out;
581
582         if (host != NULL)
583                 nvlist_move_string(nvlout, "host", host);
584         if (serv != NULL)
585                 nvlist_move_string(nvlout, "serv", serv);
586 out:
587         if (error != 0) {
588                 free(host);
589                 free(serv);
590         }
591         return (error);
592 }
593
594 static nvlist_t *
595 addrinfo_pack(const struct addrinfo *ai)
596 {
597         nvlist_t *nvl;
598
599         nvl = nvlist_create(0);
600         nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
601         nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
602         nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
603         nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
604         nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
605         if (ai->ai_canonname != NULL)
606                 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
607
608         return (nvl);
609 }
610
611 static int
612 dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
613 {
614         struct addrinfo hints, *hintsp, *res, *cur;
615         const char *hostname, *servname;
616         char nvlname[64];
617         nvlist_t *elem;
618         unsigned int ii;
619         int error, family, n;
620
621         if (!dns_allowed_type(limits, "NAME2ADDR") &&
622             !dns_allowed_type(limits, "NAME"))
623                 return (NO_RECOVERY);
624
625         hostname = dnvlist_get_string(nvlin, "hostname", NULL);
626         servname = dnvlist_get_string(nvlin, "servname", NULL);
627         if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
628                 hints.ai_flags = (int)nvlist_get_number(nvlin,
629                     "hints.ai_flags");
630                 hints.ai_family = (int)nvlist_get_number(nvlin,
631                     "hints.ai_family");
632                 hints.ai_socktype = (int)nvlist_get_number(nvlin,
633                     "hints.ai_socktype");
634                 hints.ai_protocol = (int)nvlist_get_number(nvlin,
635                     "hints.ai_protocol");
636                 hints.ai_addrlen = 0;
637                 hints.ai_addr = NULL;
638                 hints.ai_canonname = NULL;
639                 hints.ai_next = NULL;
640                 hintsp = &hints;
641                 family = hints.ai_family;
642         } else {
643                 hintsp = NULL;
644                 family = AF_UNSPEC;
645         }
646
647         if (!dns_allowed_family(limits, family))
648                 return (NO_RECOVERY);
649
650         error = getaddrinfo(hostname, servname, hintsp, &res);
651         if (error != 0)
652                 goto out;
653
654         for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
655                 elem = addrinfo_pack(cur);
656                 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
657                 assert(n > 0 && n < (int)sizeof(nvlname));
658                 nvlist_move_nvlist(nvlout, nvlname, elem);
659         }
660
661         freeaddrinfo(res);
662         error = 0;
663 out:
664         return (error);
665 }
666
667 static bool
668 limit_has_entry(const nvlist_t *limits, const char *prefix)
669 {
670         const char *name;
671         size_t prefixlen;
672         void *cookie;
673
674         if (limits == NULL)
675                 return (false);
676
677         prefixlen = strlen(prefix);
678
679         cookie = NULL;
680         while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
681                 if (strncmp(name, prefix, prefixlen) == 0)
682                         return (true);
683         }
684
685         return (false);
686 }
687
688 static int
689 dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
690 {
691         const char *name;
692         void *cookie;
693         int nvtype;
694         bool hastype, hasfamily;
695
696         hastype = false;
697         hasfamily = false;
698
699         cookie = NULL;
700         while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
701                 if (nvtype == NV_TYPE_STRING) {
702                         const char *type;
703
704                         if (strncmp(name, "type", sizeof("type") - 1) != 0)
705                                 return (EINVAL);
706                         type = nvlist_get_string(newlimits, name);
707                         if (strcmp(type, "ADDR2NAME") != 0 &&
708                             strcmp(type, "NAME2ADDR") != 0 &&
709                             strcmp(type, "ADDR") != 0 &&
710                             strcmp(type, "NAME") != 0) {
711                                 return (EINVAL);
712                         }
713                         if (!dns_allowed_type(oldlimits, type))
714                                 return (ENOTCAPABLE);
715                         hastype = true;
716                 } else if (nvtype == NV_TYPE_NUMBER) {
717                         int family;
718
719                         if (strncmp(name, "family", sizeof("family") - 1) != 0)
720                                 return (EINVAL);
721                         family = (int)nvlist_get_number(newlimits, name);
722                         if (!dns_allowed_family(oldlimits, family))
723                                 return (ENOTCAPABLE);
724                         hasfamily = true;
725                 } else {
726                         return (EINVAL);
727                 }
728         }
729
730         /*
731          * If the new limit doesn't mention type or family we have to
732          * check if the current limit does have those. Missing type or
733          * family in the limit means that all types or families are
734          * allowed.
735          */
736         if (!hastype) {
737                 if (limit_has_entry(oldlimits, "type"))
738                         return (ENOTCAPABLE);
739         }
740         if (!hasfamily) {
741                 if (limit_has_entry(oldlimits, "family"))
742                         return (ENOTCAPABLE);
743         }
744
745         return (0);
746 }
747
748 static int
749 dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
750     nvlist_t *nvlout)
751 {
752         int error;
753
754         if (strcmp(cmd, "gethostbyname") == 0)
755                 error = dns_gethostbyname(limits, nvlin, nvlout);
756         else if (strcmp(cmd, "gethostbyaddr") == 0)
757                 error = dns_gethostbyaddr(limits, nvlin, nvlout);
758         else if (strcmp(cmd, "getnameinfo") == 0)
759                 error = dns_getnameinfo(limits, nvlin, nvlout);
760         else if (strcmp(cmd, "getaddrinfo") == 0)
761                 error = dns_getaddrinfo(limits, nvlin, nvlout);
762         else
763                 error = NO_RECOVERY;
764
765         return (error);
766 }
767
768 CREATE_SERVICE("system.dns", dns_limit, dns_command, 0);