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