1296047Soshogbo/*-
2296047Soshogbo * Copyright (c) 2012-2013 The FreeBSD Foundation
3296047Soshogbo * All rights reserved.
4296047Soshogbo *
5296047Soshogbo * This software was developed by Pawel Jakub Dawidek under sponsorship from
6296047Soshogbo * the FreeBSD Foundation.
7296047Soshogbo *
8296047Soshogbo * Redistribution and use in source and binary forms, with or without
9296047Soshogbo * modification, are permitted provided that the following conditions
10296047Soshogbo * are met:
11296047Soshogbo * 1. Redistributions of source code must retain the above copyright
12296047Soshogbo *    notice, this list of conditions and the following disclaimer.
13296047Soshogbo * 2. Redistributions in binary form must reproduce the above copyright
14296047Soshogbo *    notice, this list of conditions and the following disclaimer in the
15296047Soshogbo *    documentation and/or other materials provided with the distribution.
16296047Soshogbo *
17296047Soshogbo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18296047Soshogbo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19296047Soshogbo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20296047Soshogbo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21296047Soshogbo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22296047Soshogbo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23296047Soshogbo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24296047Soshogbo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25296047Soshogbo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26296047Soshogbo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27296047Soshogbo * SUCH DAMAGE.
28296047Soshogbo */
29296047Soshogbo
30296047Soshogbo#include <sys/cdefs.h>
31296047Soshogbo__FBSDID("$FreeBSD$");
32296047Soshogbo
33296047Soshogbo#include <sys/dnv.h>
34296047Soshogbo#include <sys/nv.h>
35296047Soshogbo#include <netinet/in.h>
36296047Soshogbo
37296047Soshogbo#include <assert.h>
38296047Soshogbo#include <errno.h>
39296047Soshogbo#include <netdb.h>
40296047Soshogbo#include <stdlib.h>
41296047Soshogbo#include <string.h>
42296047Soshogbo#include <unistd.h>
43296047Soshogbo
44296047Soshogbo#include <libcasper.h>
45296047Soshogbo#include <libcasper_service.h>
46296047Soshogbo
47296047Soshogbo#include "cap_dns.h"
48296047Soshogbo
49296047Soshogbostatic struct hostent hent;
50296047Soshogbo
51296047Soshogbostatic void
52296047Soshogbohostent_free(struct hostent *hp)
53296047Soshogbo{
54296047Soshogbo	unsigned int ii;
55296047Soshogbo
56296047Soshogbo	free(hp->h_name);
57296047Soshogbo	hp->h_name = NULL;
58296047Soshogbo	if (hp->h_aliases != NULL) {
59296047Soshogbo		for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
60296047Soshogbo			free(hp->h_aliases[ii]);
61296047Soshogbo		free(hp->h_aliases);
62296047Soshogbo		hp->h_aliases = NULL;
63296047Soshogbo	}
64296047Soshogbo	if (hp->h_addr_list != NULL) {
65296047Soshogbo		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
66296047Soshogbo			free(hp->h_addr_list[ii]);
67296047Soshogbo		free(hp->h_addr_list);
68296047Soshogbo		hp->h_addr_list = NULL;
69296047Soshogbo	}
70296047Soshogbo}
71296047Soshogbo
72296047Soshogbostatic struct hostent *
73296047Soshogbohostent_unpack(const nvlist_t *nvl, struct hostent *hp)
74296047Soshogbo{
75296047Soshogbo	unsigned int ii, nitems;
76296047Soshogbo	char nvlname[64];
77296047Soshogbo	int n;
78296047Soshogbo
79296047Soshogbo	hostent_free(hp);
80296047Soshogbo
81296047Soshogbo	hp->h_name = strdup(nvlist_get_string(nvl, "name"));
82296047Soshogbo	if (hp->h_name == NULL)
83296047Soshogbo		goto fail;
84296047Soshogbo	hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
85296047Soshogbo	hp->h_length = (int)nvlist_get_number(nvl, "length");
86296047Soshogbo
87296047Soshogbo	nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
88296047Soshogbo	hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
89296047Soshogbo	if (hp->h_aliases == NULL)
90296047Soshogbo		goto fail;
91296047Soshogbo	for (ii = 0; ii < nitems; ii++) {
92296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
93296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
94296047Soshogbo		hp->h_aliases[ii] =
95296047Soshogbo		    strdup(nvlist_get_string(nvl, nvlname));
96296047Soshogbo		if (hp->h_aliases[ii] == NULL)
97296047Soshogbo			goto fail;
98296047Soshogbo	}
99296047Soshogbo	hp->h_aliases[ii] = NULL;
100296047Soshogbo
101296047Soshogbo	nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
102296047Soshogbo	hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
103296047Soshogbo	if (hp->h_addr_list == NULL)
104296047Soshogbo		goto fail;
105296047Soshogbo	for (ii = 0; ii < nitems; ii++) {
106296047Soshogbo		hp->h_addr_list[ii] = malloc(hp->h_length);
107296047Soshogbo		if (hp->h_addr_list[ii] == NULL)
108296047Soshogbo			goto fail;
109296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
110296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
111296047Soshogbo		bcopy(nvlist_get_binary(nvl, nvlname, NULL),
112296047Soshogbo		    hp->h_addr_list[ii], hp->h_length);
113296047Soshogbo	}
114296047Soshogbo	hp->h_addr_list[ii] = NULL;
115296047Soshogbo
116296047Soshogbo	return (hp);
117296047Soshogbofail:
118296047Soshogbo	hostent_free(hp);
119296047Soshogbo	h_errno = NO_RECOVERY;
120296047Soshogbo	return (NULL);
121296047Soshogbo}
122296047Soshogbo
123296047Soshogbostruct hostent *
124296047Soshogbocap_gethostbyname(cap_channel_t *chan, const char *name)
125296047Soshogbo{
126296047Soshogbo
127296047Soshogbo	return (cap_gethostbyname2(chan, name, AF_INET));
128296047Soshogbo}
129296047Soshogbo
130296047Soshogbostruct hostent *
131296047Soshogbocap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
132296047Soshogbo{
133296047Soshogbo	struct hostent *hp;
134296047Soshogbo	nvlist_t *nvl;
135296047Soshogbo
136296047Soshogbo	nvl = nvlist_create(0);
137296047Soshogbo	nvlist_add_string(nvl, "cmd", "gethostbyname");
138296047Soshogbo	nvlist_add_number(nvl, "family", (uint64_t)type);
139296047Soshogbo	nvlist_add_string(nvl, "name", name);
140296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
141296047Soshogbo	if (nvl == NULL) {
142296047Soshogbo		h_errno = NO_RECOVERY;
143296047Soshogbo		return (NULL);
144296047Soshogbo	}
145296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
146296047Soshogbo		h_errno = (int)nvlist_get_number(nvl, "error");
147296047Soshogbo		nvlist_destroy(nvl);
148296047Soshogbo		return (NULL);
149296047Soshogbo	}
150296047Soshogbo
151296047Soshogbo	hp = hostent_unpack(nvl, &hent);
152296047Soshogbo	nvlist_destroy(nvl);
153296047Soshogbo	return (hp);
154296047Soshogbo}
155296047Soshogbo
156296047Soshogbostruct hostent *
157296047Soshogbocap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
158296047Soshogbo    int type)
159296047Soshogbo{
160296047Soshogbo	struct hostent *hp;
161296047Soshogbo	nvlist_t *nvl;
162296047Soshogbo
163296047Soshogbo	nvl = nvlist_create(0);
164296047Soshogbo	nvlist_add_string(nvl, "cmd", "gethostbyaddr");
165296047Soshogbo	nvlist_add_binary(nvl, "addr", addr, (size_t)len);
166296047Soshogbo	nvlist_add_number(nvl, "family", (uint64_t)type);
167296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
168296047Soshogbo	if (nvl == NULL) {
169296047Soshogbo		h_errno = NO_RECOVERY;
170296047Soshogbo		return (NULL);
171296047Soshogbo	}
172296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
173296047Soshogbo		h_errno = (int)nvlist_get_number(nvl, "error");
174296047Soshogbo		nvlist_destroy(nvl);
175296047Soshogbo		return (NULL);
176296047Soshogbo	}
177296047Soshogbo	hp = hostent_unpack(nvl, &hent);
178296047Soshogbo	nvlist_destroy(nvl);
179296047Soshogbo	return (hp);
180296047Soshogbo}
181296047Soshogbo
182296047Soshogbostatic struct addrinfo *
183296047Soshogboaddrinfo_unpack(const nvlist_t *nvl)
184296047Soshogbo{
185296047Soshogbo	struct addrinfo *ai;
186296047Soshogbo	const void *addr;
187296047Soshogbo	size_t addrlen;
188296047Soshogbo	const char *canonname;
189296047Soshogbo
190296047Soshogbo	addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
191296047Soshogbo	ai = malloc(sizeof(*ai) + addrlen);
192296047Soshogbo	if (ai == NULL)
193296047Soshogbo		return (NULL);
194296047Soshogbo	ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
195296047Soshogbo	ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
196296047Soshogbo	ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
197296047Soshogbo	ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
198296047Soshogbo	ai->ai_addrlen = (socklen_t)addrlen;
199296047Soshogbo	canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
200296047Soshogbo	if (canonname != NULL) {
201296047Soshogbo		ai->ai_canonname = strdup(canonname);
202296047Soshogbo		if (ai->ai_canonname == NULL) {
203296047Soshogbo			free(ai);
204296047Soshogbo			return (NULL);
205296047Soshogbo		}
206296047Soshogbo	} else {
207296047Soshogbo		ai->ai_canonname = NULL;
208296047Soshogbo	}
209296047Soshogbo	ai->ai_addr = (void *)(ai + 1);
210296047Soshogbo	bcopy(addr, ai->ai_addr, addrlen);
211296047Soshogbo	ai->ai_next = NULL;
212296047Soshogbo
213296047Soshogbo	return (ai);
214296047Soshogbo}
215296047Soshogbo
216296047Soshogboint
217296047Soshogbocap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
218296047Soshogbo    const struct addrinfo *hints, struct addrinfo **res)
219296047Soshogbo{
220296047Soshogbo	struct addrinfo *firstai, *prevai, *curai;
221296047Soshogbo	unsigned int ii;
222296047Soshogbo	const nvlist_t *nvlai;
223296047Soshogbo	char nvlname[64];
224296047Soshogbo	nvlist_t *nvl;
225296047Soshogbo	int error, n;
226296047Soshogbo
227296047Soshogbo	nvl = nvlist_create(0);
228296047Soshogbo	nvlist_add_string(nvl, "cmd", "getaddrinfo");
229296047Soshogbo	if (hostname != NULL)
230296047Soshogbo		nvlist_add_string(nvl, "hostname", hostname);
231296047Soshogbo	if (servname != NULL)
232296047Soshogbo		nvlist_add_string(nvl, "servname", servname);
233296047Soshogbo	if (hints != NULL) {
234296047Soshogbo		nvlist_add_number(nvl, "hints.ai_flags",
235296047Soshogbo		    (uint64_t)hints->ai_flags);
236296047Soshogbo		nvlist_add_number(nvl, "hints.ai_family",
237296047Soshogbo		    (uint64_t)hints->ai_family);
238296047Soshogbo		nvlist_add_number(nvl, "hints.ai_socktype",
239296047Soshogbo		    (uint64_t)hints->ai_socktype);
240296047Soshogbo		nvlist_add_number(nvl, "hints.ai_protocol",
241296047Soshogbo		    (uint64_t)hints->ai_protocol);
242296047Soshogbo	}
243296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
244296047Soshogbo	if (nvl == NULL)
245296047Soshogbo		return (EAI_MEMORY);
246296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
247296047Soshogbo		error = (int)nvlist_get_number(nvl, "error");
248296047Soshogbo		nvlist_destroy(nvl);
249296047Soshogbo		return (error);
250296047Soshogbo	}
251296047Soshogbo
252296047Soshogbo	nvlai = NULL;
253296047Soshogbo	firstai = prevai = curai = NULL;
254296047Soshogbo	for (ii = 0; ; ii++) {
255296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
256296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
257296047Soshogbo		if (!nvlist_exists_nvlist(nvl, nvlname))
258296047Soshogbo			break;
259296047Soshogbo		nvlai = nvlist_get_nvlist(nvl, nvlname);
260296047Soshogbo		curai = addrinfo_unpack(nvlai);
261296047Soshogbo		if (curai == NULL)
262296047Soshogbo			break;
263296047Soshogbo		if (prevai != NULL)
264296047Soshogbo			prevai->ai_next = curai;
265296047Soshogbo		else if (firstai == NULL)
266296047Soshogbo			firstai = curai;
267296047Soshogbo		prevai = curai;
268296047Soshogbo	}
269296047Soshogbo	nvlist_destroy(nvl);
270296047Soshogbo	if (curai == NULL && nvlai != NULL) {
271296047Soshogbo		if (firstai == NULL)
272296047Soshogbo			freeaddrinfo(firstai);
273296047Soshogbo		return (EAI_MEMORY);
274296047Soshogbo	}
275296047Soshogbo
276296047Soshogbo	*res = firstai;
277296047Soshogbo	return (0);
278296047Soshogbo}
279296047Soshogbo
280296047Soshogboint
281296047Soshogbocap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
282296047Soshogbo    char *host, size_t hostlen, char *serv, size_t servlen, int flags)
283296047Soshogbo{
284296047Soshogbo	nvlist_t *nvl;
285296047Soshogbo	int error;
286296047Soshogbo
287296047Soshogbo	nvl = nvlist_create(0);
288296047Soshogbo	nvlist_add_string(nvl, "cmd", "getnameinfo");
289296047Soshogbo	nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
290296047Soshogbo	nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
291296047Soshogbo	nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
292296047Soshogbo	nvlist_add_number(nvl, "flags", (uint64_t)flags);
293296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
294296047Soshogbo	if (nvl == NULL)
295296047Soshogbo		return (EAI_MEMORY);
296296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
297296047Soshogbo		error = (int)nvlist_get_number(nvl, "error");
298296047Soshogbo		nvlist_destroy(nvl);
299296047Soshogbo		return (error);
300296047Soshogbo	}
301296047Soshogbo
302296047Soshogbo	if (host != NULL && nvlist_exists_string(nvl, "host"))
303296047Soshogbo		strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
304296047Soshogbo	if (serv != NULL && nvlist_exists_string(nvl, "serv"))
305296047Soshogbo		strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
306296047Soshogbo	nvlist_destroy(nvl);
307296047Soshogbo	return (0);
308296047Soshogbo}
309296047Soshogbo
310296047Soshogbostatic void
311296047Soshogbolimit_remove(nvlist_t *limits, const char *prefix)
312296047Soshogbo{
313296047Soshogbo	const char *name;
314296047Soshogbo	size_t prefixlen;
315296047Soshogbo	void *cookie;
316296047Soshogbo
317296047Soshogbo	prefixlen = strlen(prefix);
318296047Soshogboagain:
319296047Soshogbo	cookie = NULL;
320296047Soshogbo	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
321296047Soshogbo		if (strncmp(name, prefix, prefixlen) == 0) {
322296047Soshogbo			nvlist_free(limits, name);
323296047Soshogbo			goto again;
324296047Soshogbo		}
325296047Soshogbo	}
326296047Soshogbo}
327296047Soshogbo
328296047Soshogboint
329296047Soshogbocap_dns_type_limit(cap_channel_t *chan, const char * const *types,
330296047Soshogbo    size_t ntypes)
331296047Soshogbo{
332296047Soshogbo	nvlist_t *limits;
333296047Soshogbo	unsigned int i;
334296047Soshogbo	char nvlname[64];
335296047Soshogbo	int n;
336296047Soshogbo
337296047Soshogbo	if (cap_limit_get(chan, &limits) < 0)
338296047Soshogbo		return (-1);
339296047Soshogbo	if (limits == NULL)
340296047Soshogbo		limits = nvlist_create(0);
341296047Soshogbo	else
342296047Soshogbo		limit_remove(limits, "type");
343296047Soshogbo	for (i = 0; i < ntypes; i++) {
344296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "type%u", i);
345296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
346296047Soshogbo		nvlist_add_string(limits, nvlname, types[i]);
347296047Soshogbo	}
348296047Soshogbo	return (cap_limit_set(chan, limits));
349296047Soshogbo}
350296047Soshogbo
351296047Soshogboint
352296047Soshogbocap_dns_family_limit(cap_channel_t *chan, const int *families,
353296047Soshogbo    size_t nfamilies)
354296047Soshogbo{
355296047Soshogbo	nvlist_t *limits;
356296047Soshogbo	unsigned int i;
357296047Soshogbo	char nvlname[64];
358296047Soshogbo	int n;
359296047Soshogbo
360296047Soshogbo	if (cap_limit_get(chan, &limits) < 0)
361296047Soshogbo		return (-1);
362296047Soshogbo	if (limits == NULL)
363296047Soshogbo		limits = nvlist_create(0);
364296047Soshogbo	else
365296047Soshogbo		limit_remove(limits, "family");
366296047Soshogbo	for (i = 0; i < nfamilies; i++) {
367296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "family%u", i);
368296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
369296047Soshogbo		nvlist_add_number(limits, nvlname, (uint64_t)families[i]);
370296047Soshogbo	}
371296047Soshogbo	return (cap_limit_set(chan, limits));
372296047Soshogbo}
373296047Soshogbo
374296047Soshogbo/*
375296047Soshogbo * Service functions.
376296047Soshogbo */
377296047Soshogbostatic bool
378296047Soshogbodns_allowed_type(const nvlist_t *limits, const char *type)
379296047Soshogbo{
380296047Soshogbo	const char *name;
381296047Soshogbo	bool notypes;
382296047Soshogbo	void *cookie;
383296047Soshogbo
384296047Soshogbo	if (limits == NULL)
385296047Soshogbo		return (true);
386296047Soshogbo
387296047Soshogbo	notypes = true;
388296047Soshogbo	cookie = NULL;
389296047Soshogbo	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
390296047Soshogbo		if (strncmp(name, "type", sizeof("type") - 1) != 0)
391296047Soshogbo			continue;
392296047Soshogbo		notypes = false;
393296047Soshogbo		if (strcmp(nvlist_get_string(limits, name), type) == 0)
394296047Soshogbo			return (true);
395296047Soshogbo	}
396296047Soshogbo
397296047Soshogbo	/* If there are no types at all, allow any type. */
398296047Soshogbo	if (notypes)
399296047Soshogbo		return (true);
400296047Soshogbo
401296047Soshogbo	return (false);
402296047Soshogbo}
403296047Soshogbo
404296047Soshogbostatic bool
405296047Soshogbodns_allowed_family(const nvlist_t *limits, int family)
406296047Soshogbo{
407296047Soshogbo	const char *name;
408296047Soshogbo	bool nofamilies;
409296047Soshogbo	void *cookie;
410296047Soshogbo
411296047Soshogbo	if (limits == NULL)
412296047Soshogbo		return (true);
413296047Soshogbo
414296047Soshogbo	nofamilies = true;
415296047Soshogbo	cookie = NULL;
416296047Soshogbo	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
417296047Soshogbo		if (strncmp(name, "family", sizeof("family") - 1) != 0)
418296047Soshogbo			continue;
419296047Soshogbo		nofamilies = false;
420296047Soshogbo		if (family == AF_UNSPEC)
421296047Soshogbo			continue;
422296047Soshogbo		if (nvlist_get_number(limits, name) == (uint64_t)family)
423296047Soshogbo			return (true);
424296047Soshogbo	}
425296047Soshogbo
426296047Soshogbo	/* If there are no families at all, allow any family. */
427296047Soshogbo	if (nofamilies)
428296047Soshogbo		return (true);
429296047Soshogbo
430296047Soshogbo	return (false);
431296047Soshogbo}
432296047Soshogbo
433296047Soshogbostatic void
434296047Soshogbohostent_pack(const struct hostent *hp, nvlist_t *nvl)
435296047Soshogbo{
436296047Soshogbo	unsigned int ii;
437296047Soshogbo	char nvlname[64];
438296047Soshogbo	int n;
439296047Soshogbo
440296047Soshogbo	nvlist_add_string(nvl, "name", hp->h_name);
441296047Soshogbo	nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
442296047Soshogbo	nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
443296047Soshogbo
444296047Soshogbo	if (hp->h_aliases == NULL) {
445296047Soshogbo		nvlist_add_number(nvl, "naliases", 0);
446296047Soshogbo	} else {
447296047Soshogbo		for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
448296047Soshogbo			n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
449296047Soshogbo			assert(n > 0 && n < (int)sizeof(nvlname));
450296047Soshogbo			nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
451296047Soshogbo		}
452296047Soshogbo		nvlist_add_number(nvl, "naliases", (uint64_t)ii);
453296047Soshogbo	}
454296047Soshogbo
455296047Soshogbo	if (hp->h_addr_list == NULL) {
456296047Soshogbo		nvlist_add_number(nvl, "naddrs", 0);
457296047Soshogbo	} else {
458296047Soshogbo		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
459296047Soshogbo			n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
460296047Soshogbo			assert(n > 0 && n < (int)sizeof(nvlname));
461296047Soshogbo			nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
462296047Soshogbo			    (size_t)hp->h_length);
463296047Soshogbo		}
464296047Soshogbo		nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
465296047Soshogbo	}
466296047Soshogbo}
467296047Soshogbo
468296047Soshogbostatic int
469296047Soshogbodns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
470296047Soshogbo    nvlist_t *nvlout)
471296047Soshogbo{
472296047Soshogbo	struct hostent *hp;
473296047Soshogbo	int family;
474296047Soshogbo
475296047Soshogbo	if (!dns_allowed_type(limits, "NAME"))
476296047Soshogbo		return (NO_RECOVERY);
477296047Soshogbo
478296047Soshogbo	family = (int)nvlist_get_number(nvlin, "family");
479296047Soshogbo
480296047Soshogbo	if (!dns_allowed_family(limits, family))
481296047Soshogbo		return (NO_RECOVERY);
482296047Soshogbo
483296047Soshogbo	hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
484296047Soshogbo	if (hp == NULL)
485296047Soshogbo		return (h_errno);
486296047Soshogbo	hostent_pack(hp, nvlout);
487296047Soshogbo	return (0);
488296047Soshogbo}
489296047Soshogbo
490296047Soshogbostatic int
491296047Soshogbodns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
492296047Soshogbo    nvlist_t *nvlout)
493296047Soshogbo{
494296047Soshogbo	struct hostent *hp;
495296047Soshogbo	const void *addr;
496296047Soshogbo	size_t addrsize;
497296047Soshogbo	int family;
498296047Soshogbo
499296047Soshogbo	if (!dns_allowed_type(limits, "ADDR"))
500296047Soshogbo		return (NO_RECOVERY);
501296047Soshogbo
502296047Soshogbo	family = (int)nvlist_get_number(nvlin, "family");
503296047Soshogbo
504296047Soshogbo	if (!dns_allowed_family(limits, family))
505296047Soshogbo		return (NO_RECOVERY);
506296047Soshogbo
507296047Soshogbo	addr = nvlist_get_binary(nvlin, "addr", &addrsize);
508296047Soshogbo	hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
509296047Soshogbo	if (hp == NULL)
510296047Soshogbo		return (h_errno);
511296047Soshogbo	hostent_pack(hp, nvlout);
512296047Soshogbo	return (0);
513296047Soshogbo}
514296047Soshogbo
515296047Soshogbostatic int
516296047Soshogbodns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
517296047Soshogbo{
518296047Soshogbo	struct sockaddr_storage sast;
519296047Soshogbo	const void *sabin;
520296047Soshogbo	char *host, *serv;
521296047Soshogbo	size_t sabinsize, hostlen, servlen;
522296047Soshogbo	socklen_t salen;
523296047Soshogbo	int error, flags;
524296047Soshogbo
525296047Soshogbo	if (!dns_allowed_type(limits, "NAME"))
526296047Soshogbo		return (NO_RECOVERY);
527296047Soshogbo
528296047Soshogbo	error = 0;
529296047Soshogbo	host = serv = NULL;
530296047Soshogbo	memset(&sast, 0, sizeof(sast));
531296047Soshogbo
532296047Soshogbo	hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
533296047Soshogbo	servlen = (size_t)nvlist_get_number(nvlin, "servlen");
534296047Soshogbo
535296047Soshogbo	if (hostlen > 0) {
536296047Soshogbo		host = calloc(1, hostlen + 1);
537296047Soshogbo		if (host == NULL) {
538296047Soshogbo			error = EAI_MEMORY;
539296047Soshogbo			goto out;
540296047Soshogbo		}
541296047Soshogbo	}
542296047Soshogbo	if (servlen > 0) {
543296047Soshogbo		serv = calloc(1, servlen + 1);
544296047Soshogbo		if (serv == NULL) {
545296047Soshogbo			error = EAI_MEMORY;
546296047Soshogbo			goto out;
547296047Soshogbo		}
548296047Soshogbo	}
549296047Soshogbo
550296047Soshogbo	sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
551296047Soshogbo	if (sabinsize > sizeof(sast)) {
552296047Soshogbo		error = EAI_FAIL;
553296047Soshogbo		goto out;
554296047Soshogbo	}
555296047Soshogbo
556296047Soshogbo	memcpy(&sast, sabin, sabinsize);
557296047Soshogbo	salen = (socklen_t)sabinsize;
558296047Soshogbo
559296047Soshogbo	if ((sast.ss_family != AF_INET ||
560296047Soshogbo	     salen != sizeof(struct sockaddr_in)) &&
561296047Soshogbo	    (sast.ss_family != AF_INET6 ||
562296047Soshogbo	     salen != sizeof(struct sockaddr_in6))) {
563296047Soshogbo		error = EAI_FAIL;
564296047Soshogbo		goto out;
565296047Soshogbo	}
566296047Soshogbo
567296047Soshogbo	if (!dns_allowed_family(limits, (int)sast.ss_family)) {
568296047Soshogbo		error = NO_RECOVERY;
569296047Soshogbo		goto out;
570296047Soshogbo	}
571296047Soshogbo
572296047Soshogbo	flags = (int)nvlist_get_number(nvlin, "flags");
573296047Soshogbo
574296047Soshogbo	error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
575296047Soshogbo	    serv, servlen, flags);
576296047Soshogbo	if (error != 0)
577296047Soshogbo		goto out;
578296047Soshogbo
579296047Soshogbo	if (host != NULL)
580296047Soshogbo		nvlist_move_string(nvlout, "host", host);
581296047Soshogbo	if (serv != NULL)
582296047Soshogbo		nvlist_move_string(nvlout, "serv", serv);
583296047Soshogboout:
584296047Soshogbo	if (error != 0) {
585296047Soshogbo		free(host);
586296047Soshogbo		free(serv);
587296047Soshogbo	}
588296047Soshogbo	return (error);
589296047Soshogbo}
590296047Soshogbo
591296047Soshogbostatic nvlist_t *
592296047Soshogboaddrinfo_pack(const struct addrinfo *ai)
593296047Soshogbo{
594296047Soshogbo	nvlist_t *nvl;
595296047Soshogbo
596296047Soshogbo	nvl = nvlist_create(0);
597296047Soshogbo	nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
598296047Soshogbo	nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
599296047Soshogbo	nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
600296047Soshogbo	nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
601296047Soshogbo	nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
602296047Soshogbo	if (ai->ai_canonname != NULL)
603296047Soshogbo		nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
604296047Soshogbo
605296047Soshogbo	return (nvl);
606296047Soshogbo}
607296047Soshogbo
608296047Soshogbostatic int
609296047Soshogbodns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
610296047Soshogbo{
611296047Soshogbo	struct addrinfo hints, *hintsp, *res, *cur;
612296047Soshogbo	const char *hostname, *servname;
613296047Soshogbo	char nvlname[64];
614296047Soshogbo	nvlist_t *elem;
615296047Soshogbo	unsigned int ii;
616296047Soshogbo	int error, family, n;
617296047Soshogbo
618296047Soshogbo	if (!dns_allowed_type(limits, "ADDR"))
619296047Soshogbo		return (NO_RECOVERY);
620296047Soshogbo
621296047Soshogbo	hostname = dnvlist_get_string(nvlin, "hostname", NULL);
622296047Soshogbo	servname = dnvlist_get_string(nvlin, "servname", NULL);
623296047Soshogbo	if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
624296047Soshogbo		hints.ai_flags = (int)nvlist_get_number(nvlin,
625296047Soshogbo		    "hints.ai_flags");
626296047Soshogbo		hints.ai_family = (int)nvlist_get_number(nvlin,
627296047Soshogbo		    "hints.ai_family");
628296047Soshogbo		hints.ai_socktype = (int)nvlist_get_number(nvlin,
629296047Soshogbo		    "hints.ai_socktype");
630296047Soshogbo		hints.ai_protocol = (int)nvlist_get_number(nvlin,
631296047Soshogbo		    "hints.ai_protocol");
632296047Soshogbo		hints.ai_addrlen = 0;
633296047Soshogbo		hints.ai_addr = NULL;
634296047Soshogbo		hints.ai_canonname = NULL;
635297982Soshogbo		hints.ai_next = NULL;
636296047Soshogbo		hintsp = &hints;
637296047Soshogbo		family = hints.ai_family;
638296047Soshogbo	} else {
639296047Soshogbo		hintsp = NULL;
640296047Soshogbo		family = AF_UNSPEC;
641296047Soshogbo	}
642296047Soshogbo
643296047Soshogbo	if (!dns_allowed_family(limits, family))
644296047Soshogbo		return (NO_RECOVERY);
645296047Soshogbo
646296047Soshogbo	error = getaddrinfo(hostname, servname, hintsp, &res);
647296047Soshogbo	if (error != 0)
648296047Soshogbo		goto out;
649296047Soshogbo
650296047Soshogbo	for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
651296047Soshogbo		elem = addrinfo_pack(cur);
652296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
653296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
654296047Soshogbo		nvlist_move_nvlist(nvlout, nvlname, elem);
655296047Soshogbo	}
656296047Soshogbo
657296047Soshogbo	freeaddrinfo(res);
658296047Soshogbo	error = 0;
659296047Soshogboout:
660296047Soshogbo	return (error);
661296047Soshogbo}
662296047Soshogbo
663296047Soshogbostatic bool
664296047Soshogbolimit_has_entry(const nvlist_t *limits, const char *prefix)
665296047Soshogbo{
666296047Soshogbo	const char *name;
667296047Soshogbo	size_t prefixlen;
668296047Soshogbo	void *cookie;
669296047Soshogbo
670296047Soshogbo	if (limits == NULL)
671296047Soshogbo		return (false);
672296047Soshogbo
673296047Soshogbo	prefixlen = strlen(prefix);
674296047Soshogbo
675296047Soshogbo	cookie = NULL;
676296047Soshogbo	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
677296047Soshogbo		if (strncmp(name, prefix, prefixlen) == 0)
678296047Soshogbo			return (true);
679296047Soshogbo	}
680296047Soshogbo
681296047Soshogbo	return (false);
682296047Soshogbo}
683296047Soshogbo
684296047Soshogbostatic int
685296047Soshogbodns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
686296047Soshogbo{
687296047Soshogbo	const char *name;
688296047Soshogbo	void *cookie;
689296047Soshogbo	int nvtype;
690296047Soshogbo	bool hastype, hasfamily;
691296047Soshogbo
692296047Soshogbo	hastype = false;
693296047Soshogbo	hasfamily = false;
694296047Soshogbo
695296047Soshogbo	cookie = NULL;
696296047Soshogbo	while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
697296047Soshogbo		if (nvtype == NV_TYPE_STRING) {
698296047Soshogbo			const char *type;
699296047Soshogbo
700296047Soshogbo			if (strncmp(name, "type", sizeof("type") - 1) != 0)
701296047Soshogbo				return (EINVAL);
702296047Soshogbo			type = nvlist_get_string(newlimits, name);
703296047Soshogbo			if (strcmp(type, "ADDR") != 0 &&
704296047Soshogbo			    strcmp(type, "NAME") != 0) {
705296047Soshogbo				return (EINVAL);
706296047Soshogbo			}
707296047Soshogbo			if (!dns_allowed_type(oldlimits, type))
708296047Soshogbo				return (ENOTCAPABLE);
709296047Soshogbo			hastype = true;
710296047Soshogbo		} else if (nvtype == NV_TYPE_NUMBER) {
711296047Soshogbo			int family;
712296047Soshogbo
713296047Soshogbo			if (strncmp(name, "family", sizeof("family") - 1) != 0)
714296047Soshogbo				return (EINVAL);
715296047Soshogbo			family = (int)nvlist_get_number(newlimits, name);
716296047Soshogbo			if (!dns_allowed_family(oldlimits, family))
717296047Soshogbo				return (ENOTCAPABLE);
718296047Soshogbo			hasfamily = true;
719296047Soshogbo		} else {
720296047Soshogbo			return (EINVAL);
721296047Soshogbo		}
722296047Soshogbo	}
723296047Soshogbo
724296047Soshogbo	/*
725296047Soshogbo	 * If the new limit doesn't mention type or family we have to
726296047Soshogbo	 * check if the current limit does have those. Missing type or
727296047Soshogbo	 * family in the limit means that all types or families are
728296047Soshogbo	 * allowed.
729296047Soshogbo	 */
730296047Soshogbo	if (!hastype) {
731296047Soshogbo		if (limit_has_entry(oldlimits, "type"))
732296047Soshogbo			return (ENOTCAPABLE);
733296047Soshogbo	}
734296047Soshogbo	if (!hasfamily) {
735296047Soshogbo		if (limit_has_entry(oldlimits, "family"))
736296047Soshogbo			return (ENOTCAPABLE);
737296047Soshogbo	}
738296047Soshogbo
739296047Soshogbo	return (0);
740296047Soshogbo}
741296047Soshogbo
742296047Soshogbostatic int
743296047Soshogbodns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
744296047Soshogbo    nvlist_t *nvlout)
745296047Soshogbo{
746296047Soshogbo	int error;
747296047Soshogbo
748296047Soshogbo	if (strcmp(cmd, "gethostbyname") == 0)
749296047Soshogbo		error = dns_gethostbyname(limits, nvlin, nvlout);
750296047Soshogbo	else if (strcmp(cmd, "gethostbyaddr") == 0)
751296047Soshogbo		error = dns_gethostbyaddr(limits, nvlin, nvlout);
752296047Soshogbo	else if (strcmp(cmd, "getnameinfo") == 0)
753296047Soshogbo		error = dns_getnameinfo(limits, nvlin, nvlout);
754296047Soshogbo	else if (strcmp(cmd, "getaddrinfo") == 0)
755296047Soshogbo		error = dns_getaddrinfo(limits, nvlin, nvlout);
756296047Soshogbo	else
757296047Soshogbo		error = NO_RECOVERY;
758296047Soshogbo
759296047Soshogbo	return (error);
760296047Soshogbo}
761296047Soshogbo
762301572SoshogboCREATE_SERVICE("system.dns", dns_limit, dns_command, 0);
763