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