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