1/* 2 * Copyright (c) 2008-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <si_module.h> 25#include <stdio.h> 26#include <unistd.h> 27#include <string.h> 28#include <time.h> 29#include <errno.h> 30#include <arpa/inet.h> 31#include <sys/stat.h> 32#include <ils.h> 33#include <pthread.h> 34#include <libkern/OSAtomic.h> 35#include <dispatch/dispatch.h> 36 37/* GLOBAL */ 38uint32_t gL1CacheEnabled = 1; 39 40#define CACHE_COUNT CATEGORY_COUNT 41#define CACHE_MAX 20 42 43typedef struct 44{ 45 pthread_mutex_t mutex; 46 int head; 47 si_item_t *item[CACHE_MAX]; 48 si_list_t *list; 49} cache_store_t; 50 51typedef struct 52{ 53 cache_store_t cache_store[CACHE_COUNT]; 54} cache_si_private_t; 55 56static void * 57cache_validate_item(cache_si_private_t *pp, int cat, int where) 58{ 59 si_item_t *item; 60 61 item = pp->cache_store[cat].item[where]; 62 if (item == NULL) return NULL; 63 64 if (si_item_is_valid(item)) return si_item_retain(item); 65 66 si_item_release(item); 67 pp->cache_store[cat].item[where] = NULL; 68 69 return NULL; 70} 71 72static void * 73cache_validate_list(cache_si_private_t *pp, int cat) 74{ 75 uint32_t i, valid; 76 si_item_t *item, *last; 77 si_list_t *list; 78 79 list = pp->cache_store[cat].list; 80 81 if (list == NULL) return NULL; 82 if (list->count == 0) return NULL; 83 84 last = list->entry[0]; 85 valid = si_item_is_valid(last); 86 87 for (i = 1; (i < list->count) && (valid == 1); i++) 88 { 89 item = list->entry[i]; 90 if ((item->src == last->src) && (item->type == last->type) && (item->validation_a == last->validation_a) && (item->validation_b == last->validation_b)) continue; 91 92 last = item; 93 valid = si_item_is_valid(last); 94 } 95 96 if (valid) return si_list_retain(list); 97 98 si_list_release(list); 99 pp->cache_store[cat].list = NULL; 100 101 return NULL; 102} 103 104static si_item_t * 105cache_fetch_item(si_mod_t *si, int cat, const char *name, uint32_t num, int which) 106{ 107 int i; 108 cache_si_private_t *pp; 109 si_item_t *item; 110 111 if (si == NULL) return NULL; 112 if (gL1CacheEnabled == 0) return NULL; 113 114 pp = (cache_si_private_t *)si->private; 115 if (pp == NULL) return NULL; 116 117 pthread_mutex_lock(&pp->cache_store[cat].mutex); 118 119 for (i = 0; i < CACHE_MAX; i++) 120 { 121 item = cache_validate_item(pp, cat, i); 122 if (item && si_item_match(item, cat, name, num, which)) 123 { 124 break; 125 } 126 else 127 { 128 si_item_release(item); 129 item = NULL; 130 } 131 } 132 133 pthread_mutex_unlock(&(pp->cache_store[cat].mutex)); 134 135 return item; 136} 137 138static si_list_t * 139cache_fetch_list(si_mod_t *si, int cat) 140{ 141 cache_si_private_t *pp; 142 si_list_t *list; 143 144 if (si == NULL) return NULL; 145 if (gL1CacheEnabled == 0) return NULL; 146 147 pp = (cache_si_private_t *)si->private; 148 if (pp == NULL) return NULL; 149 150 pthread_mutex_lock(&(pp->cache_store[cat].mutex)); 151 list = cache_validate_list(pp, cat); 152 pthread_mutex_unlock(&(pp->cache_store[cat].mutex)); 153 154 return list; 155} 156 157static si_item_t * 158cache_user_byname(si_mod_t *si, const char *name) 159{ 160 return cache_fetch_item(si, CATEGORY_USER, name, 0, SEL_NAME); 161} 162 163static si_item_t * 164cache_user_byuid(si_mod_t *si, uid_t uid) 165{ 166 return cache_fetch_item(si, CATEGORY_USER, NULL, uid, SEL_NUMBER); 167} 168 169static si_list_t * 170cache_user_all(si_mod_t *si) 171{ 172 return cache_fetch_list(si, CATEGORY_USER); 173} 174 175static si_item_t * 176cache_group_byname(si_mod_t *si, const char *name) 177{ 178 return cache_fetch_item(si, CATEGORY_GROUP, name, 0, SEL_NAME); 179} 180 181static si_item_t * 182cache_group_bygid(si_mod_t *si, gid_t gid) 183{ 184 return cache_fetch_item(si, CATEGORY_GROUP, NULL, gid, SEL_NUMBER); 185} 186 187static si_list_t * 188cache_group_all(si_mod_t *si) 189{ 190 return cache_fetch_list(si, CATEGORY_GROUP); 191} 192 193static si_item_t * 194cache_grouplist(si_mod_t *si, const char *name, uint32_t count) 195{ 196 return cache_fetch_item(si, CATEGORY_GROUPLIST, name, 0, SEL_NAME); 197} 198 199static si_item_t * 200cache_alias_byname(si_mod_t *si, const char *name) 201{ 202 return cache_fetch_item(si, CATEGORY_ALIAS, name, 0, SEL_NAME); 203} 204 205static si_list_t * 206cache_alias_all(si_mod_t *si) 207{ 208 return cache_fetch_list(si, CATEGORY_ALIAS); 209} 210 211static si_item_t * 212cache_host_byname(si_mod_t *si, const char *name, int af, const char *ignored, uint32_t *err) 213{ 214 si_item_t *item; 215 216 if (err != NULL) *err = SI_STATUS_NO_ERROR; 217 item = NULL; 218 219 if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, name, af, SEL_NAME); 220 else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, name, af, SEL_NAME); 221 222 if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; 223 224 return item; 225} 226 227static si_item_t * 228cache_host_byaddr(si_mod_t *si, const void *addr, int af, const char *ignored, uint32_t *err) 229{ 230 si_item_t *item; 231 232 if (err != NULL) *err = SI_STATUS_NO_ERROR; 233 item = NULL; 234 235 if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, addr, af, SEL_NUMBER); 236 else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, addr, af, SEL_NUMBER); 237 238 if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; 239 240 return item; 241} 242 243static si_list_t * 244cache_host_all(si_mod_t *si) 245{ 246 return cache_fetch_list(si, CATEGORY_HOST); 247} 248 249static si_item_t * 250cache_network_byname(si_mod_t *si, const char *name) 251{ 252 return cache_fetch_item(si, CATEGORY_NETWORK, name, 0, SEL_NAME); 253} 254 255static si_item_t * 256cache_network_byaddr(si_mod_t *si, uint32_t addr) 257{ 258 return cache_fetch_item(si, CATEGORY_NETWORK, NULL, addr, SEL_NUMBER); 259} 260 261static si_list_t * 262cache_network_all(si_mod_t *si) 263{ 264 return cache_fetch_list(si, CATEGORY_NETWORK); 265} 266 267static si_item_t * 268cache_service_byname(si_mod_t *si, const char *name, const char *proto) 269{ 270 uint32_t pn; 271 272 if (name == NULL) return NULL; 273 if (proto == NULL) return cache_fetch_item(si, CATEGORY_SERVICE, name, 0, SEL_NAME); 274 275 pn = 1; 276 if (string_equal(proto, "tcp")) pn = 2; 277 278 return cache_fetch_item(si, CATEGORY_SERVICE, name, pn, SEL_NAME); 279} 280 281static si_item_t * 282cache_service_byport(si_mod_t *si, int port, const char *proto) 283{ 284 return cache_fetch_item(si, CATEGORY_SERVICE, proto, port, SEL_NUMBER); 285} 286 287static si_list_t * 288cache_service_all(si_mod_t *si) 289{ 290 return cache_fetch_list(si, CATEGORY_SERVICE); 291} 292 293static si_item_t * 294cache_protocol_byname(si_mod_t *si, const char *name) 295{ 296 return cache_fetch_item(si, CATEGORY_PROTOCOL, name, 0, SEL_NAME); 297} 298 299static si_item_t * 300cache_protocol_bynumber(si_mod_t *si, int number) 301{ 302 return cache_fetch_item(si, CATEGORY_PROTOCOL, NULL, number, SEL_NUMBER); 303} 304 305static si_list_t * 306cache_protocol_all(si_mod_t *si) 307{ 308 return cache_fetch_list(si, CATEGORY_PROTOCOL); 309} 310 311static si_item_t * 312cache_rpc_byname(si_mod_t *si, const char *name) 313{ 314 return cache_fetch_item(si, CATEGORY_RPC, name, 0, SEL_NAME); 315} 316 317static si_item_t * 318cache_rpc_bynumber(si_mod_t *si, int number) 319{ 320 return cache_fetch_item(si, CATEGORY_RPC, NULL, number, SEL_NUMBER); 321} 322 323static si_list_t * 324cache_rpc_all(si_mod_t *si) 325{ 326 return cache_fetch_list(si, CATEGORY_RPC); 327} 328 329static si_item_t * 330cache_fs_byspec(si_mod_t *si, const char *name) 331{ 332 return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NAME); 333} 334 335static si_item_t * 336cache_fs_byfile(si_mod_t *si, const char *name) 337{ 338 return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NUMBER); 339} 340 341static si_list_t * 342cache_fs_all(si_mod_t *si) 343{ 344 return cache_fetch_list(si, CATEGORY_FS); 345} 346 347static si_item_t * 348cache_mac_byname(si_mod_t *si, const char *name) 349{ 350 return cache_fetch_item(si, CATEGORY_MAC, name, 0, SEL_NAME); 351} 352 353static si_item_t * 354cache_mac_bymac(si_mod_t *si, const char *mac) 355{ 356 return cache_fetch_item(si, CATEGORY_MAC, mac, 0, SEL_NUMBER); 357} 358 359static si_list_t * 360cache_mac_all(si_mod_t *si) 361{ 362 return cache_fetch_list(si, CATEGORY_MAC); 363} 364 365static si_item_t * 366cache_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, const char *ignored, uint32_t *err) 367{ 368 /* 369 * Caching of getnameinfo(3) is not supported. 370 * Only the individual host_byaddr and serv_byaddr responses will be cached. 371 * This is because getnameinfo(3) returns numeric responses instead of 372 * failing, which would poison the cache. 373 */ 374 if (err) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; 375 return NULL; 376} 377 378static void 379cache_close(si_mod_t *si) 380{ 381 cache_si_private_t *pp; 382 int i, j; 383 384 if (si == NULL) return; 385 386 pp = (cache_si_private_t *)si->private; 387 if (pp == NULL) return; 388 389 for (i = 0; i < CACHE_COUNT; i++) 390 { 391 si_list_release(pp->cache_store[i].list); 392 393 for (j = 0; j < CACHE_MAX; j++) 394 { 395 si_item_release(pp->cache_store[i].item[j]); 396 pp->cache_store[i].item[j] = NULL; 397 } 398 399 pthread_mutex_destroy(&(pp->cache_store[i].mutex)); 400 } 401 402 free(pp); 403} 404 405si_mod_t * 406si_module_static_cache(void) 407{ 408 static const struct si_mod_vtable_s cache_vtable = 409 { 410 .sim_close = &cache_close, 411 412 .sim_user_byname = &cache_user_byname, 413 .sim_user_byuid = &cache_user_byuid, 414 .sim_user_byuuid = NULL, 415 .sim_user_all = &cache_user_all, 416 417 .sim_group_byname = &cache_group_byname, 418 .sim_group_bygid = &cache_group_bygid, 419 .sim_group_byuuid = NULL, 420 .sim_group_all = &cache_group_all, 421 422 .sim_grouplist = &cache_grouplist, 423 424 /* no netgroup support */ 425 .sim_netgroup_byname = NULL, 426 .sim_in_netgroup = NULL, 427 428 .sim_alias_byname = &cache_alias_byname, 429 .sim_alias_all = &cache_alias_all, 430 431 .sim_host_byname = &cache_host_byname, 432 .sim_host_byaddr = &cache_host_byaddr, 433 .sim_host_all = &cache_host_all, 434 435 .sim_network_byname = &cache_network_byname, 436 .sim_network_byaddr = &cache_network_byaddr, 437 .sim_network_all = &cache_network_all, 438 439 .sim_service_byname = &cache_service_byname, 440 .sim_service_byport = &cache_service_byport, 441 .sim_service_all = &cache_service_all, 442 443 .sim_protocol_byname = &cache_protocol_byname, 444 .sim_protocol_bynumber = &cache_protocol_bynumber, 445 .sim_protocol_all = &cache_protocol_all, 446 447 .sim_rpc_byname = &cache_rpc_byname, 448 .sim_rpc_bynumber = &cache_rpc_bynumber, 449 .sim_rpc_all = &cache_rpc_all, 450 451 .sim_fs_byspec = &cache_fs_byspec, 452 .sim_fs_byfile = &cache_fs_byfile, 453 .sim_fs_all = &cache_fs_all, 454 455 .sim_mac_byname = &cache_mac_byname, 456 .sim_mac_bymac = &cache_mac_bymac, 457 .sim_mac_all = &cache_mac_all, 458 459 /* no addrinfo support */ 460 .sim_wants_addrinfo = NULL, 461 .sim_addrinfo = NULL, 462 463 .sim_nameinfo = &cache_nameinfo, 464 }; 465 466 static si_mod_t si = 467 { 468 .vers = 1, 469 .refcount = 1, 470 .flags = SI_MOD_FLAG_STATIC, 471 472 .private = NULL, 473 .vtable = &cache_vtable, 474 }; 475 476 static dispatch_once_t once; 477 478 dispatch_once(&once, ^{ 479 cache_si_private_t *cache; 480 int i, j; 481 482 cache = calloc(1, sizeof(cache_si_private_t)); 483 si.name = strdup("cache"); 484 si.private = cache; 485 486 for (i = 0; i < CACHE_COUNT; i++) { 487 for (j = 0; j < CACHE_MAX; j++) { 488 pthread_mutex_init(&(cache->cache_store[i].mutex), NULL); 489 } 490 } 491 }); 492 493 return &si; 494} 495 496void 497si_cache_add_item(si_mod_t *si, si_mod_t *src, si_item_t *item) 498{ 499 cache_si_private_t *pp; 500 int head, cat; 501 502 if (si == NULL) return; 503 if (src == NULL) return; 504 if (item == NULL) return; 505 506 if (si == src) return; 507 508 if (src->name == NULL) return; 509 if (string_equal(src->name, "cache")) return; 510 511 cat = item->type; 512 if ((cat < 0) || (cat >= CACHE_COUNT)) return; 513 514 pp = (cache_si_private_t *)si->private; 515 if (pp == NULL) return; 516 517 pthread_mutex_lock(&(pp->cache_store[cat].mutex)); 518 519 head = pp->cache_store[item->type].head; 520 521 si_item_release(pp->cache_store[item->type].item[head]); 522 pp->cache_store[item->type].item[head] = si_item_retain(item); 523 524 head++; 525 if (head >= CACHE_MAX) head = 0; 526 pp->cache_store[item->type].head = head; 527 528 pthread_mutex_unlock(&(pp->cache_store[cat].mutex)); 529} 530 531void 532si_cache_add_list(si_mod_t *si, si_mod_t *src, si_list_t *list) 533{ 534 cache_si_private_t *pp; 535 si_item_t *item; 536 int cat; 537 538 if (si == NULL) return; 539 if (src == NULL) return; 540 if (list == NULL) return; 541 if (list->count == 0) return; 542 543 if (si == src) return; 544 545 if (src->name == NULL) return; 546 if (string_equal(src->name, "cache")) return; 547 548 item = list->entry[0]; 549 if (item == NULL) return; 550 551 cat = item->type; 552 if ((cat < 0) || (cat >= CACHE_COUNT)) return; 553 554 pp = (cache_si_private_t *)si->private; 555 if (pp == NULL) return; 556 557 pthread_mutex_lock(&(pp->cache_store[cat].mutex)); 558 559 si_list_release(pp->cache_store[item->type].list); 560 pp->cache_store[item->type].list = si_list_retain(list); 561 562 pthread_mutex_unlock(&(pp->cache_store[cat].mutex)); 563} 564