2  * Copyright (c) 2008-2018 Apple Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  24 #include "libinfo_common.h" 
  26 #include <si_module.h> 
  32 #include <arpa/inet.h> 
  36 #include <libkern/OSAtomic.h> 
  37 #include <dispatch/dispatch.h> 
  41 uint32_t gL1CacheEnabled 
= 1; 
  43 #define CACHE_COUNT CATEGORY_COUNT 
  48         pthread_mutex_t mutex
; 
  50         si_item_t 
*item
[CACHE_MAX
]; 
  56         cache_store_t cache_store
[CACHE_COUNT
]; 
  60 cache_validate_item(cache_si_private_t 
*pp
, int cat
, int where
) 
  64         item 
= pp
->cache_store
[cat
].item
[where
]; 
  65         if (item 
== NULL
) return NULL
; 
  67         if (si_item_is_valid(item
)) return si_item_retain(item
); 
  69         si_item_release(item
); 
  70         pp
->cache_store
[cat
].item
[where
] = NULL
; 
  76 cache_validate_list(cache_si_private_t 
*pp
, int cat
) 
  79         si_item_t 
*item
, *last
; 
  82         list 
= pp
->cache_store
[cat
].list
; 
  84         if (list 
== NULL
) return NULL
; 
  85         if (list
->count 
== 0) return NULL
; 
  87         last 
= list
->entry
[0]; 
  88         valid 
= si_item_is_valid(last
); 
  90         for (i 
= 1; (i 
< list
->count
) && (valid 
== 1); i
++) 
  92                 item 
= list
->entry
[i
]; 
  93                 if ((item
->src 
== last
->src
) && (item
->type 
== last
->type
) && (item
->validation_a 
== last
->validation_a
) && (item
->validation_b 
== last
->validation_b
)) continue; 
  96                 valid 
= si_item_is_valid(last
); 
  99         if (valid
) return si_list_retain(list
); 
 101         si_list_release(list
); 
 102         pp
->cache_store
[cat
].list 
= NULL
; 
 108 cache_fetch_item(si_mod_t 
*si
, int cat
, const char *name
, uint32_t num
, int which
) 
 111         cache_si_private_t 
*pp
; 
 114         if (si 
== NULL
) return NULL
; 
 115         if (gL1CacheEnabled 
== 0) return NULL
; 
 117         pp 
= (cache_si_private_t 
*)si
->private; 
 118         if (pp 
== NULL
) return NULL
; 
 120         pthread_mutex_lock(&pp
->cache_store
[cat
].mutex
); 
 122         for (i 
= 0; i 
< CACHE_MAX
; i
++) 
 124                 item 
= cache_validate_item(pp
, cat
, i
); 
 125                 if (item 
&& si_item_match(item
, cat
, name
, num
, which
)) 
 131                         si_item_release(item
); 
 136         pthread_mutex_unlock(&(pp
->cache_store
[cat
].mutex
)); 
 142 cache_fetch_list(si_mod_t 
*si
, int cat
) 
 144         cache_si_private_t 
*pp
; 
 147         if (si 
== NULL
) return NULL
; 
 148         if (gL1CacheEnabled 
== 0) return NULL
; 
 150         pp 
= (cache_si_private_t 
*)si
->private; 
 151         if (pp 
== NULL
) return NULL
; 
 153         pthread_mutex_lock(&(pp
->cache_store
[cat
].mutex
)); 
 154         list 
