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