1 /*- 2 * Copyright (c) 2012-2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 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, 0); 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, 0); 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, 0); 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, 0); 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 + 1); 304 if (serv != NULL && nvlist_exists_string(nvl, "serv")) 305 strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1); 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, "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, "ADDR")) 500 return (NO_RECOVERY); 501 502 family = (int)nvlist_get_number(nvlin, "family"); 503 504 if (!dns_allowed_family(limits, family)) 505 return (NO_RECOVERY); 506 507 addr = nvlist_get_binary(nvlin, "addr", &addrsize); 508 hp = gethostbyaddr(addr, (socklen_t)addrsize, family); 509 if (hp == NULL) 510 return (h_errno); 511 hostent_pack(hp, nvlout); 512 return (0); 513 } 514 515 static int 516 dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 517 { 518 struct sockaddr_storage sast; 519 const void *sabin; 520 char *host, *serv; 521 size_t sabinsize, hostlen, servlen; 522 socklen_t salen; 523 int error, flags; 524 525 if (!dns_allowed_type(limits, "NAME")) 526 return (NO_RECOVERY); 527 528 error = 0; 529 host = serv = NULL; 530 memset(&sast, 0, sizeof(sast)); 531 532 hostlen = (size_t)nvlist_get_number(nvlin, "hostlen"); 533 servlen = (size_t)nvlist_get_number(nvlin, "servlen"); 534 535 if (hostlen > 0) { 536 host = calloc(1, hostlen + 1); 537 if (host == NULL) { 538 error = EAI_MEMORY; 539 goto out; 540 } 541 } 542 if (servlen > 0) { 543 serv = calloc(1, servlen + 1); 544 if (serv == NULL) { 545 error = EAI_MEMORY; 546 goto out; 547 } 548 } 549 550 sabin = nvlist_get_binary(nvlin, "sa", &sabinsize); 551 if (sabinsize > sizeof(sast)) { 552 error = EAI_FAIL; 553 goto out; 554 } 555 556 memcpy(&sast, sabin, sabinsize); 557 salen = (socklen_t)sabinsize; 558 559 if ((sast.ss_family != AF_INET || 560 salen != sizeof(struct sockaddr_in)) && 561 (sast.ss_family != AF_INET6 || 562 salen != sizeof(struct sockaddr_in6))) { 563 error = EAI_FAIL; 564 goto out; 565 } 566 567 if (!dns_allowed_family(limits, (int)sast.ss_family)) { 568 error = NO_RECOVERY; 569 goto out; 570 } 571 572 flags = (int)nvlist_get_number(nvlin, "flags"); 573 574 error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen, 575 serv, servlen, flags); 576 if (error != 0) 577 goto out; 578 579 if (host != NULL) 580 nvlist_move_string(nvlout, "host", host); 581 if (serv != NULL) 582 nvlist_move_string(nvlout, "serv", serv); 583 out: 584 if (error != 0) { 585 free(host); 586 free(serv); 587 } 588 return (error); 589 } 590 591 static nvlist_t * 592 addrinfo_pack(const struct addrinfo *ai) 593 { 594 nvlist_t *nvl; 595 596 nvl = nvlist_create(0); 597 nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags); 598 nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family); 599 nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype); 600 nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol); 601 nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen); 602 if (ai->ai_canonname != NULL) 603 nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname); 604 605 return (nvl); 606 } 607 608 static int 609 dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 610 { 611 struct addrinfo hints, *hintsp, *res, *cur; 612 const char *hostname, *servname; 613 char nvlname[64]; 614 nvlist_t *elem; 615 unsigned int ii; 616 int error, family, n; 617 618 if (!dns_allowed_type(limits, "ADDR")) 619 return (NO_RECOVERY); 620 621 hostname = dnvlist_get_string(nvlin, "hostname", NULL); 622 servname = dnvlist_get_string(nvlin, "servname", NULL); 623 if (nvlist_exists_number(nvlin, "hints.ai_flags")) { 624 hints.ai_flags = (int)nvlist_get_number(nvlin, 625 "hints.ai_flags"); 626 hints.ai_family = (int)nvlist_get_number(nvlin, 627 "hints.ai_family"); 628 hints.ai_socktype = (int)nvlist_get_number(nvlin, 629 "hints.ai_socktype"); 630 hints.ai_protocol = (int)nvlist_get_number(nvlin, 631 "hints.ai_protocol"); 632 hints.ai_addrlen = 0; 633 hints.ai_addr = NULL; 634 hints.ai_canonname = NULL; 635 hintsp = &hints; 636 family = hints.ai_family; 637 } else { 638 hintsp = NULL; 639 family = AF_UNSPEC; 640 } 641 642 if (!dns_allowed_family(limits, family)) 643 return (NO_RECOVERY); 644 645 error = getaddrinfo(hostname, servname, hintsp, &res); 646 if (error != 0) 647 goto out; 648 649 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) { 650 elem = addrinfo_pack(cur); 651 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); 652 assert(n > 0 && n < (int)sizeof(nvlname)); 653 nvlist_move_nvlist(nvlout, nvlname, elem); 654 } 655 656 freeaddrinfo(res); 657 error = 0; 658 out: 659 return (error); 660 } 661 662 static bool 663 limit_has_entry(const nvlist_t *limits, const char *prefix) 664 { 665 const char *name; 666 size_t prefixlen; 667 void *cookie; 668 669 if (limits == NULL) 670 return (false); 671 672 prefixlen = strlen(prefix); 673 674 cookie = NULL; 675 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { 676 if (strncmp(name, prefix, prefixlen) == 0) 677 return (true); 678 } 679 680 return (false); 681 } 682 683 static int 684 dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 685 { 686 const char *name; 687 void *cookie; 688 int nvtype; 689 bool hastype, hasfamily; 690 691 hastype = false; 692 hasfamily = false; 693 694 cookie = NULL; 695 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { 696 if (nvtype == NV_TYPE_STRING) { 697 const char *type; 698 699 if (strncmp(name, "type", sizeof("type") - 1) != 0) 700 return (EINVAL); 701 type = nvlist_get_string(newlimits, name); 702 if (strcmp(type, "ADDR") != 0 && 703 strcmp(type, "NAME") != 0) { 704 return (EINVAL); 705 } 706 if (!dns_allowed_type(oldlimits, type)) 707 return (ENOTCAPABLE); 708 hastype = true; 709 } else if (nvtype == NV_TYPE_NUMBER) { 710 int family; 711 712 if (strncmp(name, "family", sizeof("family") - 1) != 0) 713 return (EINVAL); 714 family = (int)nvlist_get_number(newlimits, name); 715 if (!dns_allowed_family(oldlimits, family)) 716 return (ENOTCAPABLE); 717 hasfamily = true; 718 } else { 719 return (EINVAL); 720 } 721 } 722 723 /* 724 * If the new limit doesn't mention type or family we have to 725 * check if the current limit does have those. Missing type or 726 * family in the limit means that all types or families are 727 * allowed. 728 */ 729 if (!hastype) { 730 if (limit_has_entry(oldlimits, "type")) 731 return (ENOTCAPABLE); 732 } 733 if (!hasfamily) { 734 if (limit_has_entry(oldlimits, "family")) 735 return (ENOTCAPABLE); 736 } 737 738 return (0); 739 } 740 741 static int 742 dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 743 nvlist_t *nvlout) 744 { 745 int error; 746 747 if (strcmp(cmd, "gethostbyname") == 0) 748 error = dns_gethostbyname(limits, nvlin, nvlout); 749 else if (strcmp(cmd, "gethostbyaddr") == 0) 750 error = dns_gethostbyaddr(limits, nvlin, nvlout); 751 else if (strcmp(cmd, "getnameinfo") == 0) 752 error = dns_getnameinfo(limits, nvlin, nvlout); 753 else if (strcmp(cmd, "getaddrinfo") == 0) 754 error = dns_getaddrinfo(limits, nvlin, nvlout); 755 else 756 error = NO_RECOVERY; 757 758 return (error); 759 } 760 761 CREATE_SERVICE("system.dns", dns_limit, dns_command); 762