1259994Sgjb/*-
2259994Sgjb * SPDX-License-Identifier: BSD-2-Clause
3259994Sgjb *
4259994Sgjb * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
5259994Sgjb *
6259994Sgjb * Redistribution and use in source and binary forms, with or without
7259994Sgjb * modification, are permitted provided that the following conditions
8259994Sgjb * are met:
9259994Sgjb * 1. Redistributions of source code must retain the above copyright
10259994Sgjb *    notice, this list of conditions and the following disclaimer.
11259994Sgjb * 2. Redistributions in binary form must reproduce the above copyright
12259994Sgjb *    notice, this list of conditions and the following disclaimer in the
13259994Sgjb *    documentation and/or other materials provided with the distribution.
14259994Sgjb *
15259994Sgjb * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16259994Sgjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17259994Sgjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18259994Sgjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19259994Sgjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20259994Sgjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21259994Sgjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22259994Sgjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23259994Sgjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24259994Sgjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25259994Sgjb * SUCH DAMAGE.
26259994Sgjb */
27259994Sgjb
28259994Sgjb#include <sys/cdefs.h>
29259994Sgjb#include <sys/cnv.h>
30259994Sgjb#include <sys/dnv.h>
31259994Sgjb#include <sys/nv.h>
32259994Sgjb#include <sys/socket.h>
33259994Sgjb#include <netinet/in.h>
34259994Sgjb
35259994Sgjb#include <assert.h>
36259994Sgjb#include <errno.h>
37259994Sgjb#include <netdb.h>
38#include <stdio.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <libcasper.h>
43#include <libcasper_service.h>
44
45#include "cap_net.h"
46
47#define	CAPNET_MASK	(CAPNET_ADDR2NAME | CAPNET_NAME2ADDR	\
48    CAPNET_DEPRECATED_ADDR2NAME | CAPNET_DEPRECATED_NAME2ADDR | \
49    CAPNET_CONNECT | CAPNET_BIND | CAPNET_CONNECTDNS)
50
51/*
52 * Defines for the names of the limits.
53 * XXX: we should convert all string constats to this to avoid typos.
54 */
55#define	LIMIT_NV_BIND			"bind"
56#define	LIMIT_NV_CONNECT		"connect"
57#define	LIMIT_NV_ADDR2NAME		"addr2name"
58#define	LIMIT_NV_NAME2ADDR		"name2addr"
59
60struct cap_net_limit {
61	cap_channel_t	*cnl_chan;
62	uint64_t	 cnl_mode;
63	nvlist_t	*cnl_addr2name;
64	nvlist_t	*cnl_name2addr;
65	nvlist_t	*cnl_connect;
66	nvlist_t	*cnl_bind;
67};
68
69static struct hostent hent;
70
71static void
72hostent_free(struct hostent *hp)
73{
74	unsigned int ii;
75
76	free(hp->h_name);
77	hp->h_name = NULL;
78	if (hp->h_aliases != NULL) {
79		for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
80			free(hp->h_aliases[ii]);
81		free(hp->h_aliases);
82		hp->h_aliases = NULL;
83	}
84	if (hp->h_addr_list != NULL) {
85		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
86			free(hp->h_addr_list[ii]);
87		free(hp->h_addr_list);
88		hp->h_addr_list = NULL;
89	}
90}
91
92static struct hostent *
93hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
94{
95	unsigned int ii, nitems;
96	char nvlname[64];
97	int n;
98
99	hostent_free(hp);
100
101	hp->h_name = strdup(nvlist_get_string(nvl, "name"));
102	if (hp->h_name == NULL)
103		goto fail;
104	hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
105	hp->h_length = (int)nvlist_get_number(nvl, "length");
106
107	nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
108	hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
109	if (hp->h_aliases == NULL)
110		goto fail;
111	for (ii = 0; ii < nitems; ii++) {
112		n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
113		assert(n > 0 && n < (int)sizeof(nvlname));
114		hp->h_aliases[ii] =
115		    strdup(nvlist_get_string(nvl, nvlname));
116		if (hp->h_aliases[ii] == NULL)
117			goto fail;
118	}
119	hp->h_aliases[ii] = NULL;
120
121	nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
122	hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
123	if (hp->h_addr_list == NULL)
124		goto fail;
125	for (ii = 0; ii < nitems; ii++) {
126		hp->h_addr_list[ii] = malloc(hp->h_length);
127		if (hp->h_addr_list[ii] == NULL)
128			goto fail;
129		n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
130		assert(n > 0 && n < (int)sizeof(nvlname));
131		bcopy(nvlist_get_binary(nvl, nvlname, NULL),
132		    hp->h_addr_list[ii], hp->h_length);
133	}
134	hp->h_addr_list[ii] = NULL;
135
136	return (hp);
137fail:
138	hostent_free(hp);
139	h_errno = NO_RECOVERY;
140	return (NULL);
141}
142
143static int
144request_cb(cap_channel_t *chan, const char *name, int s,
145    const struct sockaddr *saddr, socklen_t len)
146{
147	nvlist_t *nvl;
148	int serrno;
149
150	nvl = nvlist_create(0);
151	nvlist_add_string(nvl, "cmd", name);
152	nvlist_add_descriptor(nvl, "s", s);
153	nvlist_add_binary(nvl, "saddr", saddr, len);
154
155	nvl = cap_xfer_nvlist(chan, nvl);
156	if (nvl == NULL)
157		return (-1);
158
159	if (nvlist_get_number(nvl, "error") != 0) {
160		serrno = (int)nvlist_get_number(nvl, "error");
161		nvlist_destroy(nvl);
162		errno = serrno;
163		return (-1);
164	}
165
166	s = dup2(s, nvlist_get_descriptor(nvl, "s"));
167	nvlist_destroy(nvl);
168
169	return (s == -1 ? -1 : 0);
170}
171
172int
173cap_bind(cap_channel_t *chan, int s, const struct sockaddr *addr,
174    socklen_t addrlen)
175{
176
177	return (request_cb(chan, LIMIT_NV_BIND, s, addr, addrlen));
178}
179
180int
181cap_connect(cap_channel_t *chan, int s, const struct sockaddr *name,
182    socklen_t namelen)
183{
184
185	return (request_cb(chan, LIMIT_NV_CONNECT, s, name, namelen));
186}
187
188
189struct hostent *
190cap_gethostbyname(cap_channel_t *chan, const char *name)
191{
192
193	return (cap_gethostbyname2(chan, name, AF_INET));
194}
195
196struct hostent *
197cap_gethostbyname2(cap_channel_t *chan, const char *name, int af)
198{
199	struct hostent *hp;
200	nvlist_t *nvl;
201
202	nvl = nvlist_create(0);
203	nvlist_add_string(nvl, "cmd", "gethostbyname");
204	nvlist_add_number(nvl, "family", (uint64_t)af);
205	nvlist_add_string(nvl, "name", name);
206	nvl = cap_xfer_nvlist(chan, nvl);
207	if (nvl == NULL) {
208		h_errno = NO_RECOVERY;
209		return (NULL);
210	}
211	if (nvlist_get_number(nvl, "error") != 0) {
212		h_errno = (int)nvlist_get_number(nvl, "error");
213		nvlist_destroy(nvl);
214		return (NULL);
215	}
216
217	hp = hostent_unpack(nvl, &hent);
218	nvlist_destroy(nvl);
219	return (hp);
220}
221
222struct hostent *
223cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
224    int af)
225{
226	struct hostent *hp;
227	nvlist_t *nvl;
228
229	nvl = nvlist_create(0);
230	nvlist_add_string(nvl, "cmd", "gethostbyaddr");
231	nvlist_add_binary(nvl, "addr", addr, (size_t)len);
232	nvlist_add_number(nvl, "family", (uint64_t)af);
233	nvl = cap_xfer_nvlist(chan, nvl);
234	if (nvl == NULL) {
235		h_errno = NO_RECOVERY;
236		return (NULL);
237	}
238	if (nvlist_get_number(nvl, "error") != 0) {
239		h_errno = (int)nvlist_get_number(nvl, "error");
240		nvlist_destroy(nvl);
241		return (NULL);
242	}
243	hp = hostent_unpack(nvl, &hent);
244	nvlist_destroy(nvl);
245	return (hp);
246}
247
248static struct addrinfo *
249addrinfo_unpack(const nvlist_t *nvl)
250{
251	struct addrinfo *ai;
252	const void *addr;
253	size_t addrlen;
254	const char *canonname;
255
256	addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
257	ai = malloc(sizeof(*ai) + addrlen);
258	if (ai == NULL)
259		return (NULL);
260	ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
261	ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
262	ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
263	ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
264	ai->ai_addrlen = (socklen_t)addrlen;
265	canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
266	if (canonname != NULL) {
267		ai->ai_canonname = strdup(canonname);
268		if (ai->ai_canonname == NULL) {
269			free(ai);
270			return (NULL);
271		}
272	} else {
273		ai->ai_canonname = NULL;
274	}
275	ai->ai_addr = (void *)(ai + 1);
276	bcopy(addr, ai->ai_addr, addrlen);
277	ai->ai_next = NULL;
278
279	return (ai);
280}
281
282int
283cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
284    const struct addrinfo *hints, struct addrinfo **res)
285{
286	struct addrinfo *firstai, *prevai, *curai;
287	unsigned int ii;
288	const nvlist_t *nvlai;
289	char nvlname[64];
290	nvlist_t *nvl;
291	int error, n;
292
293	nvl = nvlist_create(0);
294	nvlist_add_string(nvl, "cmd", "getaddrinfo");
295	if (hostname != NULL)
296		nvlist_add_string(nvl, "hostname", hostname);
297	if (servname != NULL)
298		nvlist_add_string(nvl, "servname", servname);
299	if (hints != NULL) {
300		nvlist_add_number(nvl, "hints.ai_flags",
301		    (uint64_t)hints->ai_flags);
302		nvlist_add_number(nvl, "hints.ai_family",
303		    (uint64_t)hints->ai_family);
304		nvlist_add_number(nvl, "hints.ai_socktype",
305		    (uint64_t)hints->ai_socktype);
306		nvlist_add_number(nvl, "hints.ai_protocol",
307		    (uint64_t)hints->ai_protocol);
308	}
309	nvl = cap_xfer_nvlist(chan, nvl);
310	if (nvl == NULL)
311		return (EAI_MEMORY);
312	if (nvlist_get_number(nvl, "error") != 0) {
313		error = (int)nvlist_get_number(nvl, "error");
314		nvlist_destroy(nvl);
315		return (error);
316	}
317
318	nvlai = NULL;
319	firstai = prevai = curai = NULL;
320	for (ii = 0; ; ii++) {
321		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
322		assert(n > 0 && n < (int)sizeof(nvlname));
323		if (!nvlist_exists_nvlist(nvl, nvlname))
324			break;
325		nvlai = nvlist_get_nvlist(nvl, nvlname);
326		curai = addrinfo_unpack(nvlai);
327		if (curai == NULL) {
328			nvlist_destroy(nvl);
329			return (EAI_MEMORY);
330		}
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
348int
349cap_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);
372	if (serv != NULL && nvlist_exists_string(nvl, "serv"))
373		strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen);
374	nvlist_destroy(nvl);
375	return (0);
376}
377
378cap_net_limit_t *
379cap_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
396static void
397pack_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
407int
408cap_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
427void
428cap_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
442static void
443pack_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
461static void
462pack_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
476cap_net_limit_t *
477cap_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
484cap_net_limit_t *
485cap_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
492cap_net_limit_t *
493cap_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
512cap_net_limit_t *
513cap_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
522cap_net_limit_t *
523cap_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
531cap_net_limit_t *
532cap_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
544static nvlist_t *capdnscache;
545
546static void
547net_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
573static void
574net_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
603static bool
604net_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
613static bool
614net_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
637static bool
638net_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
694static bool
695net_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
708static bool
709net_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
745static void
746hostent_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
784static int
785net_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 = NULL;
799	if (limits != NULL) {
800		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR,
801		    NULL);
802	}
803
804	family = (int)nvlist_get_number(nvlin, "family");
805	if (!net_allowed_family(funclimit, family))
806		return (ENOTCAPABLE);
807
808	name = nvlist_get_string(nvlin, "name");
809	if (!net_allowed_hosts(funclimit, name, ""))
810		return (ENOTCAPABLE);
811
812	hp = gethostbyname2(name, family);
813	if (hp == NULL)
814		return (h_errno);
815	hostent_pack(hp, nvlout, dnscache);
816	return (0);
817}
818
819static int
820net_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
821    nvlist_t *nvlout)
822{
823	struct hostent *hp;
824	const void *addr;
825	size_t addrsize;
826	int family;
827	const nvlist_t *funclimit;
828
829	if (!net_allowed_mode(limits, CAPNET_DEPRECATED_ADDR2NAME))
830		return (ENOTCAPABLE);
831
832	funclimit = NULL;
833	if (limits != NULL) {
834		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME,
835		    NULL);
836	}
837
838	family = (int)nvlist_get_number(nvlin, "family");
839	if (!net_allowed_family(funclimit, family))
840		return (ENOTCAPABLE);
841
842	addr = nvlist_get_binary(nvlin, "addr", &addrsize);
843	if (!net_allowed_bsaddr(funclimit, addr, addrsize))
844		return (ENOTCAPABLE);
845
846	hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
847	if (hp == NULL)
848		return (h_errno);
849	hostent_pack(hp, nvlout, false);
850	return (0);
851}
852
853static int
854net_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
855{
856	struct sockaddr_storage sast;
857	const void *sabin;
858	char *host, *serv;
859	size_t sabinsize, hostlen, servlen;
860	socklen_t salen;
861	int error, flags;
862	const nvlist_t *funclimit;
863
864	if (!net_allowed_mode(limits, CAPNET_ADDR2NAME))
865		return (ENOTCAPABLE);
866	funclimit = NULL;
867	if (limits != NULL) {
868		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME,
869		    NULL);
870	}
871
872	error = 0;
873	host = serv = NULL;
874	memset(&sast, 0, sizeof(sast));
875
876	hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
877	servlen = (size_t)nvlist_get_number(nvlin, "servlen");
878
879	if (hostlen > 0) {
880		host = calloc(1, hostlen);
881		if (host == NULL) {
882			error = EAI_MEMORY;
883			goto out;
884		}
885	}
886	if (servlen > 0) {
887		serv = calloc(1, servlen);
888		if (serv == NULL) {
889			error = EAI_MEMORY;
890			goto out;
891		}
892	}
893
894	sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
895	if (sabinsize > sizeof(sast)) {
896		error = EAI_FAIL;
897		goto out;
898	}
899	if (!net_allowed_bsaddr(funclimit, sabin, sabinsize)) {
900		error = ENOTCAPABLE;
901		goto out;
902	}
903
904	memcpy(&sast, sabin, sabinsize);
905	salen = (socklen_t)sabinsize;
906
907	if ((sast.ss_family != AF_INET ||
908	     salen != sizeof(struct sockaddr_in)) &&
909	    (sast.ss_family != AF_INET6 ||
910	     salen != sizeof(struct sockaddr_in6))) {
911		error = EAI_FAIL;
912		goto out;
913	}
914
915	if (!net_allowed_family(funclimit, (int)sast.ss_family)) {
916		error = ENOTCAPABLE;
917		goto out;
918	}
919
920	flags = (int)nvlist_get_number(nvlin, "flags");
921
922	error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
923	    serv, servlen, flags);
924	if (error != 0)
925		goto out;
926
927	if (host != NULL)
928		nvlist_move_string(nvlout, "host", host);
929	if (serv != NULL)
930		nvlist_move_string(nvlout, "serv", serv);
931out:
932	if (error != 0) {
933		free(host);
934		free(serv);
935	}
936	return (error);
937}
938
939static nvlist_t *
940addrinfo_pack(const struct addrinfo *ai)
941{
942	nvlist_t *nvl;
943
944	nvl = nvlist_create(0);
945	nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
946	nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
947	nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
948	nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
949	nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
950	if (ai->ai_canonname != NULL)
951		nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
952
953	return (nvl);
954}
955
956static int
957net_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
958{
959	struct addrinfo hints, *hintsp, *res, *cur;
960	const char *hostname, *servname;
961	char nvlname[64];
962	nvlist_t *elem;
963	unsigned int ii;
964	int error, family, n;
965	const nvlist_t *funclimit;
966	bool dnscache;
967
968	if (!net_allowed_mode(limits, CAPNET_NAME2ADDR))
969		return (ENOTCAPABLE);
970	dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
971	funclimit = NULL;
972	if (limits != NULL) {
973		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR,
974		    NULL);
975	}
976
977	hostname = dnvlist_get_string(nvlin, "hostname", NULL);
978	servname = dnvlist_get_string(nvlin, "servname", NULL);
979	if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
980		hints.ai_flags = (int)nvlist_get_number(nvlin,
981		    "hints.ai_flags");
982		hints.ai_family = (int)nvlist_get_number(nvlin,
983		    "hints.ai_family");
984		hints.ai_socktype = (int)nvlist_get_number(nvlin,
985		    "hints.ai_socktype");
986		hints.ai_protocol = (int)nvlist_get_number(nvlin,
987		    "hints.ai_protocol");
988		hints.ai_addrlen = 0;
989		hints.ai_addr = NULL;
990		hints.ai_canonname = NULL;
991		hints.ai_next = NULL;
992		hintsp = &hints;
993		family = hints.ai_family;
994	} else {
995		hintsp = NULL;
996		family = AF_UNSPEC;
997	}
998
999	if (!net_allowed_family(funclimit, family))
1000		return (ENOTCAPABLE);
1001	if (!net_allowed_hosts(funclimit, hostname, servname))
1002		return (ENOTCAPABLE);
1003	error = getaddrinfo(hostname, servname, hintsp, &res);
1004	if (error != 0) {
1005		goto out;
1006	}
1007
1008	for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
1009		elem = addrinfo_pack(cur);
1010		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
1011		assert(n > 0 && n < (int)sizeof(nvlname));
1012		nvlist_move_nvlist(nvlout, nvlname, elem);
1013		if (dnscache) {
1014			net_add_sockaddr_to_cache(cur->ai_addr,
1015			    cur->ai_addrlen, false);
1016		}
1017	}
1018
1019	freeaddrinfo(res);
1020	error = 0;
1021out:
1022	return (error);
1023}
1024
1025static int
1026net_bind(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1027{
1028	int socket, serrno;
1029	const void *saddr;
1030	size_t len;
1031	const nvlist_t *funclimit;
1032
1033	if (!net_allowed_mode(limits, CAPNET_BIND))
1034		return (ENOTCAPABLE);
1035	funclimit = NULL;
1036	if (limits != NULL)
1037		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_BIND, NULL);
1038
1039	saddr = nvlist_get_binary(nvlin, "saddr", &len);
1040
1041	if (!net_allowed_bsaddr(funclimit, saddr, len))
1042		return (ENOTCAPABLE);
1043
1044	socket = nvlist_take_descriptor(nvlin, "s");
1045	if (bind(socket, saddr, len) < 0) {
1046		serrno = errno;
1047		close(socket);
1048		return (serrno);
1049	}
1050
1051	nvlist_move_descriptor(nvlout, "s", socket);
1052
1053	return (0);
1054}
1055
1056static int
1057net_connect(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1058{
1059	int socket, serrno;
1060	const void *saddr;
1061	const nvlist_t *funclimit;
1062	size_t len;
1063	bool conn, conndns, allowed;
1064
1065	conn = net_allowed_mode(limits, CAPNET_CONNECT);
1066	conndns = net_allowed_mode(limits, CAPNET_CONNECTDNS);
1067
1068	if (!conn && !conndns)
1069		return (ENOTCAPABLE);
1070
1071	funclimit = NULL;
1072	if (limits != NULL)
1073		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_CONNECT, NULL);
1074
1075	saddr = nvlist_get_binary(nvlin, "saddr", &len);
1076	allowed = false;
1077
1078	if (conn && net_allowed_bsaddr(funclimit, saddr, len)) {
1079		allowed = true;
1080	}
1081	if (conndns && capdnscache != NULL &&
1082	   net_allowed_bsaddr_impl(capdnscache, saddr, len)) {
1083		allowed = true;
1084	}
1085
1086	if (allowed == false) {
1087		return (ENOTCAPABLE);
1088	}
1089
1090	socket = dup(nvlist_get_descriptor(nvlin, "s"));
1091	if (connect(socket, saddr, len) < 0) {
1092		serrno = errno;
1093		close(socket);
1094		return (serrno);
1095	}
1096
1097	nvlist_move_descriptor(nvlout, "s", socket);
1098
1099	return (0);
1100}
1101
1102static bool
1103verify_only_sa_newlimts(const nvlist_t *oldfunclimits,
1104    const nvlist_t *newfunclimit)
1105{
1106	void *cookie;
1107
1108	cookie = NULL;
1109	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1110		void *sacookie;
1111
1112		if (strcmp(cnvlist_name(cookie), "sockaddr") != 0)
1113			return (false);
1114
1115		if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1116			return (false);
1117
1118		sacookie = NULL;
1119		while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1120		    &sacookie) != NULL) {
1121			const void *sa;
1122			size_t sasize;
1123
1124			if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1125				return (false);
1126
1127			sa = cnvlist_get_binary(sacookie, &sasize);
1128			if (!net_allowed_bsaddr(oldfunclimits, sa, sasize))
1129				return (false);
1130		}
1131	}
1132
1133	return (true);
1134}
1135
1136static bool
1137verify_bind_newlimts(const nvlist_t *oldlimits,
1138    const nvlist_t *newfunclimit)
1139{
1140	const nvlist_t *oldfunclimits;
1141
1142	oldfunclimits = NULL;
1143	if (oldlimits != NULL) {
1144		oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_BIND,
1145		    NULL);
1146	}
1147
1148	return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1149}
1150
1151
1152static bool
1153verify_connect_newlimits(const nvlist_t *oldlimits,
1154    const nvlist_t *newfunclimit)
1155{
1156	const nvlist_t *oldfunclimits;
1157
1158	oldfunclimits = NULL;
1159	if (oldlimits != NULL) {
1160		oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_CONNECT,
1161		    NULL);
1162	}
1163
1164	return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1165}
1166
1167static bool
1168verify_addr2name_newlimits(const nvlist_t *oldlimits,
1169    const nvlist_t *newfunclimit)
1170{
1171	void *cookie;
1172	const nvlist_t *oldfunclimits;
1173
1174	oldfunclimits = NULL;
1175	if (oldlimits != NULL) {
1176		oldfunclimits = dnvlist_get_nvlist(oldlimits,
1177		    LIMIT_NV_ADDR2NAME, NULL);
1178	}
1179
1180	cookie = NULL;
1181	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1182		if (strcmp(cnvlist_name(cookie), "sockaddr") == 0) {
1183			void *sacookie;
1184
1185			if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1186				return (false);
1187
1188			sacookie = NULL;
1189			while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1190			    &sacookie) != NULL) {
1191				const void *sa;
1192				size_t sasize;
1193
1194				if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1195					return (false);
1196
1197				sa = cnvlist_get_binary(sacookie, &sasize);
1198				if (!net_allowed_bsaddr(oldfunclimits, sa,
1199				    sasize)) {
1200					return (false);
1201				}
1202			}
1203		} else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1204			size_t i, sfamilies;
1205			const uint64_t *families;
1206
1207			if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1208				return (false);
1209
1210			families = cnvlist_get_number_array(cookie, &sfamilies);
1211			for (i = 0; i < sfamilies; i++) {
1212				if (!net_allowed_family(oldfunclimits,
1213				    families[i])) {
1214					return (false);
1215				}
1216			}
1217		} else {
1218			return (false);
1219		}
1220	}
1221
1222	return (true);
1223}
1224
1225static bool
1226verify_name2addr_newlimits(const nvlist_t *oldlimits,
1227    const nvlist_t *newfunclimit)
1228{
1229	void *cookie;
1230	const nvlist_t *oldfunclimits;
1231
1232	oldfunclimits = NULL;
1233	if (oldlimits != NULL) {
1234		oldfunclimits = dnvlist_get_nvlist(oldlimits,
1235		    LIMIT_NV_NAME2ADDR, NULL);
1236	}
1237
1238	cookie = NULL;
1239	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1240		if (strcmp(cnvlist_name(cookie), "hosts") == 0) {
1241			void *hostcookie;
1242
1243			if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1244				return (false);
1245
1246			hostcookie = NULL;
1247			while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1248			    &hostcookie) != NULL) {
1249				if (cnvlist_type(hostcookie) != NV_TYPE_STRING)
1250					return (false);
1251
1252				if (!net_allowed_hosts(oldfunclimits,
1253				    cnvlist_name(hostcookie),
1254				    cnvlist_get_string(hostcookie))) {
1255					return (false);
1256				}
1257			}
1258		} else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1259			size_t i, sfamilies;
1260			const uint64_t *families;
1261
1262			if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1263				return (false);
1264
1265			families = cnvlist_get_number_array(cookie, &sfamilies);
1266			for (i = 0; i < sfamilies; i++) {
1267				if (!net_allowed_family(oldfunclimits,
1268				    families[i])) {
1269					return (false);
1270				}
1271			}
1272		} else {
1273			return (false);
1274		}
1275	}
1276
1277	return (true);
1278}
1279
1280static int
1281net_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
1282{
1283	const char *name;
1284	void *cookie;
1285	bool hasmode, hasconnect, hasbind, hasaddr2name, hasname2addr;
1286
1287	/*
1288	 * Modes:
1289	 *	ADDR2NAME:
1290	 *		getnameinfo
1291	 *	DEPRECATED_ADDR2NAME:
1292	 *		gethostbyaddr
1293	 *
1294	 *	NAME2ADDR:
1295	 *		getaddrinfo
1296	 *	DEPRECATED_NAME2ADDR:
1297	 *		gethostbyname
1298	 *
1299	 * Limit scheme:
1300	 *	mode	: NV_TYPE_NUMBER
1301	 *	connect : NV_TYPE_NVLIST
1302	 *		sockaddr : NV_TYPE_NVLIST
1303	 *			""	: NV_TYPE_BINARY
1304	 *			...	: NV_TYPE_BINARY
1305	 *	bind	: NV_TYPE_NVLIST
1306	 *		sockaddr : NV_TYPE_NVLIST
1307	 *			""	: NV_TYPE_BINARY
1308	 *			...	: NV_TYPE_BINARY
1309	 *	addr2name : NV_TYPE_NVLIST
1310	 *		family  : NV_TYPE_NUMBER_ARRAY
1311	 *		sockaddr : NV_TYPE_NVLIST
1312	 *			""	: NV_TYPE_BINARY
1313	 *			...	: NV_TYPE_BINARY
1314	 *	name2addr : NV_TYPE_NVLIST
1315	 *		family : NV_TYPE_NUMBER
1316	 *		hosts	: NV_TYPE_NVLIST
1317	 *			host	: servname : NV_TYPE_STRING
1318	 */
1319
1320	hasmode = false;
1321	hasconnect = false;
1322	hasbind = false;
1323	hasaddr2name = false;
1324	hasname2addr = false;
1325
1326	cookie = NULL;
1327	while ((name = nvlist_next(newlimits, NULL, &cookie)) != NULL) {
1328		if (strcmp(name, "mode") == 0) {
1329			if (cnvlist_type(cookie) != NV_TYPE_NUMBER) {
1330				return (NO_RECOVERY);
1331			}
1332			if (!net_allowed_mode(oldlimits,
1333			    cnvlist_get_number(cookie))) {
1334				return (ENOTCAPABLE);
1335			}
1336			hasmode = true;
1337			continue;
1338		}
1339
1340		if (cnvlist_type(cookie) != NV_TYPE_NVLIST) {
1341			return (NO_RECOVERY);
1342		}
1343
1344		if (strcmp(name, LIMIT_NV_BIND) == 0) {
1345			hasbind = true;
1346			if (!verify_bind_newlimts(oldlimits,
1347			    cnvlist_get_nvlist(cookie))) {
1348				return (ENOTCAPABLE);
1349			}
1350		} else if (strcmp(name, LIMIT_NV_CONNECT) == 0) {
1351			hasconnect = true;
1352			if (!verify_connect_newlimits(oldlimits,
1353			    cnvlist_get_nvlist(cookie))) {
1354				return (ENOTCAPABLE);
1355			}
1356		} else if (strcmp(name, LIMIT_NV_ADDR2NAME) == 0) {
1357			hasaddr2name = true;
1358			if (!verify_addr2name_newlimits(oldlimits,
1359			    cnvlist_get_nvlist(cookie))) {
1360				return (ENOTCAPABLE);
1361			}
1362		} else if (strcmp(name, LIMIT_NV_NAME2ADDR) == 0) {
1363			hasname2addr = true;
1364			if (!verify_name2addr_newlimits(oldlimits,
1365			    cnvlist_get_nvlist(cookie))) {
1366				return (ENOTCAPABLE);
1367			}
1368		}
1369	}
1370
1371	/* Mode is required. */
1372	if (!hasmode)
1373		return (ENOTCAPABLE);
1374
1375	/*
1376	 * If the new limit doesn't mention mode or family we have to
1377	 * check if the current limit does have those. Missing mode or
1378	 * family in the limit means that all modes or families are
1379	 * allowed.
1380	 */
1381	if (oldlimits == NULL)
1382		return (0);
1383	if (!hasbind && nvlist_exists(oldlimits, LIMIT_NV_BIND))
1384		return (ENOTCAPABLE);
1385	if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_CONNECT))
1386		return (ENOTCAPABLE);
1387	if (!hasaddr2name && nvlist_exists(oldlimits, LIMIT_NV_ADDR2NAME))
1388		return (ENOTCAPABLE);
1389	if (!hasname2addr && nvlist_exists(oldlimits, LIMIT_NV_NAME2ADDR))
1390		return (ENOTCAPABLE);
1391	return (0);
1392}
1393
1394static int
1395net_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
1396    nvlist_t *nvlout)
1397{
1398
1399	if (strcmp(cmd, "bind") == 0)
1400		return (net_bind(limits, nvlin, nvlout));
1401	else if (strcmp(cmd, "connect") == 0)
1402		return (net_connect(limits, nvlin, nvlout));
1403	else if (strcmp(cmd, "gethostbyname") == 0)
1404		return (net_gethostbyname(limits, nvlin, nvlout));
1405	else if (strcmp(cmd, "gethostbyaddr") == 0)
1406		return (net_gethostbyaddr(limits, nvlin, nvlout));
1407	else if (strcmp(cmd, "getnameinfo") == 0)
1408		return (net_getnameinfo(limits, nvlin, nvlout));
1409	else if (strcmp(cmd, "getaddrinfo") == 0)
1410		return (net_getaddrinfo(limits, nvlin, nvlout));
1411
1412	return (EINVAL);
1413}
1414
1415CREATE_SERVICE("system.net", net_limit, net_command, 0);
1416