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