]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_net/cap_net.c
libcasper: Introduce cap_net a network service for Casper.
[FreeBSD/FreeBSD.git] / lib / libcasper / services / cap_net / cap_net.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/cnv.h>
32 #include <sys/dnv.h>
33 #include <sys/nv.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include <libcasper.h>
45 #include <libcasper_service.h>
46
47 #include "cap_net.h"
48
49 #define CAPNET_MASK     (CAPNET_ADDR2NAME | CAPNET_NAME2ADDR    \
50     CAPNET_DEPRECATED_ADDR2NAME | CAPNET_DEPRECATED_NAME2ADDR | \
51     CAPNET_CONNECT | CAPNET_BIND | CAPNET_CONNECTDNS)
52
53 /*
54  * Defines for the names of the limits.
55  * XXX: we should convert all string constats to this to avoid typos.
56  */
57 #define LIMIT_NV_BIND                   "bind"
58 #define LIMIT_NV_CONNECT                "connect"
59 #define LIMIT_NV_ADDR2NAME              "addr2name"
60 #define LIMIT_NV_NAME2ADDR              "name2addr"
61
62 struct cap_net_limit {
63         cap_channel_t   *cnl_chan;
64         uint64_t         cnl_mode;
65         nvlist_t        *cnl_addr2name;
66         nvlist_t        *cnl_name2addr;
67         nvlist_t        *cnl_connect;
68         nvlist_t        *cnl_bind;
69 };
70
71 static struct hostent hent;
72
73 static void
74 hostent_free(struct hostent *hp)
75 {
76         unsigned int ii;
77
78         free(hp->h_name);
79         hp->h_name = NULL;
80         if (hp->h_aliases != NULL) {
81                 for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
82                         free(hp->h_aliases[ii]);
83                 free(hp->h_aliases);
84                 hp->h_aliases = NULL;
85         }
86         if (hp->h_addr_list != NULL) {
87                 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
88                         free(hp->h_addr_list[ii]);
89                 free(hp->h_addr_list);
90                 hp->h_addr_list = NULL;
91         }
92 }
93
94 static struct hostent *
95 hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
96 {
97         unsigned int ii, nitems;
98         char nvlname[64];
99         int n;
100
101         hostent_free(hp);
102
103         hp->h_name = strdup(nvlist_get_string(nvl, "name"));
104         if (hp->h_name == NULL)
105                 goto fail;
106         hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
107         hp->h_length = (int)nvlist_get_number(nvl, "length");
108
109         nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
110         hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
111         if (hp->h_aliases == NULL)
112                 goto fail;
113         for (ii = 0; ii < nitems; ii++) {
114                 n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
115                 assert(n > 0 && n < (int)sizeof(nvlname));
116                 hp->h_aliases[ii] =
117                     strdup(nvlist_get_string(nvl, nvlname));
118                 if (hp->h_aliases[ii] == NULL)
119                         goto fail;
120         }
121         hp->h_aliases[ii] = NULL;
122
123         nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
124         hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
125         if (hp->h_addr_list == NULL)
126                 goto fail;
127         for (ii = 0; ii < nitems; ii++) {
128                 hp->h_addr_list[ii] = malloc(hp->h_length);
129                 if (hp->h_addr_list[ii] == NULL)
130                         goto fail;
131                 n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
132                 assert(n > 0 && n < (int)sizeof(nvlname));
133                 bcopy(nvlist_get_binary(nvl, nvlname, NULL),
134                     hp->h_addr_list[ii], hp->h_length);
135         }
136         hp->h_addr_list[ii] = NULL;
137
138         return (hp);
139 fail:
140         hostent_free(hp);
141         h_errno = NO_RECOVERY;
142         return (NULL);
143 }
144
145 static int
146 request_cb(cap_channel_t *chan, const char *name, int s,
147     const struct sockaddr *saddr, socklen_t len)
148 {
149         nvlist_t *nvl;
150         int serrno;
151
152         nvl = nvlist_create(0);
153         nvlist_add_string(nvl, "cmd", name);
154         nvlist_add_descriptor(nvl, "s", s);
155         nvlist_add_binary(nvl, "saddr", saddr, len);
156
157         nvl = cap_xfer_nvlist(chan, nvl);
158         if (nvl == NULL)
159                 return (-1);
160
161         if (nvlist_get_number(nvl, "error") != 0) {
162                 serrno = (int)nvlist_get_number(nvl, "error");
163                 nvlist_destroy(nvl);
164                 errno = serrno;
165                 return (-1);
166         }
167
168         s = dup2(s, nvlist_get_descriptor(nvl, "s"));
169         nvlist_destroy(nvl);
170
171         return (s == -1 ? -1 : 0);
172 }
173
174 int
175 cap_bind(cap_channel_t *chan, int s, const struct sockaddr *addr,
176     socklen_t addrlen)
177 {
178
179         return (request_cb(chan, LIMIT_NV_BIND, s, addr, addrlen));
180 }
181
182 int
183 cap_connect(cap_channel_t *chan, int s, const struct sockaddr *name,
184     socklen_t namelen)
185 {
186
187         return (request_cb(chan, LIMIT_NV_CONNECT, s, name, namelen));
188 }
189
190
191 struct hostent *
192 cap_gethostbyname(cap_channel_t *chan, const char *name)
193 {
194
195         return (cap_gethostbyname2(chan, name, AF_INET));
196 }
197
198 struct hostent *
199 cap_gethostbyname2(cap_channel_t *chan, const char *name, int af)
200 {
201         struct hostent *hp;
202         nvlist_t *nvl;
203
204         nvl = nvlist_create(0);
205         nvlist_add_string(nvl, "cmd", "gethostbyname");
206         nvlist_add_number(nvl, "family", (uint64_t)af);
207         nvlist_add_string(nvl, "name", name);
208         nvl = cap_xfer_nvlist(chan, nvl);
209         if (nvl == NULL) {
210                 h_errno = NO_RECOVERY;
211                 return (NULL);
212         }
213         if (nvlist_get_number(nvl, "error") != 0) {
214                 h_errno = (int)nvlist_get_number(nvl, "error");
215                 nvlist_destroy(nvl);
216                 return (NULL);
217         }
218
219         hp = hostent_unpack(nvl, &hent);
220         nvlist_destroy(nvl);
221         return (hp);
222 }
223
224 struct hostent *
225 cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
226     int af)
227 {
228         struct hostent *hp;
229         nvlist_t *nvl;
230
231         nvl = nvlist_create(0);
232         nvlist_add_string(nvl, "cmd", "gethostbyaddr");
233         nvlist_add_binary(nvl, "addr", addr, (size_t)len);
234         nvlist_add_number(nvl, "family", (uint64_t)af);
235         nvl = cap_xfer_nvlist(chan, nvl);
236         if (nvl == NULL) {
237                 h_errno = NO_RECOVERY;
238                 return (NULL);
239         }
240         if (nvlist_get_number(nvl, "error") != 0) {
241                 h_errno = (int)nvlist_get_number(nvl, "error");
242                 nvlist_destroy(nvl);
243                 return (NULL);
244         }
245         hp = hostent_unpack(nvl, &hent);
246         nvlist_destroy(nvl);
247         return (hp);
248 }
249
250 static struct addrinfo *
251 addrinfo_unpack(const nvlist_t *nvl)
252 {
253         struct addrinfo *ai;
254         const void *addr;
255         size_t addrlen;
256         const char *canonname;
257
258         addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
259         ai = malloc(sizeof(*ai) + addrlen);
260         if (ai == NULL)
261                 return (NULL);
262         ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
263         ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
264         ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
265         ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
266         ai->ai_addrlen = (socklen_t)addrlen;
267         canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
268         if (canonname != NULL) {
269                 ai->ai_canonname = strdup(canonname);
270                 if (ai->ai_canonname == NULL) {
271                         free(ai);
272                         return (NULL);
273                 }
274         } else {
275                 ai->ai_canonname = NULL;
276         }
277         ai->ai_addr = (void *)(ai + 1);
278         bcopy(addr, ai->ai_addr, addrlen);
279         ai->ai_next = NULL;
280
281         return (ai);
282 }
283
284 int
285 cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
286     const struct addrinfo *hints, struct addrinfo **res)
287 {
288         struct addrinfo *firstai, *prevai, *curai;
289         unsigned int ii;
290         const nvlist_t *nvlai;
291         char nvlname[64];
292         nvlist_t *nvl;
293         int error, n;
294
295         nvl = nvlist_create(0);
296         nvlist_add_string(nvl, "cmd", "getaddrinfo");
297         if (hostname != NULL)
298                 nvlist_add_string(nvl, "hostname", hostname);
299         if (servname != NULL)
300                 nvlist_add_string(nvl, "servname", servname);
301         if (hints != NULL) {
302                 nvlist_add_number(nvl, "hints.ai_flags",
303                     (uint64_t)hints->ai_flags);
304                 nvlist_add_number(nvl, "hints.ai_family",
305                     (uint64_t)hints->ai_family);
306                 nvlist_add_number(nvl, "hints.ai_socktype",
307                     (uint64_t)hints->ai_socktype);
308                 nvlist_add_number(nvl, "hints.ai_protocol",
309                     (uint64_t)hints->ai_protocol);
310         }
311         nvl = cap_xfer_nvlist(chan, nvl);
312         if (nvl == NULL)
313                 return (EAI_MEMORY);
314         if (nvlist_get_number(nvl, "error") != 0) {
315                 error = (int)nvlist_get_number(nvl, "error");
316                 nvlist_destroy(nvl);
317                 return (error);
318         }
319
320         nvlai = NULL;
321         firstai = prevai = curai = NULL;
322         for (ii = 0; ; ii++) {
323                 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
324                 assert(n > 0 && n < (int)sizeof(nvlname));
325                 if (!nvlist_exists_nvlist(nvl, nvlname))
326                         break;
327                 nvlai = nvlist_get_nvlist(nvl, nvlname);
328                 curai = addrinfo_unpack(nvlai);
329                 if (curai == NULL)
330                         return (EAI_MEMORY);
331                 if (prevai != NULL)
332                         prevai->ai_next = curai;
333                 else
334                         firstai = curai;
335                 prevai = curai;
336         }
337         nvlist_destroy(nvl);
338         if (curai == NULL && nvlai != NULL) {
339                 if (firstai == NULL)
340                         freeaddrinfo(firstai);
341                 return (EAI_MEMORY);
342         }
343
344         *res = firstai;
345         return (0);
346 }
347
348 int
349 cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
350     char *host, size_t hostlen, char *serv, size_t servlen, int flags)
351 {
352         nvlist_t *nvl;
353         int error;
354
355         nvl = nvlist_create(0);
356         nvlist_add_string(nvl, "cmd", "getnameinfo");
357         nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
358         nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
359         nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
360         nvlist_add_number(nvl, "flags", (uint64_t)flags);
361         nvl = cap_xfer_nvlist(chan, nvl);
362         if (nvl == NULL)
363                 return (EAI_MEMORY);
364         if (nvlist_get_number(nvl, "error") != 0) {
365                 error = (int)nvlist_get_number(nvl, "error");
366                 nvlist_destroy(nvl);
367                 return (error);
368         }
369
370         if (host != NULL && nvlist_exists_string(nvl, "host"))
371                 strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
372         if (serv != NULL && nvlist_exists_string(nvl, "serv"))
373                 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
374         nvlist_destroy(nvl);
375         return (0);
376 }
377
378 cap_net_limit_t *
379 cap_net_limit_init(cap_channel_t *chan, uint64_t mode)
380 {
381         cap_net_limit_t *limit;
382
383         limit = calloc(1, sizeof(*limit));
384         if (limit != NULL) {
385                 limit->cnl_mode = mode;
386                 limit->cnl_chan = chan;
387                 limit->cnl_addr2name = nvlist_create(0);
388                 limit->cnl_name2addr = nvlist_create(0);
389                 limit->cnl_connect = nvlist_create(0);
390                 limit->cnl_bind = nvlist_create(0);
391         }
392
393         return (limit);
394 }
395
396 static void
397 pack_limit(nvlist_t *lnvl, const char *name, nvlist_t *limit)
398 {
399
400         if (!nvlist_empty(limit)) {
401                 nvlist_move_nvlist(lnvl, name, limit);
402         } else {
403                 nvlist_destroy(limit);
404         }
405 }
406
407 int
408 cap_net_limit(cap_net_limit_t *limit)
409 {
410         nvlist_t *lnvl;
411         cap_channel_t *chan;
412
413         lnvl = nvlist_create(0);
414         nvlist_add_number(lnvl, "mode", limit->cnl_mode);
415
416         pack_limit(lnvl, LIMIT_NV_ADDR2NAME, limit->cnl_addr2name);
417         pack_limit(lnvl, LIMIT_NV_NAME2ADDR, limit->cnl_name2addr);
418         pack_limit(lnvl, LIMIT_NV_CONNECT, limit->cnl_connect);
419         pack_limit(lnvl, LIMIT_NV_BIND, limit->cnl_bind);
420
421         chan = limit->cnl_chan;
422         free(limit);
423
424         return (cap_limit_set(chan, lnvl));
425 }
426
427 void
428 cap_net_free(cap_net_limit_t *limit)
429 {
430
431         if (limit == NULL)
432                 return;
433
434         nvlist_destroy(limit->cnl_addr2name);
435         nvlist_destroy(limit->cnl_name2addr);
436         nvlist_destroy(limit->cnl_connect);
437         nvlist_destroy(limit->cnl_bind);
438
439         free(limit);
440 }
441
442 static void
443 pack_family(nvlist_t *nvl, int *family, size_t size)
444 {
445         size_t i;
446
447         i = 0;
448         if (!nvlist_exists_number_array(nvl, "family")) {
449                 uint64_t val;
450
451                 val = family[0];
452                 nvlist_add_number_array(nvl, "family", &val, 1);
453                 i += 1;
454         }
455
456         for (; i < size; i++) {
457                 nvlist_append_number_array(nvl, "family", family[i]);
458         }
459 }
460
461 static void
462 pack_sockaddr(nvlist_t *res, const struct sockaddr *sa, socklen_t salen)
463 {
464         nvlist_t *nvl;
465
466         if (!nvlist_exists_nvlist(res, "sockaddr")) {
467                 nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
468         } else {
469                 nvl = nvlist_take_nvlist(res, "sockaddr");
470         }
471
472         nvlist_add_binary(nvl, "", sa, salen);
473         nvlist_move_nvlist(res, "sockaddr", nvl);
474 }
475
476 cap_net_limit_t *
477 cap_net_limit_addr2name_family(cap_net_limit_t *limit, int *family, size_t size)
478 {
479
480         pack_family(limit->cnl_addr2name, family, size);
481         return (limit);
482 }
483
484 cap_net_limit_t *
485 cap_net_limit_name2addr_family(cap_net_limit_t *limit, int *family, size_t size)
486 {
487
488         pack_family(limit->cnl_name2addr, family, size);
489         return (limit);
490 }
491
492 cap_net_limit_t *
493 cap_net_limit_name2addr(cap_net_limit_t *limit, const char *host,
494     const char *serv)
495 {
496         nvlist_t *nvl;
497
498         if (!nvlist_exists_nvlist(limit->cnl_name2addr, "hosts")) {
499                 nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
500         } else {
501                 nvl = nvlist_take_nvlist(limit->cnl_name2addr, "hosts");
502         }
503
504         nvlist_add_string(nvl,
505             host != NULL ? host : "",
506             serv != NULL ? serv : "");
507
508         nvlist_move_nvlist(limit->cnl_name2addr, "hosts", nvl);
509         return (limit);
510 }
511
512 cap_net_limit_t *
513 cap_net_limit_addr2name(cap_net_limit_t *limit, const struct sockaddr *sa,
514     socklen_t salen)
515 {
516
517         pack_sockaddr(limit->cnl_addr2name, sa, salen);
518         return (limit);
519 }
520
521
522 cap_net_limit_t *
523 cap_net_limit_connect(cap_net_limit_t *limit, const struct sockaddr *sa,
524     socklen_t salen)
525 {
526
527         pack_sockaddr(limit->cnl_connect, sa, salen);
528         return (limit);
529 }
530
531 cap_net_limit_t *
532 cap_net_limit_bind(cap_net_limit_t *limit, const struct sockaddr *sa,
533     socklen_t salen)
534 {
535
536         pack_sockaddr(limit->cnl_bind, sa, salen);
537         return (limit);
538 }
539
540 /*
541  * Service functions.
542  */
543
544 static nvlist_t *capdnscache;
545
546 static void
547 net_add_sockaddr_to_cache(struct sockaddr *sa, socklen_t salen, bool deprecated)
548 {
549         void *cookie;
550
551         if (capdnscache == NULL) {
552                 capdnscache = nvlist_create(NV_FLAG_NO_UNIQUE);
553         } else {
554                 /* Lets keep it clean. Look for dups. */
555                 cookie = NULL;
556                 while (nvlist_next(capdnscache, NULL, &cookie) != NULL) {
557                         const void *data;
558                         size_t size;
559
560                         assert(cnvlist_type(cookie) == NV_TYPE_BINARY);
561
562                         data = cnvlist_get_binary(cookie, &size);
563                         if (salen != size)
564                                 continue;
565                         if (memcmp(data, sa, size) == 0)
566                                 return;
567                 }
568         }
569
570         nvlist_add_binary(capdnscache, deprecated ? "d" : "", sa, salen);
571 }
572
573 static void
574 net_add_hostent_to_cache(const char *address, size_t asize, int family)
575 {
576
577         if (family != AF_INET && family != AF_INET6)
578                 return;
579
580         if (family == AF_INET6) {
581                 struct sockaddr_in6 connaddr;
582
583                 memset(&connaddr, 0, sizeof(connaddr));
584                 connaddr.sin6_family = AF_INET6;
585                 memcpy((char *)&connaddr.sin6_addr, address, asize);
586                 connaddr.sin6_port = 0;
587
588                 net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
589                     sizeof(connaddr), true);
590         } else {
591                 struct sockaddr_in connaddr;
592
593                 memset(&connaddr, 0, sizeof(connaddr));
594                 connaddr.sin_family = AF_INET;
595                 memcpy((char *)&connaddr.sin_addr.s_addr, address, asize);
596                 connaddr.sin_port = 0;
597
598                 net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
599                     sizeof(connaddr), true);
600         }
601 }
602
603 static bool
604 net_allowed_mode(const nvlist_t *limits, uint64_t mode)
605 {
606
607         if (limits == NULL)
608                 return (true);
609
610         return ((nvlist_get_number(limits, "mode") & mode) == mode);
611 }
612
613 static bool
614 net_allowed_family(const nvlist_t *limits, int family)
615 {
616         const uint64_t *allowedfamily;
617         size_t i, allsize;
618
619         if (limits == NULL)
620                 return (true);
621
622         /* If there are no familes at all, allow any mode. */
623         if (!nvlist_exists_number_array(limits, "family"))
624                 return (true);
625
626         allowedfamily = nvlist_get_number_array(limits, "family", &allsize);
627         for (i = 0; i < allsize; i++) {
628                 /* XXX: what with AF_UNSPEC? */
629                 if (allowedfamily[i] == (uint64_t)family) {
630                         return (true);
631                 }
632         }
633
634         return (false);
635 }
636
637 static bool
638 net_allowed_bsaddr_impl(const nvlist_t *salimits, const void *saddr,
639     size_t saddrsize)
640 {
641         void *cookie;
642         const void *limit;
643         size_t limitsize;
644
645         cookie = NULL;
646         while (nvlist_next(salimits, NULL, &cookie) != NULL) {
647                 limit = cnvlist_get_binary(cookie, &limitsize);
648
649                 if (limitsize != saddrsize) {
650                         continue;
651                 }
652                 if (memcmp(limit, saddr, limitsize) == 0) {
653                         return (true);
654                 }
655
656                 /*
657                  * In case of deprecated version (gethostbyname) we have to
658                  * ignore port, because there is no such info in the hostent.
659                  * Suporting only AF_INET and AF_INET6.
660                  */
661                 if (strcmp(cnvlist_name(cookie), "d") != 0 ||
662                     (saddrsize != sizeof(struct sockaddr_in) &&
663                     saddrsize != sizeof(struct sockaddr_in6))) {
664                         continue;
665                 }
666                 if (saddrsize == sizeof(struct sockaddr_in)) {
667                         const struct sockaddr_in *saddrptr;
668                         struct sockaddr_in sockaddr;
669
670                         saddrptr = (const struct sockaddr_in *)saddr;
671                         memcpy(&sockaddr, limit, sizeof(sockaddr));
672                         sockaddr.sin_port = saddrptr->sin_port;
673
674                         if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
675                                 return (true);
676                         }
677                 } else if (saddrsize == sizeof(struct sockaddr_in6)) {
678                         const struct sockaddr_in6 *saddrptr;
679                         struct sockaddr_in6 sockaddr;
680
681                         saddrptr = (const struct sockaddr_in6 *)saddr;
682                         memcpy(&sockaddr, limit, sizeof(sockaddr));
683                         sockaddr.sin6_port = saddrptr->sin6_port;
684
685                         if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
686                                 return (true);
687                         }
688                 }
689         }
690
691         return (false);
692 }
693
694 static bool
695 net_allowed_bsaddr(const nvlist_t *limits, const void *saddr, size_t saddrsize)
696 {
697
698         if (limits == NULL)
699                 return (true);
700
701         if (!nvlist_exists_nvlist(limits, "sockaddr"))
702                 return (true);
703
704         return (net_allowed_bsaddr_impl(nvlist_get_nvlist(limits, "sockaddr"),
705             saddr, saddrsize));
706 }
707
708 static bool
709 net_allowed_hosts(const nvlist_t *limits, const char *name, const char *srvname)
710 {
711         void *cookie;
712         const nvlist_t *hlimits;
713         const char *testname, *testsrvname;
714
715         if (limits == NULL) {
716                 return (true);
717         }
718
719         /* If there are no hosts at all, allow any. */
720         if (!nvlist_exists_nvlist(limits, "hosts")) {
721                 return (true);
722         }
723
724         cookie = NULL;
725         testname = (name == NULL ? "" : name);
726         testsrvname = (srvname == NULL ? "" : srvname);
727         hlimits = nvlist_get_nvlist(limits, "hosts");
728         while (nvlist_next(hlimits, NULL, &cookie) != NULL) {
729                 if (strcmp(cnvlist_name(cookie), "") != 0 &&
730                     strcmp(cnvlist_name(cookie), testname) != 0) {
731                         continue;
732                 }
733
734                 if (strcmp(cnvlist_get_string(cookie), "") != 0 &&
735                     strcmp(cnvlist_get_string(cookie), testsrvname) != 0) {
736                         continue;
737                 }
738
739                 return (true);
740         }
741
742         return (false);
743 }
744
745 static void
746 hostent_pack(const struct hostent *hp, nvlist_t *nvl, bool addtocache)
747 {
748         unsigned int ii;
749         char nvlname[64];
750         int n;
751
752         nvlist_add_string(nvl, "name", hp->h_name);
753         nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
754         nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
755
756         if (hp->h_aliases == NULL) {
757                 nvlist_add_number(nvl, "naliases", 0);
758         } else {
759                 for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
760                         n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
761                         assert(n > 0 && n < (int)sizeof(nvlname));
762                         nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
763                 }
764                 nvlist_add_number(nvl, "naliases", (uint64_t)ii);
765         }
766
767         if (hp->h_addr_list == NULL) {
768                 nvlist_add_number(nvl, "naddrs", 0);
769         } else {
770                 for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
771                         n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
772                         assert(n > 0 && n < (int)sizeof(nvlname));
773                         nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
774                             (size_t)hp->h_length);
775                         if (addtocache) {
776                                 net_add_hostent_to_cache(hp->h_addr_list[ii],
777                                     hp->h_length, hp->h_addrtype);
778                         }
779                 }
780                 nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
781         }
782 }
783
784 static int
785 net_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
786     nvlist_t *nvlout)
787 {
788         struct hostent *hp;
789         int family;
790         const nvlist_t *funclimit;
791         const char *name;
792         bool dnscache;
793
794         if (!net_allowed_mode(limits, CAPNET_DEPRECATED_NAME2ADDR))
795                 return (ENOTCAPABLE);
796
797         dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
798         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR, NULL);
799
800         family = (int)nvlist_get_number(nvlin, "family");
801         if (!net_allowed_family(funclimit, family))
802                 return (ENOTCAPABLE);
803
804         name = nvlist_get_string(nvlin, "name");
805         if (!net_allowed_hosts(funclimit, name, ""))
806                 return (ENOTCAPABLE);
807
808         hp = gethostbyname2(name, family);
809         if (hp == NULL)
810                 return (h_errno);
811         hostent_pack(hp, nvlout, dnscache);
812         return (0);
813 }
814
815 static int
816 net_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
817     nvlist_t *nvlout)
818 {
819         struct hostent *hp;
820         const void *addr;
821         size_t addrsize;
822         int family;
823         const nvlist_t *funclimit;
824
825         if (!net_allowed_mode(limits, CAPNET_DEPRECATED_ADDR2NAME))
826                 return (ENOTCAPABLE);
827
828         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME, NULL);
829
830         family = (int)nvlist_get_number(nvlin, "family");
831         if (!net_allowed_family(funclimit, family))
832                 return (ENOTCAPABLE);
833
834         addr = nvlist_get_binary(nvlin, "addr", &addrsize);
835         if (!net_allowed_bsaddr(funclimit, addr, addrsize))
836                 return (ENOTCAPABLE);
837
838         hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
839         if (hp == NULL)
840                 return (h_errno);
841         hostent_pack(hp, nvlout, false);
842         return (0);
843 }
844
845 static int
846 net_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
847 {
848         struct sockaddr_storage sast;
849         const void *sabin;
850         char *host, *serv;
851         size_t sabinsize, hostlen, servlen;
852         socklen_t salen;
853         int error, flags;
854         const nvlist_t *funclimit;
855
856         if (!net_allowed_mode(limits, CAPNET_ADDR2NAME))
857                 return (ENOTCAPABLE);
858         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME, NULL);
859
860         error = 0;
861         host = serv = NULL;
862         memset(&sast, 0, sizeof(sast));
863
864         hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
865         servlen = (size_t)nvlist_get_number(nvlin, "servlen");
866
867         if (hostlen > 0) {
868                 host = calloc(1, hostlen + 1);
869                 if (host == NULL) {
870                         error = EAI_MEMORY;
871                         goto out;
872                 }
873         }
874         if (servlen > 0) {
875                 serv = calloc(1, servlen + 1);
876                 if (serv == NULL) {
877                         error = EAI_MEMORY;
878                         goto out;
879                 }
880         }
881
882         sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
883         if (sabinsize > sizeof(sast)) {
884                 error = EAI_FAIL;
885                 goto out;
886         }
887         if (!net_allowed_bsaddr(funclimit, sabin, sabinsize))
888                 return (ENOTCAPABLE);
889
890         memcpy(&sast, sabin, sabinsize);
891         salen = (socklen_t)sabinsize;
892
893         if ((sast.ss_family != AF_INET ||
894              salen != sizeof(struct sockaddr_in)) &&
895             (sast.ss_family != AF_INET6 ||
896              salen != sizeof(struct sockaddr_in6))) {
897                 error = EAI_FAIL;
898                 goto out;
899         }
900
901         if (!net_allowed_family(funclimit, (int)sast.ss_family)) {
902                 error = ENOTCAPABLE;
903                 goto out;
904         }
905
906         flags = (int)nvlist_get_number(nvlin, "flags");
907
908         error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
909             serv, servlen, flags);
910         if (error != 0)
911                 goto out;
912
913         if (host != NULL)
914                 nvlist_move_string(nvlout, "host", host);
915         if (serv != NULL)
916                 nvlist_move_string(nvlout, "serv", serv);
917 out:
918         if (error != 0) {
919                 free(host);
920                 free(serv);
921         }
922         return (error);
923 }
924
925 static nvlist_t *
926 addrinfo_pack(const struct addrinfo *ai)
927 {
928         nvlist_t *nvl;
929
930         nvl = nvlist_create(0);
931         nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
932         nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
933         nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
934         nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
935         nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
936         if (ai->ai_canonname != NULL)
937                 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
938
939         return (nvl);
940 }
941
942 static int
943 net_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
944 {
945         struct addrinfo hints, *hintsp, *res, *cur;
946         const char *hostname, *servname;
947         char nvlname[64];
948         nvlist_t *elem;
949         unsigned int ii;
950         int error, family, n;
951         const nvlist_t *funclimit;
952         bool dnscache;
953
954         if (!net_allowed_mode(limits, CAPNET_NAME2ADDR))
955                 return (ENOTCAPABLE);
956         dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
957         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR, NULL);
958
959         hostname = dnvlist_get_string(nvlin, "hostname", NULL);
960         servname = dnvlist_get_string(nvlin, "servname", NULL);
961         if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
962                 hints.ai_flags = (int)nvlist_get_number(nvlin,
963                     "hints.ai_flags");
964                 hints.ai_family = (int)nvlist_get_number(nvlin,
965                     "hints.ai_family");
966                 hints.ai_socktype = (int)nvlist_get_number(nvlin,
967                     "hints.ai_socktype");
968                 hints.ai_protocol = (int)nvlist_get_number(nvlin,
969                     "hints.ai_protocol");
970                 hints.ai_addrlen = 0;
971                 hints.ai_addr = NULL;
972                 hints.ai_canonname = NULL;
973                 hints.ai_next = NULL;
974                 hintsp = &hints;
975                 family = hints.ai_family;
976         } else {
977                 hintsp = NULL;
978                 family = AF_UNSPEC;
979         }
980
981         if (!net_allowed_family(funclimit, family))
982                 return (ENOTCAPABLE);
983         if (!net_allowed_hosts(funclimit, hostname, servname))
984                 return (ENOTCAPABLE);
985         error = getaddrinfo(hostname, servname, hintsp, &res);
986         if (error != 0) {
987                 goto out;
988         }
989
990         for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
991                 elem = addrinfo_pack(cur);
992                 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
993                 assert(n > 0 && n < (int)sizeof(nvlname));
994                 nvlist_move_nvlist(nvlout, nvlname, elem);
995                 if (dnscache) {
996                         net_add_sockaddr_to_cache(cur->ai_addr,
997                             cur->ai_addrlen, false);
998                 }
999         }
1000
1001         freeaddrinfo(res);
1002         error = 0;
1003 out:
1004         return (error);
1005 }
1006
1007 static int
1008 net_bind(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1009 {
1010         int socket, serrno;
1011         const void *saddr;
1012         size_t len;
1013         const nvlist_t *funclimit;
1014
1015         if (!net_allowed_mode(limits, CAPNET_BIND))
1016                 return (ENOTCAPABLE);
1017         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_BIND, NULL);
1018
1019         saddr = nvlist_get_binary(nvlin, "saddr", &len);
1020
1021         if (!net_allowed_bsaddr(funclimit, saddr, len))
1022                 return (ENOTCAPABLE);
1023
1024         socket = nvlist_take_descriptor(nvlin, "s");
1025         if (bind(socket, saddr, len) < 0) {
1026                 serrno = errno;
1027                 close(socket);
1028                 return (serrno);
1029         }
1030
1031         nvlist_move_descriptor(nvlout, "s", socket);
1032
1033         return (0);
1034 }
1035
1036 static int
1037 net_connect(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1038 {
1039         int socket, serrno;
1040         const void *saddr;
1041         const nvlist_t *funclimit;
1042         size_t len;
1043         bool conn, conndns;
1044
1045         conn = net_allowed_mode(limits, CAPNET_CONNECT);
1046         conndns = net_allowed_mode(limits, CAPNET_CONNECTDNS);
1047
1048         if (!conn && !conndns)
1049                 return (ENOTCAPABLE);
1050
1051         funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_CONNECT, NULL);
1052
1053         saddr = nvlist_get_binary(nvlin, "saddr", &len);
1054         if (conn && !net_allowed_bsaddr(funclimit, saddr, len)) {
1055                 return (ENOTCAPABLE);
1056         } else if (conndns && (capdnscache == NULL ||
1057            !net_allowed_bsaddr_impl(capdnscache, saddr, len))) {
1058                 return (ENOTCAPABLE);
1059         }
1060         socket = dup(nvlist_get_descriptor(nvlin, "s"));
1061         if (connect(socket, saddr, len) < 0) {
1062                 serrno = errno;
1063                 close(socket);
1064                 return (serrno);
1065         }
1066
1067         nvlist_move_descriptor(nvlout, "s", socket);
1068
1069         return (0);
1070 }
1071
1072 static bool
1073 verify_only_sa_newlimts(const nvlist_t *oldfunclimits,
1074     const nvlist_t *newfunclimit)
1075 {
1076         void *cookie;
1077
1078         cookie = NULL;
1079         while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1080                 void *sacookie;
1081
1082                 if (strcmp(cnvlist_name(cookie), "sockaddr") != 0)
1083                         return (false);
1084
1085                 if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1086                         return (false);
1087
1088                 sacookie = NULL;
1089                 while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1090                     &sacookie) != NULL) {
1091                         const void *sa;
1092                         size_t sasize;
1093
1094                         if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1095                                 return (false);
1096
1097                         sa = cnvlist_get_binary(sacookie, &sasize);
1098                         if (!net_allowed_bsaddr(oldfunclimits, sa, sasize))
1099                                 return (false);
1100                 }
1101         }
1102
1103         return (true);
1104 }
1105
1106 static bool
1107 verify_bind_newlimts(const nvlist_t *oldlimits,
1108     const nvlist_t *newfunclimit)
1109 {
1110         const nvlist_t *oldfunclimits;
1111
1112         oldfunclimits = NULL;
1113         if (oldlimits != NULL) {
1114                 oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_BIND,
1115                     NULL);
1116         }
1117
1118         return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1119 }
1120
1121
1122 static bool
1123 verify_connect_newlimits(const nvlist_t *oldlimits,
1124     const nvlist_t *newfunclimit)
1125 {
1126         const nvlist_t *oldfunclimits;
1127
1128         oldfunclimits = NULL;
1129         if (oldlimits != NULL) {
1130                 oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_CONNECT,
1131                     NULL);
1132         }
1133
1134         return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1135 }
1136
1137 static bool
1138 verify_addr2name_newlimits(const nvlist_t *oldlimits,
1139     const nvlist_t *newfunclimit)
1140 {
1141         void *cookie;
1142         const nvlist_t *oldfunclimits;
1143
1144         oldfunclimits = NULL;
1145         if (oldlimits != NULL) {
1146                 oldfunclimits = dnvlist_get_nvlist(oldlimits,
1147                     LIMIT_NV_ADDR2NAME, NULL);
1148         }
1149
1150         cookie = NULL;
1151         while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1152                 if (strcmp(cnvlist_name(cookie), "sockaddr") == 0) {
1153                         void *sacookie;
1154
1155                         if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1156                                 return (false);
1157
1158                         sacookie = NULL;
1159                         while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1160                             &sacookie) != NULL) {
1161                                 const void *sa;
1162                                 size_t sasize;
1163
1164                                 if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1165                                         return (false);
1166
1167                                 sa = cnvlist_get_binary(sacookie, &sasize);
1168                                 if (!net_allowed_bsaddr(oldfunclimits, sa,
1169                                     sasize)) {
1170                                         return (false);
1171                                 }
1172                         }
1173                 } else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1174                         size_t i, sfamilies;
1175                         const uint64_t *families;
1176
1177                         if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1178                                 return (false);
1179
1180                         families = cnvlist_get_number_array(cookie, &sfamilies);
1181                         for (i = 0; i < sfamilies; i++) {
1182                                 if (!net_allowed_family(oldfunclimits,
1183                                     families[i])) {
1184                                         return (false);
1185                                 }
1186                         }
1187                 } else {
1188                         return (false);
1189                 }
1190         }
1191
1192         return (true);
1193 }
1194
1195 static bool
1196 verify_name2addr_newlimits(const nvlist_t *oldlimits,
1197     const nvlist_t *newfunclimit)
1198 {
1199         void *cookie;
1200         const nvlist_t *oldfunclimits;
1201
1202         oldfunclimits = NULL;
1203         if (oldlimits != NULL) {
1204                 oldfunclimits = dnvlist_get_nvlist(oldlimits,
1205                     LIMIT_NV_ADDR2NAME, NULL);
1206         }
1207
1208         cookie = NULL;
1209         while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1210                 if (strcmp(cnvlist_name(cookie), "hosts") == 0) {
1211                         void *hostcookie;
1212
1213                         if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1214                                 return (false);
1215
1216                         hostcookie = NULL;
1217                         while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1218                             &hostcookie) != NULL) {
1219                                 if (cnvlist_type(hostcookie) != NV_TYPE_STRING)
1220                                         return (false);
1221
1222                                 if (!net_allowed_hosts(oldfunclimits,
1223                                     cnvlist_name(hostcookie),
1224                                     cnvlist_get_string(hostcookie))) {
1225                                         return (false);
1226                                 }
1227                         }
1228                 } else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1229                         size_t i, sfamilies;
1230                         const uint64_t *families;
1231
1232                         if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1233                                 return (false);
1234
1235                         families = cnvlist_get_number_array(cookie, &sfamilies);
1236                         for (i = 0; i < sfamilies; i++) {
1237                                 if (!net_allowed_family(oldfunclimits,
1238                                     families[i])) {
1239                                         return (false);
1240                                 }
1241                         }
1242                 } else {
1243                         return (false);
1244                 }
1245         }
1246
1247         return (true);
1248 }
1249
1250 static int
1251 net_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
1252 {
1253         const char *name;
1254         void *cookie;
1255         bool hasmode, hasconnect, hasbind, hasaddr2name, hasname2addr;
1256
1257         /*
1258          * Modes:
1259          *      ADDR2NAME:
1260          *              getnameinfo
1261          *      DEPRECATED_ADDR2NAME:
1262          *              gethostbyaddr
1263          *
1264          *      NAME2ADDR:
1265          *              getaddrinfo
1266          *      DEPRECATED_NAME2ADDR:
1267          *              gethostbyname
1268          *
1269          * Limit scheme:
1270          *      mode    : NV_TYPE_NUMBER
1271          *      connect : NV_TYPE_NVLIST
1272          *              sockaddr : NV_TYPE_NVLIST
1273          *                      ""      : NV_TYPE_BINARY
1274          *                      ...     : NV_TYPE_BINARY
1275          *      bind    : NV_TYPE_NVLIST
1276          *              sockaddr : NV_TYPE_NVLIST
1277          *                      ""      : NV_TYPE_BINARY
1278          *                      ...     : NV_TYPE_BINARY
1279          *      addr2name : NV_TYPE_NVLIST
1280          *              family  : NV_TYPE_NUMBER_ARRAY
1281          *              sockaddr : NV_TYPE_NVLIST
1282          *                      ""      : NV_TYPE_BINARY
1283          *                      ...     : NV_TYPE_BINARY
1284          *      name2addr : NV_TYPE_NVLIST
1285          *              family : NV_TYPE_NUMBER
1286          *              hosts   : NV_TYPE_NVLIST
1287          *                      host    : servname : NV_TYPE_STRING
1288          */
1289
1290         hasmode = false;
1291         hasconnect = false;
1292         hasbind = false;
1293         hasaddr2name = false;
1294         hasname2addr = false;
1295
1296         cookie = NULL;
1297         while ((name = nvlist_next(newlimits, NULL, &cookie)) != NULL) {
1298                 if (strcmp(name, "mode") == 0) {
1299                         if (cnvlist_type(cookie) != NV_TYPE_NUMBER) {
1300                                 return (NO_RECOVERY);
1301                         }
1302                         if (!net_allowed_mode(oldlimits,
1303                             cnvlist_get_number(cookie))) {
1304                                 return (ENOTCAPABLE);
1305                         }
1306                         hasmode = true;
1307                         continue;
1308                 }
1309
1310                 if (cnvlist_type(cookie) != NV_TYPE_NVLIST) {
1311                         return (NO_RECOVERY);
1312                 }
1313
1314                 if (strcmp(name, LIMIT_NV_BIND) == 0) {
1315                         hasbind = true;
1316                         if (!verify_bind_newlimts(oldlimits,
1317                             cnvlist_get_nvlist(cookie))) {
1318                                 return (ENOTCAPABLE);
1319                         }
1320                 } else if (strcmp(name, LIMIT_NV_CONNECT) == 0) {
1321                         hasconnect = true;
1322                         if (!verify_connect_newlimits(oldlimits,
1323                             cnvlist_get_nvlist(cookie))) {
1324                                 return (ENOTCAPABLE);
1325                         }
1326                 } else if (strcmp(name, LIMIT_NV_ADDR2NAME) == 0) {
1327                         hasaddr2name = true;
1328                         if (!verify_addr2name_newlimits(oldlimits,
1329                             cnvlist_get_nvlist(cookie))) {
1330                                 return (ENOTCAPABLE);
1331                         }
1332                 } else if (strcmp(name, LIMIT_NV_NAME2ADDR) == 0) {
1333                         hasname2addr = true;
1334                         if (!verify_name2addr_newlimits(oldlimits,
1335                             cnvlist_get_nvlist(cookie))) {
1336                                 return (ENOTCAPABLE);
1337                         }
1338                 }
1339         }
1340
1341         /* Mode is required. */
1342         if (!hasmode)
1343                 return (ENOTCAPABLE);
1344
1345         /*
1346          * If the new limit doesn't mention mode or family we have to
1347          * check if the current limit does have those. Missing mode or
1348          * family in the limit means that all modes or families are
1349          * allowed.
1350          */
1351         if (oldlimits == NULL)
1352                 return (0);
1353         if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_BIND))
1354                 return (ENOTCAPABLE);
1355         if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_CONNECT))
1356                 return (ENOTCAPABLE);
1357         if (!hasaddr2name && nvlist_exists(oldlimits, LIMIT_NV_ADDR2NAME))
1358                 return (ENOTCAPABLE);
1359         if (!hasname2addr && nvlist_exists(oldlimits, LIMIT_NV_NAME2ADDR))
1360                 return (ENOTCAPABLE);
1361         return (0);
1362 }
1363
1364 static int
1365 net_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
1366     nvlist_t *nvlout)
1367 {
1368
1369         if (strcmp(cmd, "bind") == 0)
1370                 return (net_bind(limits, nvlin, nvlout));
1371         else if (strcmp(cmd, "connect") == 0)
1372                 return (net_connect(limits, nvlin, nvlout));
1373         else if (strcmp(cmd, "gethostbyname") == 0)
1374                 return (net_gethostbyname(limits, nvlin, nvlout));
1375         else if (strcmp(cmd, "gethostbyaddr") == 0)
1376                 return (net_gethostbyaddr(limits, nvlin, nvlout));
1377         else if (strcmp(cmd, "getnameinfo") == 0)
1378                 return (net_getnameinfo(limits, nvlin, nvlout));
1379         else if (strcmp(cmd, "getaddrinfo") == 0)
1380                 return (net_getaddrinfo(limits, nvlin, nvlout));
1381
1382         return (EINVAL);
1383 }
1384
1385 CREATE_SERVICE("system.net", net_limit, net_command, 0);