= cache_validate_list(pp
, cat
); 
 155         pthread_mutex_unlock(&(pp
->cache_store
[cat
].mutex
)); 
 161 cache_user_byname(si_mod_t 
*si
, const char *name
) 
 163         return cache_fetch_item(si
, CATEGORY_USER
, name
, 0, SEL_NAME
); 
 167 cache_user_byuid(si_mod_t 
*si
, uid_t uid
) 
 169         return cache_fetch_item(si
, CATEGORY_USER
, NULL
, uid
, SEL_NUMBER
); 
 173 cache_user_all(si_mod_t 
*si
) 
 175         return cache_fetch_list(si
, CATEGORY_USER
); 
 179 cache_group_byname(si_mod_t 
*si
, const char *name
) 
 181         return cache_fetch_item(si
, CATEGORY_GROUP
, name
, 0, SEL_NAME
); 
 185 cache_group_bygid(si_mod_t 
*si
, gid_t gid
) 
 187         return cache_fetch_item(si
, CATEGORY_GROUP
, NULL
, gid
, SEL_NUMBER
); 
 191 cache_group_all(si_mod_t 
*si
) 
 193         return cache_fetch_list(si
, CATEGORY_GROUP
); 
 197 cache_grouplist(si_mod_t 
*si
, const char *name
, uint32_t count
) 
 199         return cache_fetch_item(si
, CATEGORY_GROUPLIST
, name
, 0, SEL_NAME
); 
 203 cache_alias_byname(si_mod_t 
*si
, const char *name
) 
 205         return cache_fetch_item(si
, CATEGORY_ALIAS
, name
, 0, SEL_NAME
); 
 209 cache_alias_all(si_mod_t 
*si
) 
 211         return cache_fetch_list(si
, CATEGORY_ALIAS
); 
 215 cache_host_byname(si_mod_t 
*si
, const char *name
, int af
, const char *ignored
, uint32_t *err
) 
 219         if (err 
!= NULL
) *err 
= SI_STATUS_NO_ERROR
; 
 222         if (af 
== AF_INET
) item 
= cache_fetch_item(si
, CATEGORY_HOST_IPV4
, name
, af
, SEL_NAME
); 
 223         else item 
= cache_fetch_item(si
, CATEGORY_HOST_IPV6
, name
, af
, SEL_NAME
); 
 225         if ((item 
== NULL
) && (err 
!= NULL
) && (*err 
== 0)) *err 
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
; 
 231 cache_host_byaddr(si_mod_t 
*si
, const void *addr
, int af
, const char *ignored
, uint32_t *err
) 
 235         if (err 
!= NULL
) *err 
= SI_STATUS_NO_ERROR
; 
 238         if (af 
== AF_INET
) item 
= cache_fetch_item(si
, CATEGORY_HOST_IPV4
, addr
, af
, SEL_NUMBER
); 
 239         else item 
= cache_fetch_item(si
, CATEGORY_HOST_IPV6
, addr
, af
, SEL_NUMBER
); 
 241         if ((item 
== NULL
) && (err 
!= NULL
) && (*err 
== 0)) *err 
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
; 
 247 cache_host_all(si_mod_t 
*si
) 
 249         return cache_fetch_list(si
, CATEGORY_HOST
); 
 253 cache_network_byname(si_mod_t 
*si
, const char *name
) 
 255         return cache_fetch_item(si
, CATEGORY_NETWORK
, name
, 0, SEL_NAME
); 
 259 cache_network_byaddr(si_mod_t 
*si
, uint32_t addr
) 
 261         return cache_fetch_item(si
, CATEGORY_NETWORK
, NULL
, addr
, SEL_NUMBER
); 
 265 cache_network_all(si_mod_t 
*si
) 
 267         return cache_fetch_list(si
, CATEGORY_NETWORK
); 
 271 cache_service_byname(si_mod_t 
*si
, const char *name
, const char *proto
) 
 275         if (name 
== NULL
) return NULL
; 
 276         if (proto 
== NULL
) return cache_fetch_item(si
, CATEGORY_SERVICE
, name
, 0, SEL_NAME
); 
 279         if (string_equal(proto
, "tcp")) pn 
= 2; 
 281         return cache_fetch_item(si
, CATEGORY_SERVICE
, name
, pn
, SEL_NAME
); 
 285 cache_service_byport(si_mod_t 
*si
, int port
, const char *proto
) 
 287         return cache_fetch_item(si
, CATEGORY_SERVICE
, proto
, port
, SEL_NUMBER
); 
 291 cache_service_all(si_mod_t 
*si
) 
 293         return cache_fetch_list(si
, CATEGORY_SERVICE
); 
 297 cache_protocol_byname(si_mod_t 
*si
, const char *name
) 
 299         return cache_fetch_item(si
, CATEGORY_PROTOCOL
, name
, 0, SEL_NAME
); 
 303 cache_protocol_bynumber(si_mod_t 
*si
, int number
) 
 305         return cache_fetch_item(si
, CATEGORY_PROTOCOL
, NULL
, number
, SEL_NUMBER
); 
 309 cache_protocol_all(si_mod_t 
*si
) 
 311         return cache_fetch_list(si
, CATEGORY_PROTOCOL
); 
 315 cache_rpc_byname(si_mod_t 
*si
, const char *name
) 
 317         return cache_fetch_item(si
, CATEGORY_RPC
, name
, 0, SEL_NAME
); 
 321 cache_rpc_bynumber(si_mod_t 
*si
, int number
) 
 323         return cache_fetch_item(si
, CATEGORY_RPC
, NULL
, number
, SEL_NUMBER
); 
 327 cache_rpc_all(si_mod_t 
*si
) 
 329         return cache_fetch_list(si
, CATEGORY_RPC
); 
 333 cache_fs_byspec(si_mod_t 
*si
, const char *name
) 
 335         return cache_fetch_item(si
, CATEGORY_FS
, name
, 0, SEL_NAME
); 
 339 cache_fs_byfile(si_mod_t 
*si
, const char *name
) 
 341         return cache_fetch_item(si
, CATEGORY_FS
, name
, 0, SEL_NUMBER
); 
 345 cache_fs_all(si_mod_t 
*si
) 
 347         return cache_fetch_list(si
, CATEGORY_FS
); 
 351 cache_mac_byname(si_mod_t 
*si
, const char *name
) 
 353         return cache_fetch_item(si
, CATEGORY_MAC
, name
, 0, SEL_NAME
); 
 357 cache_mac_bymac(si_mod_t 
*si
, const char *mac
) 
 359         return cache_fetch_item(si
, CATEGORY_MAC
, mac
, 0, SEL_NUMBER
); 
 363 cache_mac_all(si_mod_t 
*si
) 
 365         return cache_fetch_list(si
, CATEGORY_MAC
); 
 369 cache_nameinfo(si_mod_t 
*si
, const struct sockaddr 
*sa
, int flags
, const char *ignored
, uint32_t *err
) 
 372          * Caching of getnameinfo(3) is not supported. 
 373          * Only the individual host_byaddr and serv_byaddr responses will be cached. 
 374          * This is because getnameinfo(3) returns numeric responses instead of 
 375          * failing, which would poison the cache. 
 377         if (err
) *err 
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
; 
 382 cache_close(si_mod_t 
*si
) 
 384         cache_si_private_t 
