cap_dns.c revision 301572
155714Skris/*-
255714Skris * Copyright (c) 2012-2013 The FreeBSD Foundation
355714Skris * All rights reserved.
455714Skris *
555714Skris * This software was developed by Pawel Jakub Dawidek under sponsorship from
655714Skris * the FreeBSD Foundation.
755714Skris *
855714Skris * Redistribution and use in source and binary forms, with or without
955714Skris * modification, are permitted provided that the following conditions
1055714Skris * are met:
1155714Skris * 1. Redistributions of source code must retain the above copyright
1255714Skris *    notice, this list of conditions and the following disclaimer.
1355714Skris * 2. Redistributions in binary form must reproduce the above copyright
1455714Skris *    notice, this list of conditions and the following disclaimer in the
1555714Skris *    documentation and/or other materials provided with the distribution.
1655714Skris *
1755714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1855714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1955714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2055714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2155714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2255714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2355714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2455714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2555714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2655714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2755714Skris * SUCH DAMAGE.
2855714Skris */
2955714Skris
3055714Skris#include <sys/cdefs.h>
3155714Skris__FBSDID("$FreeBSD: head/lib/libcasper/services/cap_dns/cap_dns.c 301572 2016-06-08 02:03:53Z oshogbo $");
3255714Skris
3355714Skris#include <sys/dnv.h>
3455714Skris#include <sys/nv.h>
3555714Skris#include <netinet/in.h>
3655714Skris
3755714Skris#include <assert.h>
3855714Skris#include <errno.h>
3955714Skris#include <netdb.h>
4055714Skris#include <stdlib.h>
4155714Skris#include <string.h>
4255714Skris#include <unistd.h>
4355714Skris
4455714Skris#include <libcasper.h>
4555714Skris#include <libcasper_service.h>
4655714Skris
4755714Skris#include "cap_dns.h"
4855714Skris
4955714Skrisstatic struct hostent hent;
5055714Skris
5155714Skrisstatic void
5255714Skrishostent_free(struct hostent *hp)
5355714Skris{
5455714Skris	unsigned int ii;
5555714Skris
5655714Skris	free(hp->h_name);
5755714Skris	hp->h_name = NULL;
5855714Skris	if (hp->h_aliases != NULL) {
5955714Skris		for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
6055714Skris			free(hp->h_aliases[ii]);
6168651Skris		free(hp->h_aliases);
6268651Skris		hp->h_aliases = NULL;
6355714Skris	}
6455714Skris	if (hp->h_addr_list != NULL) {
6555714Skris		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
6659191Skris			free(hp->h_addr_list[ii]);
6755714Skris		free(hp->h_addr_list);
6855714Skris		hp->h_addr_list = NULL;
6959191Skris	}
7055714Skris}
7159191Skris
7259191Skrisstatic struct hostent *
7355714Skrishostent_unpack(const nvlist_t *nvl, struct hostent *hp)
7455714Skris{
7555714Skris	unsigned int ii, nitems;
7655714Skris	char nvlname[64];
7759191Skris	int n;
7859191Skris
7959191Skris	hostent_free(hp);
8055714Skris
8159191Skris	hp->h_name = strdup(nvlist_get_string(nvl, "name"));
8259191Skris	if (hp->h_name == NULL)
8359191Skris		goto fail;
8455714Skris	hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
8555714Skris	hp->h_length = (int)nvlist_get_number(nvl, "length");
8655714Skris
8755714Skris	nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
8855714Skris	hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
8955714Skris	if (hp->h_aliases == NULL)
9055714Skris		goto fail;
9155714Skris	for (ii = 0; ii < nitems; ii++) {
9255714Skris		n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
9355714Skris		assert(n > 0 && n < (int)sizeof(nvlname));
9455714Skris		hp->h_aliases[ii] =
9555714Skris		    strdup(nvlist_get_string(nvl, nvlname));
9655714Skris		if (hp->h_aliases[ii] == NULL)
9755714Skris			goto fail;
9855714Skris	}
9955714Skris	hp->h_aliases[ii] = NULL;
10059191Skris
10159191Skris	nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
10255714Skris	hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
10355714Skris	if (hp->h_addr_list == NULL)
10455714Skris		goto fail;
10559191Skris	for (ii = 0; ii < nitems; ii++) {
10659191Skris		hp->h_addr_list[ii] = malloc(hp->h_length);
10759191Skris		if (hp->h_addr_list[ii] == NULL)
10859191Skris			goto fail;
10959191Skris		n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
11059191Skris		assert(n > 0 && n < (int)sizeof(nvlname));
11155714Skris		bcopy(nvlist_get_binary(nvl, nvlname, NULL),
11255714Skris		    hp->h_addr_list[ii], hp->h_length);
11355714Skris	}
11455714Skris	hp->h_addr_list[ii] = NULL;
11555714Skris
11655714Skris	return (hp);
11755714Skrisfail:
11855714Skris	hostent_free(hp);
11955714Skris	h_errno = NO_RECOVERY;
12055714Skris	return (NULL);
12155714Skris}
12272613Skris
12355714Skrisstruct hostent *
12472613Skriscap_gethostbyname(cap_channel_t *chan, const char *name)
12555714Skris{
12655714Skris
12755714Skris	return (cap_gethostbyname2(chan, name, AF_INET));
12855714Skris}
12955714Skris
13055714Skrisstruct hostent *
13155714Skriscap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
13255714Skris{
13355714Skris	struct hostent *hp;
13455714Skris	nvlist_t *nvl;
13555714Skris
13655714Skris	nvl = nvlist_create(0);
13755714Skris	nvlist_add_string(nvl, "cmd", "gethostbyname");
13855714Skris	nvlist_add_number(nvl, "family", (uint64_t)type);
13955714Skris	nvlist_add_string(nvl, "name", name);
14055714Skris	nvl = cap_xfer_nvlist(chan, nvl, 0);
14155714Skris	if (nvl == NULL) {
14255714Skris		h_errno = NO_RECOVERY;
14355714Skris		return (NULL);
14455714Skris	}
14555714Skris	if (nvlist_get_number(nvl, "error") != 0) {
14655714Skris		h_errno = (int)nvlist_get_number(nvl, "error");
14755714Skris		nvlist_destroy(nvl);
14855714Skris		return (NULL);
14955714Skris	}
15055714Skris
15155714Skris	hp = hostent_unpack(nvl, &hent);
15255714Skris	nvlist_destroy(nvl);
15355714Skris	return (hp);
15455714Skris}
15555714Skris
15655714Skrisstruct hostent *
15755714Skriscap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
15855714Skris    int type)
15955714Skris{
16055714Skris	struct hostent *hp;
16155714Skris	nvlist_t *nvl;
16255714Skris
16355714Skris	nvl = nvlist_create(0);
16455714Skris	nvlist_add_string(nvl, "cmd", "gethostbyaddr");
16555714Skris	nvlist_add_binary(nvl, "addr", addr, (size_t)len);
16655714Skris	nvlist_add_number(nvl, "family", (uint64_t)type);
16755714Skris	nvl = cap_xfer_nvlist(chan, nvl, 0);
16855714Skris	if (nvl == NULL) {
16955714Skris		h_errno = NO_RECOVERY;
17055714Skris		return (NULL);
17155714Skris	}
17255714Skris	if (nvlist_get_number(nvl, "error") != 0) {
17355714Skris		h_errno = (int)nvlist_get_number(nvl, "error");
17455714Skris		nvlist_destroy(nvl);
17555714Skris		return (NULL);
17655714Skris	}
17755714Skris	hp = hostent_unpack(nvl, &hent);
17855714Skris	nvlist_destroy(nvl);
17955714Skris	return (hp);
18055714Skris}
18155714Skris
18255714Skrisstatic struct addrinfo *
18355714Skrisaddrinfo_unpack(const nvlist_t *nvl)
18455714Skris{
18555714Skris	struct addrinfo *ai;
18655714Skris	const void *addr;
18755714Skris	size_t addrlen;
18855714Skris	const char *canonname;
18955714Skris
19068651Skris	addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
19155714Skris	ai = malloc(sizeof(*ai) + addrlen);
19255714Skris	if (ai == NULL)
19355714Skris		return (NULL);
19455714Skris	ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
19555714Skris	ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
19655714Skris	ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
19755714Skris	ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
19855714Skris	ai->ai_addrlen = (socklen_t)addrlen;
19955714Skris	canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
20055714Skris	if (canonname != NULL) {
20155714Skris		ai->ai_canonname = strdup(canonname);
20255714Skris		if (ai->ai_canonname == NULL) {
20355714Skris			free(ai);
20455714Skris			return (NULL);
20555714Skris		}
20655714Skris	} else {
20755714Skris		ai->ai_canonname = NULL;
20855714Skris	}
20955714Skris	ai->ai_addr = (void *)(ai + 1);
21055714Skris	bcopy(addr, ai->ai_addr, addrlen);
21155714Skris	ai->ai_next = NULL;
21255714Skris
21355714Skris	return (ai);
21455714Skris}
21555714Skris
21655714Skrisint
21759191Skriscap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
21859191Skris    const struct addrinfo *hints, struct addrinfo **res)
21955714Skris{
22055714Skris	struct addrinfo *firstai, *prevai, *curai;
22155714Skris	unsigned int ii;
22255714Skris	const nvlist_t *nvlai;
22355714Skris	char nvlname[64];
22455714Skris	nvlist_t *nvl;
22555714Skris	int error, n;
22655714Skris
22755714Skris	nvl = nvlist_create(0);
22855714Skris	nvlist_add_string(nvl, "cmd", "getaddrinfo");
22955714Skris	if (hostname != NULL)
23055714Skris		nvlist_add_string(nvl, "hostname", hostname);
23155714Skris	if (servname != NULL)
23255714Skris		nvlist_add_string(nvl, "servname", servname);
23355714Skris	if (hints != NULL) {
23472613Skris		nvlist_add_number(nvl, "hints.ai_flags",
23555714Skris		    (uint64_t)hints->ai_flags);
23655714Skris		nvlist_add_number(nvl, "hints.ai_family",
23759191Skris		    (uint64_t)hints->ai_family);
23855714Skris		nvlist_add_number(nvl, "hints.ai_socktype",
23955714Skris		    (uint64_t)hints->ai_socktype);
24055714Skris		nvlist_add_number(nvl, "hints.ai_protocol",
24155714Skris		    (uint64_t)hints->ai_protocol);
24255714Skris	}
24355714Skris	nvl = cap_xfer_nvlist(chan, nvl, 0);
24455714Skris	if (nvl == NULL)
24555714Skris		return (EAI_MEMORY);
24655714Skris	if (nvlist_get_number(nvl, "error") != 0) {
24768651Skris		error = (int)nvlist_get_number(nvl, "error");
24855714Skris		nvlist_destroy(nvl);
24955714Skris		return (error);
25055714Skris	}
25155714Skris
25255714Skris	nvlai = NULL;
25355714Skris	firstai = prevai = curai = NULL;
25455714Skris	for (ii = 0; ; ii++) {
25555714Skris		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
25655714Skris		assert(n > 0 && n < (int)sizeof(nvlname));
25755714Skris		if (!nvlist_exists_nvlist(nvl, nvlname))
25855714Skris			break;
25955714Skris		nvlai = nvlist_get_nvlist(nvl, nvlname);
26055714Skris		curai = addrinfo_unpack(nvlai);
26155714Skris		if (curai == NULL)
26255714Skris			break;
26355714Skris		if (prevai != NULL)
26455714Skris			prevai->ai_next = curai;
26555714Skris		else if (firstai == NULL)
26655714Skris			firstai = curai;
26755714Skris		prevai = curai;
26855714Skris	}
26955714Skris	nvlist_destroy(nvl);
27055714Skris	if (curai == NULL && nvlai != NULL) {
27155714Skris		if (firstai == NULL)
27255714Skris			freeaddrinfo(firstai);
27355714Skris		return (EAI_MEMORY);
27455714Skris	}
27555714Skris
27655714Skris	*res = firstai;
27755714Skris	return (0);
27855714Skris}
27955714Skris
28055714Skrisint
28159191Skriscap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
28259191Skris    char *host, size_t hostlen, char *serv, size_t servlen, int flags)
28359191Skris{
28459191Skris	nvlist_t *nvl;
28559191Skris	int error;
28659191Skris
28759191Skris	nvl = nvlist_create(0);
28859191Skris	nvlist_add_string(nvl, "cmd", "getnameinfo");
28959191Skris	nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
29059191Skris	nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
29159191Skris	nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
29259191Skris	nvlist_add_number(nvl, "flags", (uint64_t)flags);
29359191Skris	nvl = cap_xfer_nvlist(chan, nvl, 0);
29459191Skris	if (nvl == NULL)
29559191Skris		return (EAI_MEMORY);
29659191Skris	if (nvlist_get_number(nvl, "error") != 0) {
29759191Skris		error = (int)nvlist_get_number(nvl, "error");
29859191Skris		nvlist_destroy(nvl);
29959191Skris		return (error);
30059191Skris	}
30159191Skris
30259191Skris	if (host != NULL && nvlist_exists_string(nvl, "host"))
30359191Skris		strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
30459191Skris	if (serv != NULL && nvlist_exists_string(nvl, "serv"))
30559191Skris		strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
30659191Skris	nvlist_destroy(nvl);
30759191Skris	return (0);
30859191Skris}
30959191Skris
31059191Skrisstatic void
31159191Skrislimit_remove(nvlist_t *limits, const char *prefix)
31259191Skris{
31359191Skris	const char *name;
31459191Skris	size_t prefixlen;
31559191Skris	void *cookie;
31659191Skris
31759191Skris	prefixlen = strlen(prefix);
31859191Skrisagain:
31959191Skris	cookie = NULL;
32059191Skris	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
32155714Skris		if (strncmp(name, prefix, prefixlen) == 0) {
32255714Skris			nvlist_free(limits, name);
32355714Skris			goto again;
32455714Skris		}
32555714Skris	}
32655714Skris}
32755714Skris
32855714Skrisint
32955714Skriscap_dns_type_limit(cap_channel_t *chan, const char * const *types,
33055714Skris    size_t ntypes)
33155714Skris{
33255714Skris	nvlist_t *limits;
33355714Skris	unsigned int i;
33455714Skris	char nvlname[64];
33555714Skris	int n;
33655714Skris
33755714Skris	if (cap_limit_get(chan, &limits) < 0)
33855714Skris		return (-1);
33955714Skris	if (limits == NULL)
34055714Skris		limits = nvlist_create(0);
34155714Skris	else
34255714Skris		limit_remove(limits, "type");
34355714Skris	for (i = 0; i < ntypes; i++) {
34455714Skris		n = snprintf(nvlname, sizeof(nvlname), "type%u", i);
34555714Skris		assert(n > 0 && n < (int)sizeof(nvlname));
34655714Skris		nvlist_add_string(limits, nvlname, types[i]);
34755714Skris	}
34855714Skris	return (cap_limit_set(chan, limits));
34955714Skris}
35055714Skris
35155714Skrisint
35255714Skriscap_dns_family_limit(cap_channel_t *chan, const int *families,
35355714Skris    size_t nfamilies)
35455714Skris{
35555714Skris	nvlist_t *limits;
35655714Skris	unsigned int i;
35755714Skris	char nvlname[64];
35855714Skris	int n;
35955714Skris
36055714Skris	if (cap_limit_get(chan, &limits) < 0)
36155714Skris		return (-1);
36255714Skris	if (limits == NULL)
36355714Skris		limits = nvlist_create(0);
36455714Skris	else
36555714Skris		limit_remove(limits, "family");
36655714Skris	for (i = 0; i < nfamilies; i++) {
36755714Skris		n = snprintf(nvlname, sizeof(nvlname), "family%u", i);
36855714Skris		assert(n > 0 && n < (int)sizeof(nvlname));
36955714Skris		nvlist_add_number(limits, nvlname, (uint64_t)families[i]);
37055714Skris	}
37155714Skris	return (cap_limit_set(chan, limits));
37255714Skris}
37355714Skris
37455714Skris/*
37555714Skris * Service functions.
37655714Skris */
37755714Skrisstatic bool
37855714Skrisdns_allowed_type(const nvlist_t *limits, const char *type)
37955714Skris{
38055714Skris	const char *name;
38155714Skris	bool notypes;
38255714Skris	void *cookie;
38368651Skris
38455714Skris	if (limits == NULL)
38555714Skris		return (true);
38655714Skris
38755714Skris	notypes = true;
38855714Skris	cookie = NULL;
38955714Skris	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
39055714Skris		if (strncmp(name, "type", sizeof("type") - 1) != 0)
39155714Skris			continue;
39255714Skris		notypes = false;
39355714Skris		if (strcmp(nvlist_get_string(limits, name), type) == 0)
39455714Skris			return (true);
39555714Skris	}
39655714Skris
39755714Skris	/* If there are no types at all, allow any type. */
39855714Skris	if (notypes)
39955714Skris		return (true);
40055714Skris
40155714Skris	return (false);
40255714Skris}
40355714Skris
40455714Skrisstatic bool
40555714Skrisdns_allowed_family(const nvlist_t *limits, int family)
40655714Skris{
40755714Skris	const char *name;
40855714Skris	bool nofamilies;
40955714Skris	void *cookie;
41055714Skris
41155714Skris	if (limits == NULL)
41255714Skris		return (true);
41355714Skris
41455714Skris	nofamilies = true;
41555714Skris	cookie = NULL;
41655714Skris	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
41755714Skris		if (strncmp(name, "family", sizeof("family") - 1) != 0)
41855714Skris			continue;
41955714Skris		nofamilies = false;
42055714Skris		if (family == AF_UNSPEC)
42155714Skris			continue;
42255714Skris		if (nvlist_get_number(limits, name) == (uint64_t)family)
42355714Skris			return (true);
42455714Skris	}
42555714Skris
42655714Skris	/* If there are no families at all, allow any family. */
42755714Skris	if (nofamilies)
42855714Skris		return (true);
42955714Skris
43055714Skris	return (false);
43155714Skris}
43255714Skris
43355714Skrisstatic void
43455714Skrishostent_pack(const struct hostent *hp, nvlist_t *nvl)
43555714Skris{
43655714Skris	unsigned int ii;
43755714Skris	char nvlname[64];
43855714Skris	int n;
43955714Skris
44055714Skris	nvlist_add_string(nvl, "name", hp->h_name);
44155714Skris	nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
44255714Skris	nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
44355714Skris
44455714Skris	if (hp->h_aliases == NULL) {
44555714Skris		nvlist_add_number(nvl, "naliases", 0);
44655714Skris	} else {
44755714Skris		for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
44855714Skris			n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
44955714Skris			assert(n > 0 && n < (int)sizeof(nvlname));
45055714Skris			nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
45155714Skris		}
45255714Skris		nvlist_add_number(nvl, "naliases", (uint64_t)ii);
45355714Skris	}
45455714Skris
45555714Skris	if (hp->h_addr_list == NULL) {
45655714Skris		nvlist_add_number(nvl, "naddrs", 0);
45755714Skris	} else {
45855714Skris		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
45955714Skris			n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
46055714Skris			assert(n > 0 && n < (int)sizeof(nvlname));
46155714Skris			nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
46255714Skris			    (size_t)hp->h_length);
46355714Skris		}
46455714Skris		nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
46555714Skris	}
46655714Skris}
46755714Skris
46855714Skrisstatic int
46955714Skrisdns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
47055714Skris    nvlist_t *nvlout)
47155714Skris{
47255714Skris	struct hostent *hp;
47355714Skris	int family;
47455714Skris
47555714Skris	if (!dns_allowed_type(limits, "NAME"))
47655714Skris		return (NO_RECOVERY);
47755714Skris
47855714Skris	family = (int)nvlist_get_number(nvlin, "family");
47955714Skris
48055714Skris	if (!dns_allowed_family(limits, family))
48155714Skris		return (NO_RECOVERY);
48255714Skris
48355714Skris	hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
48455714Skris	if (hp == NULL)
48555714Skris		return (h_errno);
48655714Skris	hostent_pack(hp, nvlout);
48755714Skris	return (0);
48855714Skris}
48955714Skris
49055714Skrisstatic int
49155714Skrisdns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
49259191Skris    nvlist_t *nvlout)
49359191Skris{
49459191Skris	struct hostent *hp;
49559191Skris	const void *addr;
49659191Skris	size_t addrsize;
49759191Skris	int family;
49859191Skris
49959191Skris	if (!dns_allowed_type(limits, "ADDR"))
50059191Skris		return (NO_RECOVERY);
50159191Skris
50259191Skris	family = (int)nvlist_get_number(nvlin, "family");
50359191Skris
50459191Skris	if (!dns_allowed_family(limits, family))
50559191Skris		return (NO_RECOVERY);
50659191Skris
50759191Skris	addr = nvlist_get_binary(nvlin, "addr", &addrsize);
50859191Skris	hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
50959191Skris	if (hp == NULL)
51059191Skris		return (h_errno);
51159191Skris	hostent_pack(hp, nvlout);
51259191Skris	return (0);
51359191Skris}
51459191Skris
51559191Skrisstatic int
51659191Skrisdns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
51759191Skris{
51859191Skris	struct sockaddr_storage sast;
51959191Skris	const void *sabin;
52059191Skris	char *host, *serv;
52159191Skris	size_t sabinsize, hostlen, servlen;
52259191Skris	socklen_t salen;
52359191Skris	int error, flags;
52455714Skris
52555714Skris	if (!dns_allowed_type(limits, "NAME"))
52655714Skris		return (NO_RECOVERY);
52755714Skris
52855714Skris	error = 0;
52955714Skris	host = serv = NULL;
53055714Skris	memset(&sast, 0, sizeof(sast));
53155714Skris
53255714Skris	hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
53355714Skris	servlen = (size_t)nvlist_get_number(nvlin, "servlen");
53455714Skris
53555714Skris	if (hostlen > 0) {
53655714Skris		host = calloc(1, hostlen + 1);
53755714Skris		if (host == NULL) {
53855714Skris			error = EAI_MEMORY;
53955714Skris			goto out;
54055714Skris		}
54155714Skris	}
54255714Skris	if (servlen > 0) {
54355714Skris		serv = calloc(1, servlen + 1);
54455714Skris		if (serv == NULL) {
54555714Skris			error = EAI_MEMORY;
54655714Skris			goto out;
54755714Skris		}
54855714Skris	}
54955714Skris
55055714Skris	sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
55155714Skris	if (sabinsize > sizeof(sast)) {
55255714Skris		error = EAI_FAIL;
55355714Skris		goto out;
55455714Skris	}
55555714Skris
55655714Skris	memcpy(&sast, sabin, sabinsize);
55755714Skris	salen = (socklen_t)sabinsize;
55855714Skris
55955714Skris	if ((sast.ss_family != AF_INET ||
56055714Skris	     salen != sizeof(struct sockaddr_in)) &&
56155714Skris	    (sast.ss_family != AF_INET6 ||
56255714Skris	     salen != sizeof(struct sockaddr_in6))) {
56355714Skris		error = EAI_FAIL;
56455714Skris		goto out;
56555714Skris	}
56655714Skris
56755714Skris	if (!dns_allowed_family(limits, (int)sast.ss_family)) {
56855714Skris		error = NO_RECOVERY;
56955714Skris		goto out;
57055714Skris	}
57155714Skris
57255714Skris	flags = (int)nvlist_get_number(nvlin, "flags");
57355714Skris
57455714Skris	error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
57555714Skris	    serv, servlen, flags);
57655714Skris	if (error != 0)
57755714Skris		goto out;
57855714Skris
57955714Skris	if (host != NULL)
58055714Skris		nvlist_move_string(nvlout, "host", host);
58155714Skris	if (serv != NULL)
58255714Skris		nvlist_move_string(nvlout, "serv", serv);
58355714Skrisout:
58455714Skris	if (error != 0) {
58555714Skris		free(host);
58655714Skris		free(serv);
58755714Skris	}
58855714Skris	return (error);
58955714Skris}
59055714Skris
59155714Skrisstatic nvlist_t *
59255714Skrisaddrinfo_pack(const struct addrinfo *ai)
59355714Skris{
59455714Skris	nvlist_t *nvl;
59555714Skris
59655714Skris	nvl = nvlist_create(0);
59755714Skris	nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
59855714Skris	nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
59955714Skris	nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
60055714Skris	nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
60155714Skris	nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
60255714Skris	if (ai->ai_canonname != NULL)
60355714Skris		nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
60455714Skris
60555714Skris	return (nvl);
60655714Skris}
60759191Skris
60859191Skrisstatic int
60959191Skrisdns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
61055714Skris{
61155714Skris	struct addrinfo hints, *hintsp, *res, *cur;
61255714Skris	const char *hostname, *servname;
61355714Skris	char nvlname[64];
61455714Skris	nvlist_t *elem;
61555714Skris	unsigned int ii;
61655714Skris	int error, family, n;
61755714Skris
61855714Skris	if (!dns_allowed_type(limits, "ADDR"))
61955714Skris		return (NO_RECOVERY);
62055714Skris
62155714Skris	hostname = dnvlist_get_string(nvlin, "hostname", NULL);
62255714Skris	servname = dnvlist_get_string(nvlin, "servname", NULL);
62355714Skris	if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
62455714Skris		hints.ai_flags = (int)nvlist_get_number(nvlin,
62555714Skris		    "hints.ai_flags");
62655714Skris		hints.ai_family = (int)nvlist_get_number(nvlin,
62755714Skris		    "hints.ai_family");
62855714Skris		hints.ai_socktype = (int)nvlist_get_number(nvlin,
62955714Skris		    "hints.ai_socktype");
63055714Skris		hints.ai_protocol = (int)nvlist_get_number(nvlin,
63155714Skris		    "hints.ai_protocol");
63255714Skris		hints.ai_addrlen = 0;
63355714Skris		hints.ai_addr = NULL;
63455714Skris		hints.ai_canonname = NULL;
63555714Skris		hints.ai_next = NULL;
63655714Skris		hintsp = &hints;
63755714Skris		family = hints.ai_family;
63855714Skris	} else {
63955714Skris		hintsp = NULL;
64055714Skris		family = AF_UNSPEC;
64155714Skris	}
64255714Skris
64355714Skris	if (!dns_allowed_family(limits, family))
64455714Skris		return (NO_RECOVERY);
64555714Skris
64655714Skris	error = getaddrinfo(hostname, servname, hintsp, &res);
64755714Skris	if (error != 0)
64855714Skris		goto out;
64955714Skris
65055714Skris	for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
65155714Skris		elem = addrinfo_pack(cur);
65255714Skris		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
65355714Skris		assert(n > 0 && n < (int)sizeof(nvlname));
65455714Skris		nvlist_move_nvlist(nvlout, nvlname, elem);
65555714Skris	}
65655714Skris
65755714Skris	freeaddrinfo(res);
65855714Skris	error = 0;
65955714Skrisout:
66055714Skris	return (error);
66155714Skris}
66255714Skris
66355714Skrisstatic bool
66455714Skrislimit_has_entry(const nvlist_t *limits, const char *prefix)
66555714Skris{
66655714Skris	const char *name;
66755714Skris	size_t prefixlen;
66855714Skris	void *cookie;
66955714Skris
67055714Skris	if (limits == NULL)
67155714Skris		return (false);
67255714Skris
67355714Skris	prefixlen = strlen(prefix);
67455714Skris
67555714Skris	cookie = NULL;
67655714Skris	while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
67755714Skris		if (strncmp(name, prefix, prefixlen) == 0)
67855714Skris			return (true);
67955714Skris	}
68055714Skris
68155714Skris	return (false);
68255714Skris}
68355714Skris
68455714Skrisstatic int
68555714Skrisdns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
68655714Skris{
68755714Skris	const char *name;
68855714Skris	void *cookie;
68955714Skris	int nvtype;
69055714Skris	bool hastype, hasfamily;
69155714Skris
69255714Skris	hastype = false;
69355714Skris	hasfamily = false;
69455714Skris
69555714Skris	cookie = NULL;
69655714Skris	while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
69755714Skris		if (nvtype == NV_TYPE_STRING) {
69855714Skris			const char *type;
69955714Skris
70055714Skris			if (strncmp(name, "type", sizeof("type") - 1) != 0)
70155714Skris				return (EINVAL);
70255714Skris			type = nvlist_get_string(newlimits, name);
70355714Skris			if (strcmp(type, "ADDR") != 0 &&
70455714Skris			    strcmp(type, "NAME") != 0) {
70555714Skris				return (EINVAL);
70655714Skris			}
70755714Skris			if (!dns_allowed_type(oldlimits, type))
70855714Skris				return (ENOTCAPABLE);
70955714Skris			hastype = true;
71055714Skris		} else if (nvtype == NV_TYPE_NUMBER) {
71155714Skris			int family;
71255714Skris
71355714Skris			if (strncmp(name, "family", sizeof("family") - 1) != 0)
71455714Skris				return (EINVAL);
71555714Skris			family = (int)nvlist_get_number(newlimits, name);
71655714Skris			if (!dns_allowed_family(oldlimits, family))
71755714Skris				return (ENOTCAPABLE);
71855714Skris			hasfamily = true;
71955714Skris		} else {
72055714Skris			return (EINVAL);
72155714Skris		}
72255714Skris	}
72355714Skris
72455714Skris	/*
72555714Skris	 * If the new limit doesn't mention type or family we have to
72655714Skris	 * check if the current limit does have those. Missing type or
72755714Skris	 * family in the limit means that all types or families are
72855714Skris	 * allowed.
72955714Skris	 */
73055714Skris	if (!hastype) {
73155714Skris		if (limit_has_entry(oldlimits, "type"))
73255714Skris			return (ENOTCAPABLE);
73355714Skris	}
73455714Skris	if (!hasfamily) {
73555714Skris		if (limit_has_entry(oldlimits, "family"))
73655714Skris			return (ENOTCAPABLE);
73755714Skris	}
73855714Skris
73955714Skris	return (0);
74055714Skris}
74155714Skris
74255714Skrisstatic int
74355714Skrisdns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
74455714Skris    nvlist_t *nvlout)
74555714Skris{
74655714Skris	int error;
74755714Skris
74855714Skris	if (strcmp(cmd, "gethostbyname") == 0)
74955714Skris		error = dns_gethostbyname(limits, nvlin, nvlout);
75055714Skris	else if (strcmp(cmd, "gethostbyaddr") == 0)
75155714Skris		error = dns_gethostbyaddr(limits, nvlin, nvlout);
75255714Skris	else if (strcmp(cmd, "getnameinfo") == 0)
75355714Skris		error = dns_getnameinfo(limits, nvlin, nvlout);
75455714Skris	else if (strcmp(cmd, "getaddrinfo") == 0)
75555714Skris		error = dns_getaddrinfo(limits, nvlin, nvlout);
75655714Skris	else
75755714Skris		error = NO_RECOVERY;
75855714Skris
75955714Skris	return (error);
76055714Skris}
76155714Skris
76255714SkrisCREATE_SERVICE("system.dns", dns_limit, dns_command, 0);
76355714Skris