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 hints.ai_next = NULL; 636 hintsp = &hints; 637 family = hints.ai_family; 638 } else { 639 hintsp = NULL; 640 family = AF_UNSPEC; 641 } 642 643 if (!dns_allowed_family(limits, family)) 644 return (NO_RECOVERY); 645 646 error = getaddrinfo(hostname, servname, hintsp, &res); 647 if (error != 0) 648 goto out; 649 650 for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) { 651 elem = addrinfo_pack(cur); 652 n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); 653 assert(n > 0 && n < (int)sizeof(nvlname)); 654 nvlist_move_nvlist(nvlout, nvlname, elem); 655 } 656 657 freeaddrinfo(res); 658 error = 0; 659 out: 660 return (error); 661 } 662 663 static bool 664 limit_has_entry(const nvlist_t *limits, const char *prefix) 665 { 666 const char *name; 667 size_t prefixlen; 668 void *cookie; 669 670 if (limits == NULL) 671 return (false); 672 673 prefixlen = strlen(prefix); 674 675 cookie = NULL; 676 while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { 677 if (strncmp(name, prefix, prefixlen) == 0) 678 return (true); 679 } 680 681 return (false); 682 } 683 684 static int 685 dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 686 { 687 const char *name; 688 void *cookie; 689 int nvtype; 690 bool hastype, hasfamily; 691 692 hastype = false; 693 hasfamily = false; 694 695 cookie = NULL; 696 while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { 697 if (nvtype == NV_TYPE_STRING) { 698 const char *type; 699 700 if (strncmp(name, "type", sizeof("type") - 1) != 0) 701 return (EINVAL); 702 type = nvlist_get_string(newlimits, name); 703 if (strcmp(type, "ADDR") != 0 && 704 strcmp(type, "NAME") != 0) { 705 return (EINVAL); 706 } 707 if (!dns_allowed_type(oldlimits, type)) 708 return (ENOTCAPABLE); 709 hastype = true; 710 } else if (nvtype == NV_TYPE_NUMBER) { 711 int family; 712 713 if (strncmp(name, "family", sizeof("family") - 1) != 0) 714 return (EINVAL); 715 family = (int)nvlist_get_number(newlimits, name); 716 if (!dns_allowed_family(oldlimits, family)) 717 return (ENOTCAPABLE); 718 hasfamily = true; 719 } else { 720 return (EINVAL); 721 } 722 } 723 724 /* 725 * If the new limit doesn't mention type or family we have to 726 * check if the current limit does have those. Missing type or 727 * family in the limit means that all types or families are 728 * allowed. 729 */ 730 if (!hastype) { 731 if (limit_has_entry(oldlimits, "type")) 732 return (ENOTCAPABLE); 733 } 734 if (!hasfamily) { 735 if (limit_has_entry(oldlimits, "family")) 736 return (ENOTCAPABLE); 737 } 738 739 return (0); 740 } 741 742 static int 743 dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 744 nvlist_t *nvlout) 745 { 746 int error; 747 748 if (strcmp(cmd, "gethostbyname") == 0) 749 error = dns_gethostbyname(limits, nvlin, nvlout); 750 else if (strcmp(cmd, "gethostbyaddr") == 0) 751 error = dns_gethostbyaddr(limits, nvlin, nvlout); 752 else if (strcmp(cmd, "getnameinfo") == 0) 753 error = dns_getnameinfo(limits, nvlin, nvlout); 754 else if (strcmp(cmd, "getaddrinfo") == 0) 755 error = dns_getaddrinfo(limits, nvlin, nvlout); 756 else 757 error = NO_RECOVERY; 758 759 return (error); 760 } 761 762 CREATE_SERVICE("system.dns", dns_limit, dns_command); 763