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