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