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); 306 if (serv != NULL && nvlist_exists_string(nvl, "serv")) 307 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen); 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, "NAME2ADDR") && 478 !dns_allowed_type(limits, "NAME")) 479 return (NO_RECOVERY); 480 481 family = (int)nvlist_get_number(nvlin, "family"); 482 483 if (!dns_allowed_family(limits, family)) 484 return (NO_RECOVERY); 485 486 hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family); 487 if (hp == NULL) 488 return (h_errno); 489 hostent_pack(hp, nvlout); 490 return (0); 491 } 492 493 static int 494 dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin, 495 nvlist_t *nvlout) 496 { 497 struct hostent *hp; 498 const void *addr; 499 size_t addrsize; 500 int family; 501 502 if (!dns_allowed_type(limits, "ADDR2NAME") && 503 !dns_allowed_type(limits, "ADDR")) 504 return (NO_RECOVERY); 505 506 family = (int)nvlist_get_number(nvlin, "family"); 507 508 if (!dns_allowed_family(limits, family)) 509 return (NO_RECOVERY); 510 511 addr = nvlist_get_binary(nvlin, "addr", &addrsize); 512 hp = gethostbyaddr(addr, (socklen_t)addrsize, family); 513 if (hp == NULL) 514 return (h_errno); 515 hostent_pack(hp, nvlout); 516 return (0); 517 } 518 519 static int 520 dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 521 { 522 struct sockaddr_storage sast; 523 const void *sabin; 524 char *host, *serv; 525 size_t sabinsize, hostlen, servlen; 526 socklen_t salen; 527 int error, flags; 528 529 if (!dns_allowed_type(limits, "ADDR2NAME") && 530 !dns_allowed_type(limits, "ADDR")) 531 return (NO_RECOVERY); 532 533 error = 0; 534 host = serv = NULL; 535 memset(&sast, 0, sizeof(sast)); 536 537 hostlen = (size_t)nvlist_get_number(nvlin, "hostlen"); 538 servlen = (size_t)nvlist_get_number(nvlin, "servlen"); 539 540 if (hostlen > 0) { 541 host = calloc(1, hostlen); 542 if (host == NULL) { 543 error = EAI_MEMORY; 544 goto out; 545 } 546 } 547 if (servlen > 0) { 548 serv = calloc(1, servlen); 549 if (serv == NULL) { 550 error = EAI_MEMORY; 551 goto out; 552 } 553 } 554 555 sabin = nvlist_get_binary(nvlin, "sa", &sabinsize); 556 if (sabinsize > sizeof(sast)) { 557 error = EAI_FAIL; 558 goto out; 559 } 560 561 memcpy(&sast, sabin, sabinsize); 562 salen = (socklen_t)sabinsize; 563 564 if ((sast.ss_family != AF_INET || 565 salen != sizeof(struct sockaddr_in)) && 566 (sast.ss_family != AF_INET6 || 567 salen != sizeof(struct sockaddr_in6))) { 568 error = EAI_FAIL; 569 goto out; 570 } 571 572 if (!dns_allowed_family(limits, (int)sast.ss_family)) { 573 error = NO_RECOVERY; 574 goto out; 575 } 576 577 flags = (int)nvlist_get_number(nvlin, "flags"); 578 579 error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen, 580 serv, servlen, flags); 581 if (error != 0) 582 goto out; 583 584 if (host != NULL) 585 nvlist_move_string(nvlout, "host", host); 586 if (serv != NULL) 587 nvlist_move_string(nvlout, "serv", serv); 588 out: 589 if (error != 0) { 590 free(host); 591 free(serv); 592 } 593 return (error); 594 } 595 596 static nvlist_t * 597 addrinfo_pack(const struct addrinfo *ai) 598 { 599 nvlist_t *nvl; 600 601 nvl = nvlist_create(0); 602 nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags); 603 nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family); 604 nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype); 605 nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol); 606 nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen); 607 if (ai->ai_canonname != NULL) 608 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname); 609 610 return (nvl); 611 } 612 613 static int 614 dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 615 { 616 struct addrinfo hints, *hintsp, *res, *cur; 617 const char *hostname, *servname; 618 char nvlname[64]; 619 nvlist_t *elem; 620 unsigned int ii; 621 int error, family, n; 622 623 if (!dns_allowed_type(limits, "NAME2ADDR") && 624 !dns_allowed_type(limits, "NAME")) 625 return (NO_RECOVERY); 626 627 hostname = dnvlist_get_string(nvlin, "hostname", NULL); 628 servname = dnvlist_get_string(nvlin, "servname", NULL); 629 if (nvlist_exists_number(nvlin, "hints.ai_flags")) { 630 hints.ai_flags = (int)nvlist_get_number(nvlin, 631 "hints.ai_flags"); 632 hints.ai_family = (int)nvlist_get_number(nvlin, 633 "hints.ai_family"); 634 hints.ai_socktype = (int)nvlist_get_number(nvlin, 635 "hints.ai_socktype"); 636 hints.ai_protocol = (int)nvlist_get_number(nvlin, 637 "hints.ai_protocol"); 638 hints.ai_addrlen = 0; 639 hints.ai_addr = NULL; 640 hints.ai_canonname = NULL; 641 hints.ai_next = NULL; 642 hintsp = &hints; 643 family = hints.ai_family; 644 } else { 645 hintsp = NULL; 646 family = AF_UNSPEC; 647 } 648 649 if (!dns_allowed_family(limits, family)) 650 return (NO_RECOVERY); 651 652 error = getaddrinfo(hostname, servname, hintsp, &res); 653 if (error != 0) 654 goto out; 655 656 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) { 657 elem = addrinfo_pack(cur); 658 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); 659 assert(n > 0 && n < (int)sizeof(nvlname)); 660 nvlist_move_nvlist(nvlout, nvlname, elem); 661 } 662 663 freeaddrinfo(res); 664 error = 0; 665 out: 666 return (error); 667 } 668 669 static bool 670 limit_has_entry(const nvlist_t *limits, const char *prefix) 671 { 672 const char *name; 673 size_t prefixlen; 674 void *cookie; 675 676 if (limits == NULL) 677 return (false); 678 679 prefixlen = strlen(prefix); 680 681 cookie = NULL; 682 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { 683 if (strncmp(name, prefix, prefixlen) == 0) 684 return (true); 685 } 686 687 return (false); 688 } 689 690 static int 691 dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 692 { 693 const char *name; 694 void *cookie; 695 int nvtype; 696 bool hastype, hasfamily; 697 698 hastype = false; 699 hasfamily = false; 700 701 cookie = NULL; 702 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { 703 if (nvtype == NV_TYPE_STRING) { 704 const char *type; 705 706 if (strncmp(name, "type", sizeof("type") - 1) != 0) 707 return (EINVAL); 708 type = nvlist_get_string(newlimits, name); 709 if (strcmp(type, "ADDR2NAME") != 0 && 710 strcmp(type, "NAME2ADDR") != 0 && 711 strcmp(type, "ADDR") != 0 && 712 strcmp(type, "NAME") != 0) { 713 return (EINVAL); 714 } 715 if (!dns_allowed_type(oldlimits, type)) 716 return (ENOTCAPABLE); 717 hastype = true; 718 } else if (nvtype == NV_TYPE_NUMBER) { 719 int family; 720 721 if (strncmp(name, "family", sizeof("family") - 1) != 0) 722 return (EINVAL); 723 family = (int)nvlist_get_number(newlimits, name); 724 if (!dns_allowed_family(oldlimits, family)) 725 return (ENOTCAPABLE); 726 hasfamily = true; 727 } else { 728 return (EINVAL); 729 } 730 } 731 732 /* 733 * If the new limit doesn't mention type or family we have to 734 * check if the current limit does have those. Missing type or 735 * family in the limit means that all types or families are 736 * allowed. 737 */ 738 if (!hastype) { 739 if (limit_has_entry(oldlimits, "type")) 740 return (ENOTCAPABLE); 741 } 742 if (!hasfamily) { 743 if (limit_has_entry(oldlimits, "family")) 744 return (ENOTCAPABLE); 745 } 746 747 return (0); 748 } 749 750 static int 751 dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 752 nvlist_t *nvlout) 753 { 754 int error; 755 756 if (strcmp(cmd, "gethostbyname") == 0) 757 error = dns_gethostbyname(limits, nvlin, nvlout); 758 else if (strcmp(cmd, "gethostbyaddr") == 0) 759 error = dns_gethostbyaddr(limits, nvlin, nvlout); 760 else if (strcmp(cmd, "getnameinfo") == 0) 761 error = dns_getnameinfo(limits, nvlin, nvlout); 762 else if (strcmp(cmd, "getaddrinfo") == 0) 763 error = dns_getaddrinfo(limits, nvlin, nvlout); 764 else 765 error = NO_RECOVERY; 766 767 return (error); 768 } 769 770 CREATE_SERVICE("system.dns", dns_limit, dns_command, 0); 771