*pp
; 
 387         if (si 
== NULL
) return; 
 389         pp 
= (cache_si_private_t 
*)si
->private; 
 390         if (pp 
== NULL
) return; 
 392         for (i 
= 0; i 
< CACHE_COUNT
; i
++) 
 394                 si_list_release(pp
->cache_store
[i
].list
); 
 396                 for (j 
= 0; j 
< CACHE_MAX
; j
++) 
 398                         si_item_release(pp
->cache_store
[i
].item
[j
]); 
 399                         pp
->cache_store
[i
].item
[j
] = NULL
; 
 402                 pthread_mutex_destroy(&(pp
->cache_store
[i
].mutex
)); 
 409 si_module_static_cache(void) 
 411         static const struct si_mod_vtable_s cache_vtable 
= 
 413                 .sim_close 
= &cache_close
, 
 415                 .sim_user_byname 
= &cache_user_byname
, 
 416                 .sim_user_byuid 
= &cache_user_byuid
, 
 417                 .sim_user_byuuid 
= NULL
, 
 418                 .sim_user_all 
= &cache_user_all
, 
 420                 .sim_group_byname 
= &cache_group_byname
, 
 421                 .sim_group_bygid 
= &cache_group_bygid
, 
 422                 .sim_group_byuuid 
= NULL
, 
 423                 .sim_group_all 
= &cache_group_all
, 
 425                 .sim_grouplist 
= &cache_grouplist
, 
 427                 /* no netgroup support */ 
 428                 .sim_netgroup_byname 
= NULL
, 
 429                 .sim_in_netgroup 
= NULL
, 
 431                 .sim_alias_byname 
= &cache_alias_byname
, 
 432                 .sim_alias_all 
= &cache_alias_all
, 
 434                 .sim_host_byname 
= &cache_host_byname
, 
 435                 .sim_host_byaddr 
= &cache_host_byaddr
, 
 436                 .sim_host_all 
= &cache_host_all
, 
 438                 .sim_network_byname 
= &cache_network_byname
, 
 439                 .sim_network_byaddr 
= &cache_network_byaddr
, 
 440                 .sim_network_all 
= &cache_network_all
, 
 442                 .sim_service_byname 
= &cache_service_byname
, 
 443                 .sim_service_byport 
