1/*-
2 * Copyright (c) 2012-2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
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
49static struct hostent hent;
50
51static void
52hostent_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
72static struct hostent *
73hostent_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);
117fail:
118	hostent_free(hp);
119	h_errno = NO_RECOVERY;
120	return (NULL);
121}
122
123struct hostent *
124cap_gethostbyname(cap_channel_t *chan, const char *name)
125{
126
127	return (cap_gethostbyname2(chan, name, AF_INET));
128}
129
130struct hostent *
131cap_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, 0);
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
156struct hostent *
157cap_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, 0);
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
182static struct addrinfo *
183addrinfo_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
216int
217cap_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, 0);
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
280int
281cap_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, 0);
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 + 1);
304	if (serv != NULL && nvlist_exists_string(nvl, "serv"))
305		strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
306	nvlist_destroy(nvl);
307	return (0);
308}
309
310static void
311limit_remove(nvlist_t *limits, const char *prefix)
312{
313	const char *name;
314	size_t prefixlen;
315	void *cookie;
316
317	prefixlen = strlen(prefix);
318again:
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
328int
329cap_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
351int
352cap_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 */
377static bool
378dns_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
404static bool
405dns_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
433static void
434hostent_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
468static int
469dns_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, "NAME"))
476		return (NO_RECOVERY);
477
478	family = (int)nvlist_get_number(nvlin, "family");
479
480	if (!dns_allowed_family(limits, family))
481		return (NO_RECOVERY);
482
483	hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
484	if (hp == NULL)
485		return (h_errno);
486	hostent_pack(hp, nvlout);
487	return (0);
488}
489
490static int
491dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
492    nvlist_t *nvlout)
493{
494	struct hostent *hp;
495	const void *addr;
496	size_t addrsize;
497	int family;
498
499	if (!dns_allowed_type(limits, "ADDR"))
500		return (NO_RECOVERY);
501
502	family = (int)nvlist_get_number(nvlin, "family");
503
504	if (!dns_allowed_family(limits, family))
505		return (NO_RECOVERY);
506
507	addr = nvlist_get_binary(nvlin, "addr", &addrsize);
508	hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
509	if (hp == NULL)
510		return (h_errno);
511	hostent_pack(hp, nvlout);
512	return (0);
513}
514
515static int
516dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
517{
518	struct sockaddr_storage sast;
519	const void *sabin;
520	char *host, *serv;
521	size_t sabinsize, hostlen, servlen;
522	socklen_t salen;
523	int error, flags;
524
525	if (!dns_allowed_type(limits, "NAME"))
526		return (NO_RECOVERY);
527
528	error = 0;
529	host = serv = NULL;
530	memset(&sast, 0, sizeof(sast));
531
532	hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
533	servlen = (size_t)nvlist_get_number(nvlin, "servlen");
534
535	if (hostlen > 0) {
536		host = calloc(1, hostlen + 1);
537		if (host == NULL) {
538			error = EAI_MEMORY;
539			goto out;
540		}
541	}
542	if (servlen > 0) {
543		serv = calloc(1, servlen + 1);
544		if (serv == NULL) {
545			error = EAI_MEMORY;
546			goto out;
547		}
548	}
549
550	sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
551	if (sabinsize > sizeof(sast)) {
552		error = EAI_FAIL;
553		goto out;
554	}
555
556	memcpy(&sast, sabin, sabinsize);
557	salen = (socklen_t)sabinsize;
558
559	if ((sast.ss_family != AF_INET ||
560	     salen != sizeof(struct sockaddr_in)) &&
561	    (sast.ss_family != AF_INET6 ||
562	     salen != sizeof(struct sockaddr_in6))) {
563		error = EAI_FAIL;
564		goto out;
565	}
566
567	if (!dns_allowed_family(limits, (int)sast.ss_family)) {
568		error = NO_RECOVERY;
569		goto out;
570	}
571
572	flags = (int)nvlist_get_number(nvlin, "flags");
573
574	error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
575	    serv, servlen, flags);
576	if (error != 0)
577		goto out;
578
579	if (host != NULL)
580		nvlist_move_string(nvlout, "host", host);
581	if (serv != NULL)
582		nvlist_move_string(nvlout, "serv", serv);
583out:
584	if (error != 0) {
585		free(host);
586		free(serv);
587	}
588	return (error);
589}
590
591static nvlist_t *
592addrinfo_pack(const struct addrinfo *ai)
593{
594	nvlist_t *nvl;
595
596	nvl = nvlist_create(0);
597	nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
598	nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
599	nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
600	nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
601	nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
602	if (ai->ai_canonname != NULL)
603		nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
604
605	return (nvl);
606}
607
608static int
609dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
610{
611	struct addrinfo hints, *hintsp, *res, *cur;
612	const char *hostname, *servname;
613	char nvlname[64];
614	nvlist_t *elem;
615	unsigned int ii;
616	int error, family, n;
617
618	if (!dns_allowed_type(limits, "ADDR"))
619		return (NO_RECOVERY);
620
621	hostname = dnvlist_get_string(nvlin, "hostname", NULL);
622	servname = dnvlist_get_string(nvlin, "servname", NULL);
623	if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
624		hints.ai_flags = (int)nvlist_get_number(nvlin,
625		    "hints.ai_flags");
626		hints.ai_family = (int)nvlist_get_number(nvlin,
627		    "hints.ai_family");
628		hints.ai_socktype = (int)nvlist_get_number(nvlin,
629		    "hints.ai_socktype");
630		hints.ai_protocol = (int)nvlist_get_number(nvlin,
631		    "hints.ai_protocol");
632		hints.ai_addrlen = 0;
633		hints.ai_addr = NULL;
634		hints.ai_canonname = NULL;
635		hints.ai_next = NULL;
636		hintsp = &hints;
637		family = hints.ai_family;
638	} else {
639		hintsp = NULL;
640		family = AF_UNSPEC;
641	}
642
643	if (!dns_allowed_family(limits, family))
644		return (NO_RECOVERY);
645
646	error = getaddrinfo(hostname, servname, hintsp, &res);
647	if (error != 0)
648		goto out;
649
650	for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
651		elem = addrinfo_pack(cur);
652		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
653		assert(n > 0 && n < (int)sizeof(nvlname));
654		nvlist_move_nvlist(nvlout, nvlname, elem);
655	}
656
657	freeaddrinfo(res);
658	error = 0;
659out:
660	return (error);
661}
662
663static bool
664limit_has_entry(const nvlist_t *limits, const char *prefix)
665{
666	const char *name;
667	size_t prefixlen;
668	void *cookie;
669
670	if (limits == NULL)
671		return (false);
672
673	prefixlen = strlen(prefix);
674
675	cookie = NULL;
676	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
677		if (strncmp(name, prefix, prefixlen) == 0)
678			return (true);
679	}
680
681	return (false);
682}
683
684static int
685dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
686{
687	const char *name;
688	void *cookie;
689	int nvtype;
690	bool hastype, hasfamily;
691
692	hastype = false;
693	hasfamily = false;
694
695	cookie = NULL;
696	while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
697		if (nvtype == NV_TYPE_STRING) {
698			const char *type;
699
700			if (strncmp(name, "type", sizeof("type") - 1) != 0)
701				return (EINVAL);
702			type = nvlist_get_string(newlimits, name);
703			if (strcmp(type, "ADDR") != 0 &&
704			    strcmp(type, "NAME") != 0) {
705				return (EINVAL);
706			}
707			if (!dns_allowed_type(oldlimits, type))
708				return (ENOTCAPABLE);
709			hastype = true;
710		} else if (nvtype == NV_TYPE_NUMBER) {
711			int family;
712
713			if (strncmp(name, "family", sizeof("family") - 1) != 0)
714				return (EINVAL);
715			family = (int)nvlist_get_number(newlimits, name);
716			if (!dns_allowed_family(oldlimits, family))
717				return (ENOTCAPABLE);
718			hasfamily = true;
719		} else {
720			return (EINVAL);
721		}
722	}
723
724	/*
725	 * If the new limit doesn't mention type or family we have to
726	 * check if the current limit does have those. Missing type or
727	 * family in the limit means that all types or families are
728	 * allowed.
729	 */
730	if (!hastype) {
731		if (limit_has_entry(oldlimits, "type"))
732			return (ENOTCAPABLE);
733	}
734	if (!hasfamily) {
735		if (limit_has_entry(oldlimits, "family"))
736			return (ENOTCAPABLE);
737	}
738
739	return (0);
740}
741
742static int
743dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
744    nvlist_t *nvlout)
745{
746	int error;
747
748	if (strcmp(cmd, "gethostbyname") == 0)
749		error = dns_gethostbyname(limits, nvlin, nvlout);
750	else if (strcmp(cmd, "gethostbyaddr") == 0)
751		error = dns_gethostbyaddr(limits, nvlin, nvlout);
752	else if (strcmp(cmd, "getnameinfo") == 0)
753		error = dns_getnameinfo(limits, nvlin, nvlout);
754	else if (strcmp(cmd, "getaddrinfo") == 0)
755		error = dns_getaddrinfo(limits, nvlin, nvlout);
756	else
757		error = NO_RECOVERY;
758
759	return (error);
760}
761
762CREATE_SERVICE("system.dns", dns_limit, dns_command, 0);
763