/* * Copyright (c) 2008-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include /* GLOBAL */ uint32_t gL1CacheEnabled = 1; #define CACHE_COUNT CATEGORY_COUNT #define CACHE_MAX 20 typedef struct { /* XXX should be mutex */ OSSpinLock lock; int head; si_item_t *item[CACHE_MAX]; si_list_t *list; } cache_store_t; typedef struct { cache_store_t cache_store[CACHE_COUNT]; } cache_si_private_t; static void * cache_validate_item(cache_si_private_t *pp, int cat, int where) { si_item_t *item; item = pp->cache_store[cat].item[where]; if (item == NULL) return NULL; if (si_item_is_valid(item)) return si_item_retain(item); si_item_release(item); pp->cache_store[cat].item[where] = NULL; return NULL; } static void * cache_validate_list(cache_si_private_t *pp, int cat) { uint32_t i, valid; si_item_t *item, *last; si_list_t *list; list = pp->cache_store[cat].list; if (list == NULL) return NULL; if (list->count == 0) return NULL; last = list->entry[0]; valid = si_item_is_valid(last); for (i = 1; (i < list->count) && (valid == 1); i++) { item = list->entry[i]; if ((item->src == last->src) && (item->type == last->type) && (item->validation_a == last->validation_a) && (item->validation_b == last->validation_b)) continue; last = item; valid = si_item_is_valid(last); } if (valid) return si_list_retain(list); si_list_release(list); pp->cache_store[cat].list = NULL; return NULL; } static si_item_t * cache_fetch_item(si_mod_t *si, int cat, const char *name, uint32_t num, int which) { int i; cache_si_private_t *pp; si_item_t *item; if (si == NULL) return NULL; if (gL1CacheEnabled == 0) return NULL; pp = (cache_si_private_t *)si->private; if (pp == NULL) return NULL; OSSpinLockLock(&(pp->cache_store[cat].lock)); for (i = 0; i < CACHE_MAX; i++) { item = cache_validate_item(pp, cat, i); if (item && si_item_match(item, cat, name, num, which)) { break; } else { si_item_release(item); item = NULL; } } OSSpinLockUnlock(&(pp->cache_store[cat].lock)); return item; } static si_list_t * cache_fetch_list(si_mod_t *si, int cat) { cache_si_private_t *pp; si_list_t *list; if (si == NULL) return NULL; if (gL1CacheEnabled == 0) return NULL; pp = (cache_si_private_t *)si->private; if (pp == NULL) return NULL; OSSpinLockLock(&(pp->cache_store[cat].lock)); list = cache_validate_list(pp, cat); OSSpinLockUnlock(&(pp->cache_store[cat].lock)); return list; } __private_extern__ si_item_t * cache_user_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_USER, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_user_byuid(si_mod_t *si, uid_t uid) { return cache_fetch_item(si, CATEGORY_USER, NULL, uid, SEL_NUMBER); } __private_extern__ si_list_t * cache_user_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_USER); } __private_extern__ si_item_t * cache_group_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_GROUP, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_group_bygid(si_mod_t *si, gid_t gid) { return cache_fetch_item(si, CATEGORY_GROUP, NULL, gid, SEL_NUMBER); } __private_extern__ si_list_t * cache_group_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_GROUP); } __private_extern__ si_item_t * cache_grouplist(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_GROUPLIST, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_alias_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_ALIAS, name, 0, SEL_NAME); } __private_extern__ si_list_t * cache_alias_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_ALIAS); } __private_extern__ si_item_t * cache_host_byname(si_mod_t *si, const char *name, int af, const char *ignored, uint32_t *err) { si_item_t *item; if (err != NULL) *err = SI_STATUS_NO_ERROR; item = NULL; if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, name, af, SEL_NAME); else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, name, af, SEL_NAME); if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; return item; } __private_extern__ si_item_t * cache_host_byaddr(si_mod_t *si, const void *addr, int af, const char *ignored, uint32_t *err) { si_item_t *item; if (err != NULL) *err = SI_STATUS_NO_ERROR; item = NULL; if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, addr, af, SEL_NUMBER); else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, addr, af, SEL_NUMBER); if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; return item; } __private_extern__ si_list_t * cache_host_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_HOST); } __private_extern__ si_item_t * cache_network_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_NETWORK, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_network_byaddr(si_mod_t *si, uint32_t addr) { return cache_fetch_item(si, CATEGORY_NETWORK, NULL, addr, SEL_NUMBER); } __private_extern__ si_list_t * cache_network_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_NETWORK); } __private_extern__ si_item_t * cache_service_byname(si_mod_t *si, const char *name, const char *proto) { uint32_t pn; if (name == NULL) return NULL; if (proto == NULL) return cache_fetch_item(si, CATEGORY_SERVICE, name, 0, SEL_NAME); pn = 1; if (string_equal(proto, "tcp")) pn = 2; return cache_fetch_item(si, CATEGORY_SERVICE, name, pn, SEL_NAME); } __private_extern__ si_item_t * cache_service_byport(si_mod_t *si, int port, const char *proto) { return cache_fetch_item(si, CATEGORY_SERVICE, proto, port, SEL_NUMBER); } __private_extern__ si_list_t * cache_service_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_SERVICE); } __private_extern__ si_item_t * cache_protocol_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_PROTOCOL, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_protocol_bynumber(si_mod_t *si, int number) { return cache_fetch_item(si, CATEGORY_PROTOCOL, NULL, number, SEL_NUMBER); } __private_extern__ si_list_t * cache_protocol_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_PROTOCOL); } __private_extern__ si_item_t * cache_rpc_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_RPC, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_rpc_bynumber(si_mod_t *si, int number) { return cache_fetch_item(si, CATEGORY_RPC, NULL, number, SEL_NUMBER); } __private_extern__ si_list_t * cache_rpc_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_RPC); } __private_extern__ si_item_t * cache_fs_byspec(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_fs_byfile(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NUMBER); } __private_extern__ si_list_t * cache_fs_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_FS); } __private_extern__ si_item_t * cache_mac_byname(si_mod_t *si, const char *name) { return cache_fetch_item(si, CATEGORY_MAC, name, 0, SEL_NAME); } __private_extern__ si_item_t * cache_mac_bymac(si_mod_t *si, const char *mac) { return cache_fetch_item(si, CATEGORY_MAC, mac, 0, SEL_NUMBER); } __private_extern__ si_list_t * cache_mac_all(si_mod_t *si) { return cache_fetch_list(si, CATEGORY_MAC); } __private_extern__ si_item_t * cache_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, const char *ignored, uint32_t *err) { /* * Caching of getnameinfo(3) is not supported. * Only the individual host_byaddr and serv_byaddr responses will be cached. * This is because getnameinfo(3) returns numeric responses instead of * failing, which would poison the cache. */ if (err) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND; return NULL; } __private_extern__ void cache_close(si_mod_t *si) { cache_si_private_t *pp; int i, j; if (si == NULL) return; pp = (cache_si_private_t *)si->private; if (pp == NULL) return; for (i = 0; i < CACHE_COUNT; i++) { si_list_release(pp->cache_store[i].list); for (j = 0; j < CACHE_MAX; j++) { si_item_release(pp->cache_store[i].item[j]); pp->cache_store[i].item[j] = NULL; } } free(si->private); } __private_extern__ si_mod_t * si_module_static_cache() { si_mod_t *out; char *outname; cache_si_private_t *pp; out = (si_mod_t *)calloc(1, sizeof(si_mod_t)); outname = strdup("cache"); pp = (cache_si_private_t *)calloc(1, sizeof(cache_si_private_t)); if ((out == NULL) || (outname == NULL) || (pp == NULL)) { if (out != NULL) free(out); if (outname != NULL) free(outname); if (pp != NULL) free(pp); errno = ENOMEM; return NULL; } out->name = outname; out->vers = 1; out->refcount = 1; out->private = pp; out->sim_close = cache_close; out->sim_user_byname = cache_user_byname; out->sim_user_byuid = cache_user_byuid; out->sim_user_all = cache_user_all; out->sim_group_byname = cache_group_byname; out->sim_group_bygid = cache_group_bygid; out->sim_group_all = cache_group_all; out->sim_grouplist = cache_grouplist; /* no netgroup support */ out->sim_netgroup_byname = NULL; out->sim_in_netgroup = NULL; out->sim_alias_byname = cache_alias_byname; out->sim_alias_all = cache_alias_all; out->sim_host_byname = cache_host_byname; out->sim_host_byaddr = cache_host_byaddr; out->sim_host_all = cache_host_all; out->sim_network_byname = cache_network_byname; out->sim_network_byaddr = cache_network_byaddr; out->sim_network_all = cache_network_all; out->sim_service_byname = cache_service_byname; out->sim_service_byport = cache_service_byport; out->sim_service_all = cache_service_all; out->sim_protocol_byname = cache_protocol_byname; out->sim_protocol_bynumber = cache_protocol_bynumber; out->sim_protocol_all = cache_protocol_all; out->sim_rpc_byname = cache_rpc_byname; out->sim_rpc_bynumber = cache_rpc_bynumber; out->sim_rpc_all = cache_rpc_all; out->sim_fs_byspec = cache_fs_byspec; out->sim_fs_byfile = cache_fs_byfile; out->sim_fs_all = cache_fs_all; out->sim_mac_byname = cache_mac_byname; out->sim_mac_bymac = cache_mac_bymac; out->sim_mac_all = cache_mac_all; /* no addrinfo support */ out->sim_wants_addrinfo = NULL; out->sim_addrinfo = NULL; out->sim_nameinfo = cache_nameinfo; return out; } __private_extern__ void si_cache_add_item(si_mod_t *si, si_mod_t *src, si_item_t *item) { cache_si_private_t *pp; int head, cat; if (si == NULL) return; if (src == NULL) return; if (item == NULL) return; if (si == src) return; if (src->name == NULL) return; if (string_equal(src->name, "cache")) return; cat = item->type; if ((cat < 0) || (cat >= CACHE_COUNT)) return; pp = (cache_si_private_t *)si->private; if (pp == NULL) return; OSSpinLockLock(&(pp->cache_store[cat].lock)); head = pp->cache_store[item->type].head; si_item_release(pp->cache_store[item->type].item[head]); pp->cache_store[item->type].item[head] = si_item_retain(item); head++; if (head >= CACHE_MAX) head = 0; pp->cache_store[item->type].head = head; OSSpinLockUnlock(&(pp->cache_store[cat].lock)); } __private_extern__ void si_cache_add_list(si_mod_t *si, si_mod_t *src, si_list_t *list) { cache_si_private_t *pp; si_item_t *item; int cat; if (si == NULL) return; if (src == NULL) return; if (list == NULL) return; if (list->count == 0) return; if (si == src) return; if (src->name == NULL) return; if (string_equal(src->name, "cache")) return; item = list->entry[0]; if (item == NULL) return; cat = item->type; if ((cat < 0) || (cat >= CACHE_COUNT)) return; pp = (cache_si_private_t *)si->private; if (pp == NULL) return; OSSpinLockLock(&(pp->cache_store[cat].lock)); si_list_release(pp->cache_store[item->type].list); pp->cache_store[item->type].list = si_list_retain(list); OSSpinLockUnlock(&(pp->cache_store[cat].lock)); }