= &cache_service_byport
, 
 444                 .sim_service_all 
= &cache_service_all
, 
 446                 .sim_protocol_byname 
= &cache_protocol_byname
, 
 447                 .sim_protocol_bynumber 
= &cache_protocol_bynumber
, 
 448                 .sim_protocol_all 
= &cache_protocol_all
, 
 450                 .sim_rpc_byname 
= &cache_rpc_byname
, 
 451                 .sim_rpc_bynumber 
= &cache_rpc_bynumber
, 
 452                 .sim_rpc_all 
= &cache_rpc_all
, 
 454                 .sim_fs_byspec 
= &cache_fs_byspec
, 
 455                 .sim_fs_byfile 
= &cache_fs_byfile
, 
 456                 .sim_fs_all 
= &cache_fs_all
, 
 458                 .sim_mac_byname 
= &cache_mac_byname
, 
 459                 .sim_mac_bymac 
= &cache_mac_bymac
, 
 460                 .sim_mac_all 
= &cache_mac_all
, 
 462                 /* no addrinfo support */ 
 463                 .sim_wants_addrinfo 
= NULL
, 
 464                 .sim_addrinfo 
= NULL
, 
 466                 .sim_nameinfo 
= &cache_nameinfo
, 
 473                 .flags 
= SI_MOD_FLAG_STATIC
, 
 476                 .vtable 
= &cache_vtable
, 
 479         static dispatch_once_t once
; 
 481         dispatch_once(&once
, ^{ 
 482                 cache_si_private_t 
*cache
; 
 485                 cache 
= calloc(1, sizeof(cache_si_private_t
)); 
 486                 si
.name 
= strdup("cache"); 
 489                 for (i 
= 0; i 
< CACHE_COUNT
; i
++) { 
 490                         for (j 
= 0; j 
< CACHE_MAX
; j
++) { 
 491                                 pthread_mutex_init(&(cache
->cache_store
[i
].mutex
), NULL
); 
 500 si_cache_add_item(si_mod_t 
*si
, si_mod_t 
*src
, si_item_t 
*item
) 
 502         cache_si_private_t 
*pp
; 
 505         if (si 
== NULL
) return; 
 506         if (src 
== NULL
) return; 
 507         if (item 
== NULL
) return; 
 509         if (si 
== src
) return; 
 511         if (src
->name 
== NULL
) return; 
 512         if (string_equal(src
->name
, "cache")) return; 
 515         if ((cat 
< 0) || (cat 
>= CACHE_COUNT
)) return; 
 517         pp 
= (cache_si_private_t 
*)si
->private; 
 518         if (pp 
== NULL
) return; 
 520         pthread_mutex_lock(&(pp
->cache_store
[cat
].mutex
)); 
 522         head 
= pp
->cache_store
[item
->type
].head
; 
 524         si_item_release(pp
->cache_store
[item
->type
].item
[head
]); 
 525         pp
->cache_store
[item
->type
].item
[head
] = si_item_retain(item
); 
 528         if (head 
>= CACHE_MAX
) head 
= 0; 
 529         pp
->cache_store
[item
->type
].head 
= head
; 
 531         pthread_mutex_unlock(&(pp
->cache_store
[cat
].mutex
)); 
 535 si_cache_add_list(si_mod_t 
*si
, si_mod_t 
*src
, si_list_t 
*list
) 
 537         cache_si_private_t 
*pp
; 
 541         if (si 
== NULL
) return; 
 542         if (src 
== NULL
) return; 
 543         if (list 
== NULL
) return; 
 544         if (list
->count 
== 0) return; 
 546         if (si 
== src
) return; 
 548         if (src
->name 
== NULL
) return; 
 549         if (string_equal(src
->name
, "cache")) return; 
 551         item 
= list
->entry
[0]; 
 552         if (item 
== NULL
) return; 
 555         if ((cat 
< 0) || (cat 
>= CACHE_COUNT
)) return; 
 557         pp 
= (cache_si_private_t 
*)si
->private; 
 558         if (pp 
== NULL
) return; 
 560         pthread_mutex_lock(&(pp
->cache_store
[cat
].mutex
)); 
 562         si_list_release(pp
->cache_store
[item
->type
].list
); 
 563         pp
->cache_store
[item
->type
].list 
= si_list_retain(list
); 
 565         pthread_mutex_unlock(&(pp
->cache_store
[cat
].mutex
));