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