1/*
2 * Copyright (c) 2008-2011 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This ds 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 ds 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 * ds.
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#ifdef DS_AVAILABLE
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <string.h>
30#include <time.h>
31#include <errno.h>
32#include <arpa/inet.h>
33#include <sys/stat.h>
34#include <pthread.h>
35#include <ils.h>
36#include <pwd.h>
37#include <grp.h>
38#include <fstab.h>
39#include <netdb.h>
40#include <notify.h>
41#include <notify_keys.h>
42#include <si_data.h>
43#include <si_module.h>
44#include <netdb_async.h>
45#include <net/if.h>
46#include <xpc/xpc.h>
47#include <xpc/private.h>
48#include <opendirectory/odipc.h>
49#include <servers/bootstrap.h>
50#include <bootstrap_priv.h>
51#include <opendirectory/DSlibinfoMIG_types.h>
52#ifdef DEBUG
53#include <asl.h>
54#endif
55
56#define IPV6_ADDR_LEN 16
57#define IPV4_ADDR_LEN 4
58
59typedef si_item_t *(*od_extract_t)(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat);
60
61/* notify SPI */
62uint32_t notify_peek(int token, uint32_t *val);
63
64typedef struct
65{
66	int notify_token_global;
67	int notify_token_user;
68	int notify_token_group;
69	int notify_token_service;
70} ds_si_private_t;
71
72extern uint32_t gL1CacheEnabled;
73extern int _si_opendirectory_disabled;
74
75static pthread_key_t _ds_serv_cache_key = 0;
76static xpc_pipe_t __od_pipe;	/* use accessor only */
77static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
78
79mach_port_t _ds_port;
80
81static void
82_od_fork_child(void)
83{
84	// re-enable opendirectory interaction since we forked
85	_si_opendirectory_disabled = 0;
86
87	if (__od_pipe != NULL) {
88		xpc_pipe_invalidate(__od_pipe);
89		/* disable release due to 10649340, it will cause a minor leak for each fork without exec */
90		// xpc_release(__od_pipe);
91		__od_pipe = NULL;
92	}
93	_ds_port = MACH_PORT_NULL;
94	pthread_mutex_unlock(&mutex);
95}
96
97static void
98_od_fork_prepare(void)
99{
100	pthread_mutex_lock(&mutex);
101}
102
103static void
104_od_fork_parent(void)
105{
106	pthread_mutex_unlock(&mutex);
107}
108
109static void
110_ds_serv_cache_free(void *x)
111{
112	if (x != NULL) si_item_release(x);
113}
114
115void
116_si_disable_opendirectory(void)
117{
118	_si_opendirectory_disabled = 1;
119	_ds_port = MACH_PORT_NULL;
120}
121
122XPC_RETURNS_RETAINED
123static xpc_pipe_t
124_od_xpc_pipe(bool resetPipe)
125{
126	static dispatch_once_t once;
127	xpc_pipe_t result = NULL;
128
129	dispatch_once(&once, ^(void) {
130		char *xbs_disable;
131
132		/* if this is a build environment we ignore opendirectoryd */
133		xbs_disable = getenv("XBS_DISABLE_LIBINFO");
134		if ((issetugid() == 0) && (xbs_disable != NULL) && (strcmp(xbs_disable, "YES") == 0)) {
135			_si_opendirectory_disabled = 1;
136			return;
137		}
138
139		pthread_atfork(_od_fork_prepare, _od_fork_parent, _od_fork_child);
140	});
141
142	if (_si_opendirectory_disabled == 1) {
143		return NULL;
144	}
145
146	pthread_mutex_lock(&mutex);
147	if (resetPipe) {
148		xpc_release(__od_pipe);
149		__od_pipe = NULL;
150	}
151
152	if (__od_pipe == NULL) {
153		if (!issetugid() && getenv("OD_DEBUG_MODE") != NULL) {
154			__od_pipe = xpc_pipe_create(kODMachLibinfoPortNameDebug, 0);
155		} else {
156			__od_pipe = xpc_pipe_create(kODMachLibinfoPortName, XPC_PIPE_FLAG_PRIVILEGED);
157		}
158	}
159
160	if (__od_pipe != NULL) result = xpc_retain(__od_pipe);
161	pthread_mutex_unlock(&mutex);
162
163	return result;
164}
165
166static bool
167_od_running(void)
168{
169	xpc_pipe_t pipe;
170
171	pipe = _od_xpc_pipe(false);
172	if (pipe != NULL) {
173		xpc_release(pipe);
174	}
175
176	if (_si_opendirectory_disabled) {
177		return 0;
178	}
179
180	return (pipe != NULL);
181}
182
183static void
184_ds_child(void)
185{
186	_ds_port = MACH_PORT_NULL;
187}
188
189int
190_ds_running(void)
191{
192	kern_return_t status;
193	char *od_debug_mode = NULL;
194
195	if (_ds_port != MACH_PORT_NULL) return 1;
196
197	if (_si_opendirectory_disabled) return 0;
198	pthread_atfork(NULL, NULL, _ds_child);
199
200	if (!issetugid()) {
201		od_debug_mode = getenv("OD_DEBUG_MODE");
202	}
203
204	if (od_debug_mode) {
205		status = bootstrap_look_up(bootstrap_port, kDSStdMachDSLookupPortName "_debug", &_ds_port);
206	} else {
207		status = bootstrap_look_up2(bootstrap_port, kDSStdMachDSLookupPortName, &_ds_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
208	}
209	if ((status != BOOTSTRAP_SUCCESS) && (status != BOOTSTRAP_UNKNOWN_SERVICE)) _ds_port = MACH_PORT_NULL;
210
211	return (_ds_port != MACH_PORT_NULL);
212}
213
214static bool
215_valid_token(xpc_object_t reply)
216{
217	audit_token_t token;
218
219	/*
220	 * This should really call audit_token_to_au32,
221	 * but that's in libbsm, not in a Libsystem library.
222	 */
223	xpc_dictionary_get_audit_token(reply, &token);
224
225	return ((uid_t) token.val[1] == 0);
226}
227
228static void
229_ds_get_validation(si_mod_t *si, uint64_t *a, uint64_t *b, int cat)
230{
231	ds_si_private_t *pp;
232	uint32_t peek;
233	int status;
234
235	if (si == NULL) return;
236
237	pp = (ds_si_private_t *)si->private;
238	if (pp == NULL) return;
239
240	if (a != NULL)
241	{
242		*a = 0;
243		status = notify_peek(pp->notify_token_global, &peek);
244		if (status == NOTIFY_STATUS_OK) *a = ntohl(peek);
245	}
246
247	if (b != NULL)
248	{
249		*b = 0;
250		peek = 0;
251		status = NOTIFY_STATUS_FAILED;
252
253		if (cat == CATEGORY_USER) status = notify_peek(pp->notify_token_user, &peek);
254		else if (cat == CATEGORY_GROUP) status = notify_peek(pp->notify_token_group, &peek);
255		else if (cat == CATEGORY_GROUPLIST) status = notify_peek(pp->notify_token_group, &peek);
256		else if (cat == CATEGORY_SERVICE) status = notify_peek(pp->notify_token_service, &peek);
257
258		if (status == NOTIFY_STATUS_OK) *b = ntohl(peek);
259	}
260}
261
262XPC_RETURNS_RETAINED
263__private_extern__ xpc_object_t
264_od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool))
265{
266	xpc_object_t result = NULL;
267	xpc_object_t reply;
268	xpc_pipe_t od_pipe;
269	int retries, rc;
270
271	od_pipe = get_pipe(false);
272	if (od_pipe == NULL) return NULL;
273
274	if (payload == NULL) {
275		payload = xpc_dictionary_create(NULL, NULL, 0);
276	}
277
278	// we nest it for backward compatibility so we can do independent submissions
279	xpc_dictionary_set_string(payload, OD_RPC_NAME, procname);
280	xpc_dictionary_set_int64(payload, OD_RPC_VERSION, 2);
281
282	for (retries = 0; od_pipe != NULL && retries < 2; retries++) {
283		rc = xpc_pipe_routine(od_pipe, payload, &reply);
284		switch (rc) {
285			case EPIPE:
286				xpc_release(od_pipe);
287				od_pipe = get_pipe(true);
288				break;
289
290			case EAGAIN:
291				/* just loop and try to send again */
292				break;
293
294			case 0:
295				if (_valid_token(reply) == true) {
296					result = reply;
297				}
298				/* fall through since we got a valid response */
299
300			default:
301				/* release and NULL the pipe it'll break the loop */
302				xpc_release(od_pipe);
303				od_pipe = NULL;
304				break;
305		}
306	}
307
308	if (od_pipe != NULL) {
309		xpc_release(od_pipe);
310	}
311
312	return result;
313}
314
315static si_list_t *
316_ds_list(si_mod_t *si, int cat, const char *procname, const void *extra, od_extract_t extract)
317{
318	__block si_list_t *list;
319	uint64_t va, vb;
320	xpc_object_t reply, result;
321
322	if (procname == NULL) return NULL;
323
324	_ds_get_validation(si, &va, &vb, cat);
325
326	list = NULL;
327	reply = _od_rpc_call(procname, NULL, _od_xpc_pipe);
328	if (reply != NULL) {
329		result = xpc_dictionary_get_value(reply, OD_RPC_RESULT);
330		if (result != NULL && xpc_get_type(result) == XPC_TYPE_ARRAY) {
331			xpc_array_apply(result, ^bool(size_t index, xpc_object_t value) {
332				si_item_t *item = extract(si, value, extra, va, vb);
333				list = si_list_add(list, item);
334				si_item_release(item);
335
336				return true;
337			});
338		}
339
340		xpc_release(reply);
341	}
342
343	return list;
344}
345
346static si_item_t *
347_ds_item(si_mod_t *si, int cat, const char *procname, const void *extra, od_extract_t extract, xpc_object_t payload)
348{
349	xpc_object_t result;
350	uint64_t va, vb;
351	si_item_t *item = NULL;
352
353	if (procname == NULL) return NULL;
354
355	result = _od_rpc_call(procname, payload, _od_xpc_pipe);
356	if (result != NULL) {
357		_ds_get_validation(si, &va, &vb, cat);
358		if (xpc_dictionary_get_int64(result, OD_RPC_ERROR) == 0) {
359			item = extract(si, result, extra, va, vb);
360		}
361
362		xpc_release(result);
363	}
364
365	return item;
366}
367
368static int
369_ds_is_valid(si_mod_t *si, si_item_t *item)
370{
371	si_mod_t *src;
372	ds_si_private_t *pp;
373	int status;
374	uint32_t oldval, newval;
375
376	if (si == NULL) return 0;
377	if (item == NULL) return 0;
378	if (si->name == NULL) return 0;
379	if (item->src == NULL) return 0;
380
381	pp = (ds_si_private_t *)si->private;
382	if (pp == NULL) return 0;
383
384	src = (si_mod_t *)item->src;
385
386	if (src->name == NULL) return 0;
387	if (string_not_equal(si->name, src->name)) return 0;
388
389	/* check global invalidation */
390	oldval = item->validation_a;
391	newval = -1;
392	status = notify_peek(pp->notify_token_global, &newval);
393	if (status != NOTIFY_STATUS_OK) return 0;
394
395	newval = ntohl(newval);
396	if (oldval != newval) return 0;
397
398	oldval = item->validation_b;
399	newval = -1;
400	if (item->type == CATEGORY_USER) status = notify_peek(pp->notify_token_user, &newval);
401	else if (item->type == CATEGORY_GROUP) status = notify_peek(pp->notify_token_group, &newval);
402	else if (item->type == CATEGORY_SERVICE) status = notify_peek(pp->notify_token_service, &newval);
403	else return 0;
404
405	if (status != NOTIFY_STATUS_OK) return 0;
406
407	newval = ntohl(newval);
408	if (oldval != newval) return 0;
409
410	return 1;
411}
412
413static void
414_free_addr_list(char **l)
415{
416	int i;
417
418	if (l == NULL) return;
419	for (i = 0; l[i] != NULL; i++) free(l[i]);
420	free(l);
421}
422
423/* map ipv4 addresses and append to v6 list */
424static int
425_map_v4(char ***v6, uint32_t n6, char **v4, uint32_t n4)
426{
427	struct in6_addr a6;
428	uint32_t i;
429
430	a6.__u6_addr.__u6_addr32[0] = 0x00000000;
431	a6.__u6_addr.__u6_addr32[1] = 0x00000000;
432	a6.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
433
434	if (*v6 == NULL)
435	{
436		*v6 = (char **)calloc(n4 + 1, sizeof(char *));
437	}
438	else
439	{
440		*v6 = (char **)reallocf(*v6, (n6 + n4 + 1) * sizeof(char *));
441	}
442
443	if (*v6 == NULL) return -1;
444
445	for (i = 0; i < n4; i++)
446	{
447		(*v6)[n6] = (char *)calloc(1, IPV6_ADDR_LEN);
448		if ((*v6)[n6] == NULL) return -1;
449
450		memcpy(&(a6.__u6_addr.__u6_addr32[3]), v4[i], IPV4_ADDR_LEN);
451		memcpy((*v6)[n6], &(a6.__u6_addr.__u6_addr32[0]), IPV6_ADDR_LEN);
452
453		n6++;
454	}
455
456	return 0;
457}
458
459static xpc_object_t
460_xpc_query_key_string(const char *key, const char *value)
461{
462	xpc_object_t payload;
463
464	if (value == NULL) return NULL;
465
466	payload = xpc_dictionary_create(NULL, NULL, 0);
467	if (payload == NULL) return NULL;
468
469	xpc_dictionary_set_string(payload, key, value);
470
471	return payload;
472}
473
474static xpc_object_t
475_xpc_query_key_id(const char *key, id_t idValue)
476{
477	xpc_object_t payload;
478
479	payload = xpc_dictionary_create(NULL, NULL, 0);
480	if (payload == NULL) return NULL;
481
482	xpc_dictionary_set_int64(payload, key, idValue);
483
484	return payload;
485}
486
487static xpc_object_t
488_xpc_query_key_uuid(const char *key, uuid_t uu)
489{
490	xpc_object_t payload;
491
492	payload = xpc_dictionary_create(NULL, NULL, 0);
493	if (payload == NULL) return NULL;
494
495	xpc_dictionary_set_uuid(payload, key, uu);
496
497	return payload;
498}
499
500static xpc_object_t
501_xpc_query_key_int(const char *key, int64_t intValue)
502{
503	xpc_object_t payload;
504
505	payload = xpc_dictionary_create(NULL, NULL, 0);
506	if (payload == NULL) return NULL;
507
508	xpc_dictionary_set_int64(payload, key, intValue);
509
510	return payload;
511}
512
513#pragma mark -
514
515static int
516_extract_string_from_xpc_array_index(xpc_object_t reply, int index, const char **str)
517{
518	xpc_object_t value;
519
520	if (xpc_array_get_count(reply) < index) return -1;
521
522	value = xpc_array_get_value(reply, index);
523	if (xpc_get_type(value) != XPC_TYPE_STRING) return -1;
524
525	*str = xpc_string_get_string_ptr(value);
526	return 0;
527}
528
529static int
530_extract_string_from_xpc_object(xpc_object_t value, const char **str)
531{
532	if (value == NULL) return -1;
533	else if (xpc_get_type(value) == XPC_TYPE_STRING)
534	{
535		*str = xpc_string_get_string_ptr(value);
536		return 0;
537	}
538	else if (xpc_get_type(value) == XPC_TYPE_ARRAY)
539	{
540		return _extract_string_from_xpc_array_index(value, 0, str);
541	}
542
543	return -1;
544}
545
546static int
547_extract_uint32_from_xpc_object(xpc_object_t value, uint32_t *val32)
548{
549	xpc_type_t type;
550
551	if (value == NULL) return -1;
552	type = xpc_get_type(value);
553
554	if (type == XPC_TYPE_STRING)
555	{
556		*val32 = atoi(xpc_string_get_string_ptr(value));
557		return 0;
558	}
559	else if (type == XPC_TYPE_INT64)
560	{
561		*val32 = (uint32_t)xpc_int64_get_value(value);
562		return 0;
563	}
564	else if (type == XPC_TYPE_BOOL)
565	{
566		*val32 = (uint32_t)xpc_bool_get_value(value);
567		return 0;
568	}
569	else if (type == XPC_TYPE_ARRAY)
570	{
571		if (xpc_array_get_count(value) == 0) return -1;
572		return _extract_uint32_from_xpc_object(xpc_array_get_value(value, 0), val32);
573	}
574
575	return -1;
576}
577
578static int
579_extract_string_list_from_xpc_array_index(xpc_object_t reply, int index, unsigned int *len, char ***list)
580{
581	char **result;
582	xpc_object_t xpc_array = xpc_array_get_value(reply, index);
583
584	if ((xpc_array == NULL) || (xpc_get_type(xpc_array) != XPC_TYPE_ARRAY)) return -1;
585
586		result = calloc(xpc_array_get_count(xpc_array) + 1, sizeof(*result));
587	if (result == NULL) return -1;
588
589	/* include trailing NULL */
590	if (len != NULL) (*len) = xpc_array_get_count(xpc_array) + 1;
591
592	xpc_array_apply(xpc_array, ^bool(size_t idx, xpc_object_t value) {
593		result[idx] = (char *)xpc_string_get_string_ptr(value);
594		return true;
595	});
596
597	*list = result;
598	return 0;
599}
600
601static int
602_extract_uint32_from_xpc_array_index(xpc_object_t reply, int index, uint32_t *val32)
603{
604	xpc_object_t value = xpc_array_get_value(reply, index);
605	return _extract_uint32_from_xpc_object(value, val32);
606}
607
608static int
609_extract_string_list_from_xpc_array(xpc_object_t xpc_array, unsigned int *len, char ***list)
610{
611	char **result;
612
613	if ((xpc_array == NULL) || (xpc_get_type(xpc_array) != XPC_TYPE_ARRAY)) return -1;
614
615		result = calloc(xpc_array_get_count(xpc_array) + 1, sizeof(*result));
616	if (result == NULL) return -1;
617
618	/* include trailing NULL */
619	if (len != NULL) (*len) = xpc_array_get_count(xpc_array) + 1;
620
621	xpc_array_apply(xpc_array, ^bool(size_t idx, xpc_object_t value) {
622		result[idx] = (char *)xpc_string_get_string_ptr(value);
623		return true;
624	});
625
626	*list = result;
627	return 0;
628}
629
630static int
631_extract_string_from_xpc_dict(xpc_object_t reply, const char *key, const char **str)
632{
633	xpc_object_t value = xpc_dictionary_get_value(reply, key);
634	const char *result;
635
636	if (value == NULL) return -1;
637
638	if (xpc_get_type(value) != XPC_TYPE_STRING) return -1;
639
640	result = xpc_string_get_string_ptr(value);
641	if (result == NULL) return -1;
642
643	*str = result;
644	return 0;
645}
646
647static int
648_extract_uint32_from_xpc_dict(xpc_object_t reply, const char *key, uint32_t *val32)
649{
650	xpc_object_t value = xpc_dictionary_get_value(reply, key);
651	return _extract_uint32_from_xpc_object(value, val32);
652}
653
654#pragma mark -
655
656/*
657 * user schema
658 *
659 *				name	: string
660 *				passwd	: string
661 *				uid		: uint32
662 *				gid		: uint32
663 *				gecos	: string
664 *				dir		: string
665 *				shell	: string
666 */
667
668static si_item_t *
669_extract_user_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
670{
671	struct passwd tmp;
672	int i = 0;
673
674	if (xpc_array_get_count(reply) < 7) return NULL;
675
676	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_name)) return NULL;
677	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_passwd)) return NULL;
678	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.pw_uid)) return NULL;
679	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.pw_gid)) return NULL;
680	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_gecos)) return NULL;
681	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_dir)) return NULL;
682	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_shell)) return NULL;
683
684	/* default values */
685	tmp.pw_change = (time_t)0;
686	tmp.pw_expire = (time_t)0;
687	tmp.pw_class = (char *)"";
688
689	return (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, valid_global, valid_cat, tmp.pw_name, tmp.pw_passwd, tmp.pw_uid, tmp.pw_gid, tmp.pw_change, tmp.pw_class, tmp.pw_gecos, tmp.pw_dir, tmp.pw_shell, tmp.pw_expire);
690}
691
692static si_item_t *
693_extract_user_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
694{
695	__block struct passwd tmp;
696	__block int status = 0;
697	__block int parts = 3;
698
699	tmp.pw_name = (char *)"";
700	tmp.pw_passwd = (char *)"*";
701	tmp.pw_uid = (uid_t)0;
702	tmp.pw_gid = (gid_t)0;
703	tmp.pw_change = (time_t)0;
704	tmp.pw_expire = (time_t)0;
705	tmp.pw_class = (char *)"";
706	tmp.pw_gecos = (char *)"";
707	tmp.pw_dir = (char *)"/var/empty";
708	tmp.pw_shell = (char *)"/usr/bin/false";
709
710	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
711		if (key == NULL) return true;
712		else if (!strcmp(key, "pw_name"))
713		{
714			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_name);
715			if (status == 0) parts--;
716		}
717		else if (!strcmp(key, "pw_passwd"))
718		{
719			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_passwd);
720			/* no parts check - this value is optional */
721		}
722		else if (!strcmp(key, "pw_uid"))
723		{
724			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_uid);
725			if (status == 0) parts--;
726		}
727		else if (!strcmp(key, "pw_gid"))
728		{
729			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_gid);
730			if (status == 0) parts--;
731		}
732		else if (!strcmp(key, "pw_change"))
733		{
734			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_change);
735			/* no parts check - this value is optional */
736		}
737		else if (!strcmp(key, "pw_expire"))
738		{
739			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_expire);
740			/* no parts check - this value is optional */
741		}
742		else if (!strcmp(key, "pw_class"))
743		{
744			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_class);
745			/* no parts check - this value is optional */
746		}
747		else if (!strcmp(key, "pw_gecos"))
748		{
749			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_gecos);
750			/* no parts check - this value is optional */
751		}
752		else if (!strcmp(key, "pw_dir"))
753		{
754			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_dir);
755			/* no parts check - this value is optional */
756		}
757		else if (!strcmp(key, "pw_shell"))
758		{
759			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_shell);
760			/* no parts check - this value is optional */
761		}
762		return true;
763	});
764
765	if ((status != 0) || (parts != 0)) return NULL;
766
767	return (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, valid_global, valid_cat, tmp.pw_name, tmp.pw_passwd, tmp.pw_uid, tmp.pw_gid, tmp.pw_change, tmp.pw_class, tmp.pw_gecos, tmp.pw_dir, tmp.pw_shell, tmp.pw_expire);
768}
769
770static si_item_t *
771_extract_user(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
772{
773	xpc_type_t type;
774
775	if (si == NULL) return NULL;
776	if (reply == NULL) return NULL;
777
778	type = xpc_get_type(reply);
779
780	if (type == XPC_TYPE_ARRAY) return _extract_user_array(si, reply, valid_global, valid_cat);
781	else if (type == XPC_TYPE_DICTIONARY) return _extract_user_dict(si, reply, valid_global, valid_cat);
782
783	return NULL;
784}
785
786/*
787 * group schema
788 *
789 *				name	: string
790 *				gid		: uint32
791 * optional		members	: array of string
792 *
793 */
794
795static si_item_t *
796_extract_group_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
797{
798	si_item_t *item;
799	struct group tmp;
800	int i = 0;
801	int arraycount = xpc_array_get_count(reply);
802
803	if ((arraycount < 2) || (arraycount > 3)) return NULL;
804
805	memset(&tmp, 0, sizeof(tmp));
806
807	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.gr_name)) return NULL;
808	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.gr_gid)) return NULL;
809
810	if (arraycount == 3)
811	{
812		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.gr_mem)) return NULL;
813	}
814
815	/* default value */
816	tmp.gr_passwd = (char *)"*";
817
818	item = (si_item_t *) LI_ils_create("L4488ss4*", (unsigned long)si, CATEGORY_GROUP, 1, valid_global, valid_cat, tmp.gr_name, tmp.gr_passwd, tmp.gr_gid, tmp.gr_mem);
819
820	free(tmp.gr_mem);
821
822	return item;
823}
824
825static si_item_t *
826_extract_group_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
827{
828	si_item_t *item;
829	__block struct group tmp;
830	__block int status = 0;
831	__block int parts = 2;
832
833	tmp.gr_name = (char *)"";
834	tmp.gr_passwd = (char *)"*";
835	tmp.gr_gid = (gid_t)0;
836	tmp.gr_mem = NULL;
837
838	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
839		if (key == NULL) return true;
840		else if (!strcmp(key, "gr_name"))
841		{
842			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.gr_name);
843			if (status == 0) parts--;
844		}
845		else if (!strcmp(key, "gr_passwd"))
846		{
847			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.gr_passwd);
848			/* no parts check - this value is optional */
849		}
850		else if (!strcmp(key, "gr_gid"))
851		{
852			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.gr_gid);
853			if (status == 0) parts--;
854		}
855		else if (!strcmp(key, "gr_mem"))
856		{
857			status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.gr_mem);
858			/* no parts check - this value is optional */
859		}
860		return true;
861	});
862
863	if ((status != 0) || (parts != 0))
864	{
865		free(tmp.gr_mem);
866		return NULL;
867	}
868
869	item = (si_item_t *) LI_ils_create("L4488ss4*", (unsigned long)si, CATEGORY_GROUP, 1, valid_global, valid_cat, tmp.gr_name, tmp.gr_passwd, tmp.gr_gid, tmp.gr_mem);
870
871	free(tmp.gr_mem);
872
873	return item;
874}
875
876static si_item_t *
877_extract_group(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
878{
879	xpc_type_t type;
880
881	if (si == NULL) return NULL;
882	if (reply == NULL) return NULL;
883
884	type = xpc_get_type(reply);
885	if (type == XPC_TYPE_ARRAY) return _extract_group_array(si, reply, valid_global, valid_cat);
886	else if (type == XPC_TYPE_DICTIONARY) return _extract_group_dict(si, reply, valid_global, valid_cat);
887
888	return NULL;
889}
890
891/*
892 * netgroup schema
893 *
894 *				host	: string
895 *				user	: string
896 *				domain	: string
897 *
898 */
899static si_item_t *
900_extract_netgroup_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
901{
902	const char *host, *user, *domain;
903	int i = 0;
904
905	if (xpc_array_get_count(reply) != 3) return NULL;
906
907	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&host)) return NULL;
908	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&user)) return NULL;
909	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&domain)) return NULL;
910
911	return (si_item_t *)LI_ils_create("L4488sss", (unsigned long)si, CATEGORY_NETGROUP, 1, valid_global, valid_cat, host, user, domain);
912}
913
914static si_item_t *
915_extract_netgroup_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
916{
917	__block const char *host = "";
918	__block const char *user = "";
919	__block const char *domain = "";
920	__block int status = 0;
921	__block int parts = 3;
922
923	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
924		if (key == NULL) return true;
925		else if (!strcmp(key, "host"))
926		{
927			status |= _extract_string_from_xpc_object(value, (const char **)&host);
928			if (status == 0) parts--;
929		}
930		else if (!strcmp(key, "user"))
931		{
932			status |= _extract_string_from_xpc_object(value, (const char **)&user);
933			if (status == 0) parts--;
934		}
935		else if (!strcmp(key, "domain"))
936		{
937			status |= _extract_string_from_xpc_object(value, (const char **)&domain);
938			if (status == 0) parts--;
939		}
940		return true;
941	});
942
943	if ((status != 0) || (parts != 0)) return NULL;
944
945	return (si_item_t *)LI_ils_create("L4488sss", (unsigned long)si, CATEGORY_NETGROUP, 1, valid_global, valid_cat, host, user, domain);
946}
947
948static si_item_t *
949_extract_netgroup(si_mod_t *si, xpc_object_t reply, const void *ignored, uint64_t valid_global, uint64_t valid_cat)
950{
951	xpc_type_t type;
952
953	if (si == NULL) return NULL;
954	if (reply == NULL) return NULL;
955
956	type = xpc_get_type(reply);
957	if (type == XPC_TYPE_ARRAY) return _extract_netgroup_array(si, reply, valid_global, valid_cat);
958	else if (type == XPC_TYPE_DICTIONARY) return _extract_netgroup_dict(si, reply, valid_global, valid_cat);
959
960	return NULL;
961}
962
963/*
964 * alias schema
965 *
966 *				name	: string
967 *				local	: uint32
968 * optional		members	: array of string
969 *
970 */
971
972static si_item_t *
973_extract_alias_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
974{
975	si_item_t *item;
976	struct aliasent tmp;
977	int i = 0;
978	int arraycount = xpc_array_get_count(reply);
979
980	if ((arraycount < 2) || (arraycount > 3)) return NULL;
981
982	memset(&tmp, 0, sizeof(tmp));
983
984	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.alias_name)) return NULL;
985	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.alias_local)) return NULL;
986
987	if (arraycount == 3)
988	{
989		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.alias_members)) return NULL;
990	}
991
992	item = (si_item_t *)LI_ils_create("L4488s4*4", (unsigned long)si, CATEGORY_ALIAS, 1, valid_global, valid_cat, tmp.alias_name, tmp.alias_members_len, tmp.alias_members, tmp.alias_local);
993
994	free(tmp.alias_members);
995
996	return item;
997}
998
999static si_item_t *
1000_extract_alias_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1001{
1002	si_item_t *item;
1003	__block struct aliasent tmp;
1004	__block int status = 0;
1005	__block int parts = 2;
1006
1007	tmp.alias_name = (char *)"";
1008	tmp.alias_local = 0;
1009	tmp.alias_members = NULL;
1010	tmp.alias_members_len = 0;
1011
1012	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1013		if (key == NULL) return true;
1014		else if (!strcmp(key, "alias_name"))
1015		{
1016			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.alias_name);
1017			if (status == 0) parts--;
1018		}
1019		else if (!strcmp(key, "alias_local"))
1020		{
1021			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.alias_local);
1022			if (status == 0) parts--;
1023		}
1024		else if (!strcmp(key, "alias_members"))
1025		{
1026			status |= _extract_string_list_from_xpc_array(value, &tmp.alias_members_len, (char ***)&tmp.alias_members);
1027			/* no parts check - this value is optional */
1028		}
1029		return true;
1030	});
1031
1032	if ((status != 0) || (parts != 0))
1033	{
1034		free(tmp.alias_members);
1035		return NULL;
1036	}
1037
1038	item = (si_item_t *)LI_ils_create("L4488s4*4", (unsigned long)si, CATEGORY_ALIAS, 1, valid_global, valid_cat, tmp.alias_name, tmp.alias_members_len, tmp.alias_members, tmp.alias_local);
1039
1040	free(tmp.alias_members);
1041
1042	return item;
1043}
1044
1045static si_item_t *
1046_extract_alias(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1047{
1048	xpc_type_t type;
1049
1050	if (si == NULL) return NULL;
1051	if (reply == NULL) return NULL;
1052
1053	type = xpc_get_type(reply);
1054	if (type == XPC_TYPE_ARRAY) return _extract_alias_array(si, reply, valid_global, valid_cat);
1055	else if (type == XPC_TYPE_DICTIONARY) return _extract_alias_dict(si, reply, valid_global, valid_cat);
1056
1057	return NULL;
1058}
1059
1060/*
1061 * network schema
1062 *
1063 *				name	: string
1064 *				net		: uint32
1065 * optional		aliases	: array of string
1066 *
1067 */
1068
1069static si_item_t *
1070_extract_network_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1071{
1072	si_item_t *item;
1073	struct netent tmp;
1074	int i = 0;
1075	int arraycount = xpc_array_get_count(reply);
1076
1077	if ((arraycount < 2) || (arraycount > 3)) return NULL;
1078
1079	memset(&tmp, 0, sizeof(tmp));
1080
1081	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.n_name)) return NULL;
1082	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.n_net)) return NULL;
1083
1084	if (arraycount == 3)
1085	{
1086		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.n_aliases)) return NULL;
1087	}
1088
1089	/* default value */
1090	tmp.n_addrtype = AF_INET;
1091
1092	item = (si_item_t *)LI_ils_create("L4488s*44", (unsigned long)si, CATEGORY_NETWORK, 1, valid_global, valid_cat, tmp.n_name, tmp.n_aliases, tmp.n_addrtype, tmp.n_net);
1093
1094	free(tmp.n_aliases);
1095
1096	return item;
1097}
1098
1099static si_item_t *
1100_extract_network_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1101{
1102	si_item_t *item;
1103	__block struct netent tmp;
1104	__block int status = 0;
1105	__block int parts = 2;
1106
1107	if (si == NULL) return NULL;
1108	if (reply == NULL) return NULL;
1109
1110	tmp.n_name = (char *)"";
1111	tmp.n_aliases = NULL;
1112	tmp.n_net = 0;
1113
1114	/* default value */
1115	tmp.n_addrtype = AF_INET;
1116
1117	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1118		if (key == NULL) return true;
1119		else if (!strcmp(key, "n_name"))
1120		{
1121			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.n_name);
1122			if (status == 0) parts--;
1123		}
1124		else if (!strcmp(key, "n_aliases"))
1125		{
1126			status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.n_aliases);
1127			/* no parts check - this value is optional */
1128		}
1129		else if (!strcmp(key, "n_net"))
1130		{
1131			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.n_net);
1132			if (status == 0) parts--;
1133		}
1134		return true;
1135	});
1136
1137	if ((status != 0) || (parts != 0))
1138	{
1139		free(tmp.n_aliases);
1140		return NULL;
1141	}
1142
1143	item = (si_item_t *)LI_ils_create("L4488s*44", (unsigned long)si, CATEGORY_NETWORK, 1, valid_global, valid_cat, tmp.n_name, tmp.n_aliases, tmp.n_addrtype, tmp.n_net);
1144
1145	free(tmp.n_aliases);
1146
1147	return item;
1148}
1149
1150static si_item_t *
1151_extract_network(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1152{
1153	xpc_type_t type;
1154
1155	if (si == NULL) return NULL;
1156	if (reply == NULL) return NULL;
1157
1158	type = xpc_get_type(reply);
1159	if (type == XPC_TYPE_ARRAY) return _extract_network_array(si, reply, valid_global, valid_cat);
1160	else if (type == XPC_TYPE_DICTIONARY) return _extract_network_dict(si, reply, valid_global, valid_cat);
1161
1162	return NULL;
1163}
1164
1165/*
1166 * service schema
1167 *
1168 *				name	: string
1169 *				port	: uint32
1170 *				proto	: string
1171 * optional		aliases	: array of string
1172 *
1173 */
1174
1175static si_item_t *
1176_extract_service_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1177{
1178	si_item_t *item;
1179	struct servent tmp;
1180	int i = 0;
1181	int arraycount = xpc_array_get_count(reply);
1182
1183	if ((arraycount < 3) || (arraycount > 4)) return NULL;
1184
1185	memset(&tmp, 0, sizeof(tmp));
1186
1187	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.s_name)) return NULL;
1188	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.s_port)) return NULL;
1189	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.s_proto)) return NULL;
1190
1191	if (arraycount == 4)
1192	{
1193		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.s_aliases)) return NULL;
1194	}
1195
1196	item = (si_item_t *)LI_ils_create("L4488s*4s", (unsigned long)si, CATEGORY_SERVICE, 1, valid_global, valid_cat, tmp.s_name, tmp.s_aliases, tmp.s_port, tmp.s_proto);
1197
1198	free(tmp.s_aliases);
1199
1200	return item;
1201}
1202
1203static si_item_t *
1204_extract_service_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1205{
1206	si_item_t *item;
1207	__block struct servent tmp;
1208	__block int status = 0;
1209	__block int parts = 3;
1210
1211	if (si == NULL) return NULL;
1212	if (reply == NULL) return NULL;
1213
1214	tmp.s_name = (char *)"";
1215	tmp.s_aliases = NULL;
1216	tmp.s_port = 0;
1217	tmp.s_proto = (char *)"";
1218
1219	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1220		if (key == NULL) return true;
1221		else if (!strcmp(key, "s_name"))
1222		{
1223			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.s_name);
1224			if (status == 0) parts--;
1225		}
1226		else if (!strcmp(key, "s_aliases"))
1227		{
1228			status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.s_aliases);
1229			/* no parts check - this value is optional */
1230		}
1231		else if (!strcmp(key, "s_port"))
1232		{
1233			uint32_t v32;
1234			status |= _extract_uint32_from_xpc_object(value, &v32);
1235			if (status == 0)
1236			{
1237				tmp.s_port = (unsigned int)htons(v32); // ugh
1238				parts--;
1239			}
1240		}
1241		else if (!strcmp(key, "s_proto"))
1242		{
1243			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.s_proto);
1244			if (status == 0) parts--;
1245		}
1246		return true;
1247	});
1248
1249	if ((status != 0) || (parts != 0))
1250	{
1251		free(tmp.s_aliases);
1252		return NULL;
1253	}
1254
1255	item = (si_item_t *)LI_ils_create("L4488s*4s", (unsigned long)si, CATEGORY_SERVICE, 1, valid_global, valid_cat, tmp.s_name, tmp.s_aliases, tmp.s_port, tmp.s_proto);
1256
1257	free(tmp.s_aliases);
1258
1259	return item;
1260}
1261
1262static si_item_t *
1263_extract_service(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1264{
1265	xpc_type_t type;
1266
1267	if (si == NULL) return NULL;
1268	if (reply == NULL) return NULL;
1269
1270	type = xpc_get_type(reply);
1271	if (type == XPC_TYPE_ARRAY) return _extract_service_array(si, reply, valid_global, valid_cat);
1272	else if (type == XPC_TYPE_DICTIONARY) return _extract_service_dict(si, reply, valid_global, valid_cat);
1273
1274	return NULL;
1275}
1276
1277/*
1278 * protocol schema
1279 *
1280 *				name	: string
1281 *				proto	: uint32
1282 * optional		aliases	: array of string
1283 *
1284 */
1285static si_item_t *
1286_extract_protocol_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1287{
1288	si_item_t *item;
1289	struct protoent tmp;
1290	int i = 0;
1291	int arraycount = xpc_array_get_count(reply);
1292
1293	if ((arraycount < 2) || (arraycount > 3)) return NULL;
1294
1295	memset(&tmp, 0, sizeof(tmp));
1296
1297	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.p_name)) return NULL;
1298	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.p_proto)) return NULL;
1299
1300	if (arraycount == 3)
1301	{
1302		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.p_aliases)) return NULL;
1303	}
1304
1305	item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_PROTOCOL, 1, valid_global, valid_cat, tmp.p_name, tmp.p_aliases, tmp.p_proto);
1306
1307	free(tmp.p_aliases);
1308
1309	return item;
1310}
1311
1312static si_item_t *
1313_extract_protocol_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1314{
1315	si_item_t *item;
1316	__block struct protoent tmp;
1317	__block int status = 0;
1318	__block int parts = 2;
1319
1320	tmp.p_name = (char *)"";
1321	tmp.p_proto = 0;
1322	tmp.p_aliases = NULL;
1323
1324	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1325		if (key == NULL) return true;
1326		else if (!strcmp(key, "p_name"))
1327		{
1328			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.p_name);
1329			if (status == 0) parts--;
1330		}
1331		else if (!strcmp(key, "p_proto"))
1332		{
1333			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.p_proto);
1334			if (status == 0) parts--;
1335		}
1336		else if (!strcmp(key, "p_aliases"))
1337		{
1338			status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.p_aliases);
1339			/* no parts check - this value is optional */
1340		}
1341		return true;
1342	});
1343
1344	if ((status != 0) || (parts != 0))
1345	{
1346		free(tmp.p_aliases);
1347		return NULL;
1348	}
1349
1350	item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_PROTOCOL, 1, valid_global, valid_cat, tmp.p_name, tmp.p_aliases, tmp.p_proto);
1351
1352	free(tmp.p_aliases);
1353
1354	return item;
1355}
1356
1357static si_item_t *
1358_extract_protocol(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1359{
1360	xpc_type_t type;
1361
1362	if (si == NULL) return NULL;
1363	if (reply == NULL) return NULL;
1364
1365	type = xpc_get_type(reply);
1366	if (type == XPC_TYPE_ARRAY) return _extract_protocol_array(si, reply, valid_global, valid_cat);
1367	else if (type == XPC_TYPE_DICTIONARY) return _extract_protocol_dict(si, reply, valid_global, valid_cat);
1368
1369	return NULL;
1370}
1371
1372/*
1373 * rpc schema
1374 *
1375 *				name	: string
1376 *				number	: uint32
1377 * optional		aliases	: array of string
1378 *
1379 */
1380
1381static si_item_t *
1382_extract_rpc_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1383{
1384	si_item_t *item;
1385	struct rpcent tmp;
1386	int i = 0;
1387	int arraycount = xpc_array_get_count(reply);
1388
1389	if ((arraycount < 2) || (arraycount > 3)) return NULL;
1390
1391	memset(&tmp, 0, sizeof(tmp));
1392
1393	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.r_name)) return NULL;
1394	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.r_number)) return NULL;
1395
1396	if (arraycount == 3)
1397	{
1398		if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.r_aliases)) return NULL;
1399	}
1400
1401	item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_RPC, 1, valid_global, valid_cat, tmp.r_name, tmp.r_aliases, tmp.r_number);
1402
1403	free(tmp.r_aliases);
1404
1405	return item;
1406}
1407
1408static si_item_t *
1409_extract_rpc_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1410{
1411	si_item_t *item;
1412	__block struct rpcent tmp;
1413	__block int status = 0;
1414	__block int parts = 2;
1415
1416	tmp.r_name = (char *)"";
1417	tmp.r_number = 0;
1418	tmp.r_aliases = NULL;
1419
1420	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1421		if (key == NULL) return true;
1422		else if (!strcmp(key, "r_name"))
1423		{
1424			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.r_name);
1425			if (status == 0) parts--;
1426		}
1427		else if (!strcmp(key, "r_number"))
1428		{
1429			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.r_number);
1430			if (status == 0) parts--;
1431		}
1432		else if (!strcmp(key, "r_aliases"))
1433		{
1434			status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.r_aliases);
1435			/* no parts check - this value is optional */
1436		}
1437		return true;
1438	});
1439
1440	if ((status != 0) || (parts != 0))
1441	{
1442		free(tmp.r_aliases);
1443		return NULL;
1444	}
1445
1446	item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_RPC, 1, valid_global, valid_cat, tmp.r_name, tmp.r_aliases, tmp.r_number);
1447
1448	free(tmp.r_aliases);
1449
1450	return item;
1451}
1452
1453static si_item_t *
1454_extract_rpc(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1455{
1456	xpc_type_t type;
1457
1458	if (si == NULL) return NULL;
1459	if (reply == NULL) return NULL;
1460
1461	type = xpc_get_type(reply);
1462	if (type == XPC_TYPE_ARRAY) return _extract_rpc_array(si, reply, valid_global, valid_cat);
1463	else if (type == XPC_TYPE_DICTIONARY) return _extract_rpc_dict(si, reply, valid_global, valid_cat);
1464
1465	return NULL;
1466}
1467
1468/*
1469 * fstab schema
1470 *
1471 *				file	: string
1472 *				spec	: string
1473 *				freq	: uint32
1474 *				passno	: uint32
1475 *				mntopts	: string
1476 *				type	: string
1477 *				vfstype	: string
1478 */
1479
1480static si_item_t *
1481_extract_fstab_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1482{
1483	__block struct fstab tmp;
1484	int i = 0;
1485
1486	if (xpc_array_get_count(reply) != 7) return NULL;
1487
1488	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_file)) return NULL;
1489	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_spec)) return NULL;
1490	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.fs_freq)) return NULL;
1491	if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.fs_passno)) return NULL;
1492	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_mntops)) return NULL;
1493	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_type)) return NULL;
1494	if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_vfstype)) return NULL;
1495
1496	return (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, valid_global, valid_cat, tmp.fs_spec, tmp.fs_file, tmp.fs_vfstype, tmp.fs_mntops, tmp.fs_type, tmp.fs_freq, tmp.fs_passno);
1497}
1498
1499static si_item_t *
1500_extract_fstab_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1501{
1502	__block struct fstab tmp;
1503	__block int status = 0;
1504	__block int parts = 7;
1505
1506	tmp.fs_file = NULL;
1507	tmp.fs_spec = (char *)"";
1508	tmp.fs_freq = 0;
1509	tmp.fs_passno = 0;
1510	tmp.fs_mntops = (char *)"";
1511	tmp.fs_type = (char *)"";
1512	tmp.fs_vfstype = (char *)"";
1513
1514	xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1515		if (key == NULL) return true;
1516		else if (!strcmp(key, "fs_file"))
1517		{
1518			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_file);
1519			if (status == 0) parts--;
1520		}
1521		else if (!strcmp(key, "fs_spec"))
1522		{
1523			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_spec);
1524			if (status == 0) parts--;
1525		}
1526		else if (!strcmp(key, "fs_freq"))
1527		{
1528			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.fs_freq);
1529			if (status == 0) parts--;
1530		}
1531		else if (!strcmp(key, "fs_passno"))
1532		{
1533			status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.fs_passno);
1534			if (status == 0) parts--;
1535		}
1536		else if (!strcmp(key, "fs_mntops"))
1537		{
1538			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_mntops);
1539			if (status == 0) parts--;
1540		}
1541		else if (!strcmp(key, "fs_type"))
1542		{
1543			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_type);
1544			if (status == 0) parts--;
1545		}
1546		else if (!strcmp(key, "fs_vfstype"))
1547		{
1548			status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_vfstype);
1549			if (status == 0) parts--;
1550		}
1551		return true;
1552	});
1553
1554	if ((status != 0) || (parts != 0)) return NULL;
1555
1556	return (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, valid_global, valid_cat, tmp.fs_spec, tmp.fs_file, tmp.fs_vfstype, tmp.fs_mntops, tmp.fs_type, tmp.fs_freq, tmp.fs_passno);
1557}
1558
1559static si_item_t *
1560_extract_fstab(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1561{
1562	xpc_type_t type;
1563
1564	if (si == NULL) return NULL;
1565	if (reply == NULL) return NULL;
1566
1567	type = xpc_get_type(reply);
1568	if (type == XPC_TYPE_ARRAY) return _extract_fstab_array(si, reply, valid_global, valid_cat);
1569	else if (type == XPC_TYPE_DICTIONARY) return _extract_fstab_dict(si, reply, valid_global, valid_cat);
1570
1571	return NULL;
1572}
1573
1574static si_item_t *
1575_extract_mac_mac(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat)
1576{
1577	xpc_type_t type;
1578	char *cmac;
1579	const char *value = NULL;
1580	si_item_t *out;
1581
1582	if (si == NULL) return NULL;
1583	if (reply == NULL) return NULL;
1584	if (extra == NULL) return NULL;
1585
1586	type = xpc_get_type(reply);
1587	if (type == XPC_TYPE_ARRAY)
1588	{
1589		if (xpc_array_get_count(reply) >= 1)
1590		{
1591			if (0 != _extract_string_from_xpc_array_index(reply, 0, (const char **)&value)) return NULL;
1592		}
1593	}
1594	else if (type == XPC_TYPE_DICTIONARY)
1595	{
1596		if (0 != _extract_string_from_xpc_dict(reply, "mac", &value)) return NULL;
1597	}
1598
1599	if (value == NULL || value[0] == '\0') return NULL;
1600
1601	cmac = si_standardize_mac_address(value);
1602	if (cmac == NULL) return NULL;
1603
1604	out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_MAC, 1, valid_global, valid_cat, extra, cmac);
1605
1606	free(cmac);
1607
1608	return out;
1609}
1610
1611static si_item_t *
1612_extract_mac_name(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat)
1613{
1614	xpc_type_t type;
1615	const char *name = NULL;
1616	si_item_t *out;
1617
1618	if (si == NULL) return NULL;
1619	if (reply == NULL) return NULL;
1620	if (extra == NULL) return NULL;
1621
1622	type = xpc_get_type(reply);
1623	if (type == XPC_TYPE_ARRAY)
1624	{
1625		if (xpc_array_get_count(reply) >= 1)
1626		{
1627			if (0 != _extract_string_from_xpc_array_index(reply, 0, (const char **)&name )) return NULL;
1628		}
1629	}
1630	else if (type == XPC_TYPE_DICTIONARY)
1631	{
1632		if (0 != _extract_string_from_xpc_dict(reply, "name", &name)) return NULL;
1633	}
1634
1635	if (name == NULL) return NULL;
1636
1637	out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_MAC, 1, valid_global, valid_cat, name, extra);
1638
1639	return out;
1640}
1641
1642#pragma mark -
1643
1644static si_item_t *
1645ds_user_byname(si_mod_t *si, const char *name)
1646{
1647	xpc_object_t payload;
1648	si_item_t *item;
1649
1650	if (!_od_running()) return NULL;
1651
1652	payload = _xpc_query_key_string("name", name);
1653	if (payload == NULL) return NULL;
1654
1655	item = _ds_item(si, CATEGORY_USER, "getpwnam", NULL, _extract_user, payload);
1656
1657	xpc_release(payload);
1658	return item;
1659}
1660
1661static si_item_t *
1662ds_user_byuid(si_mod_t *si, uid_t uid)
1663{
1664	xpc_object_t payload;
1665	si_item_t *item;
1666
1667	if (!_od_running()) return NULL;
1668
1669	payload = _xpc_query_key_id("uid", uid);
1670	if (payload == NULL) return NULL;
1671
1672	item = _ds_item(si, CATEGORY_USER, "getpwuid", NULL, _extract_user, payload);
1673
1674	xpc_release(payload);
1675	return item;
1676}
1677
1678static si_item_t *
1679ds_user_byuuid(si_mod_t *si, uuid_t uuid)
1680{
1681	xpc_object_t payload;
1682	si_item_t *item;
1683
1684	if (!_od_running()) return NULL;
1685
1686	payload = _xpc_query_key_uuid("uuid", uuid);
1687	if (payload == NULL) return NULL;
1688
1689	item = _ds_item(si, CATEGORY_USER, "getpwuuid", NULL, _extract_user, payload);
1690
1691	xpc_release(payload);
1692	return item;
1693}
1694
1695static si_list_t *
1696ds_user_all(si_mod_t *si)
1697{
1698	return _ds_list(si, CATEGORY_USER, "getpwent", NULL, _extract_user);
1699}
1700
1701static si_item_t *
1702ds_group_byname(si_mod_t *si, const char *name)
1703{
1704	xpc_object_t payload;
1705	si_item_t *item;
1706
1707	if (!_od_running()) return NULL;
1708
1709	payload = _xpc_query_key_string("name", name);
1710	if (payload == NULL) return NULL;
1711
1712	item = _ds_item(si, CATEGORY_GROUP, "getgrnam", NULL, _extract_group, payload);
1713
1714	xpc_release(payload);
1715	return item;
1716}
1717
1718static si_item_t *
1719ds_group_bygid(si_mod_t *si, gid_t gid)
1720{
1721	xpc_object_t payload;
1722	si_item_t *item;
1723
1724	if (!_od_running()) return NULL;
1725
1726	payload = _xpc_query_key_id("gid", gid);
1727	if (payload == NULL) return NULL;
1728
1729	item = _ds_item(si, CATEGORY_GROUP, "getgrgid", NULL, _extract_group, payload);
1730
1731	xpc_release(payload);
1732	return item;
1733}
1734
1735static si_item_t *
1736ds_group_byuuid(si_mod_t *si, uuid_t uuid)
1737{
1738	xpc_object_t payload;
1739	si_item_t *item;
1740
1741	if (!_od_running()) return NULL;
1742
1743	payload = _xpc_query_key_uuid("uuid", uuid);
1744	if (payload == NULL) return NULL;
1745
1746	item = _ds_item(si, CATEGORY_GROUP, "getgruuid", NULL, _extract_group, payload);
1747
1748	xpc_release(payload);
1749	return item;
1750}
1751
1752static si_list_t *
1753ds_group_all(si_mod_t *si)
1754{
1755	if (!_od_running()) return NULL;
1756	return _ds_list(si, CATEGORY_GROUP, "getgrent", NULL, _extract_group);
1757}
1758
1759static si_item_t *
1760ds_grouplist(si_mod_t *si, const char *name, uint32_t ngroups)
1761{
1762	xpc_object_t payload, reply;
1763	si_item_t *item = NULL;
1764
1765	if (!_od_running()) return NULL;
1766	if (name == NULL) return NULL;
1767
1768	payload = xpc_dictionary_create(NULL, NULL, 0);
1769	if (payload == NULL) return NULL;
1770
1771	xpc_dictionary_set_string(payload, "name", name);
1772	xpc_dictionary_set_int64(payload, "ngroups", ngroups);
1773
1774	reply = _od_rpc_call("getgrouplist", payload, _od_xpc_pipe);
1775	if (reply != NULL) {
1776		size_t gidptrsz;
1777		const gid_t *gidptr = xpc_dictionary_get_data(reply, "groups", &gidptrsz);
1778		uint32_t count = 0;
1779		uint64_t va, vb;
1780
1781		_ds_get_validation(si, &va, &vb, CATEGORY_GROUPLIST);
1782
1783		/* see what we were sent */
1784		if (0 == _extract_uint32_from_xpc_dict(reply, "count", &count))
1785		{
1786			if (count != 0)
1787			{
1788				item = (si_item_t *)LI_ils_create("L4488s4@", (unsigned long)si, CATEGORY_GROUPLIST, 1, va, vb, name, count,
1789												  gidptrsz, gidptr);
1790			}
1791		}
1792
1793		xpc_release(reply);
1794	}
1795
1796	xpc_release(payload);
1797
1798	return item;
1799}
1800
1801static si_list_t *
1802ds_netgroup_byname(si_mod_t *si, const char *name)
1803{
1804	xpc_object_t payload;
1805	si_list_t *list = NULL;
1806	si_item_t *item;
1807
1808	if (!_od_running()) return NULL;
1809
1810	payload = _xpc_query_key_string("netgroup", name);
1811	if (payload == NULL) return NULL;
1812
1813	item = _ds_item(si, CATEGORY_NETGROUP, "getnetgrent", NULL, _extract_netgroup, payload);
1814	if (item != NULL) {
1815		list = si_list_add(list, item);
1816		si_item_release(item);
1817	}
1818
1819	xpc_release(payload);
1820
1821	return list;
1822}
1823
1824static int
1825ds_in_netgroup(si_mod_t *si, const char *group, const char *host, const char *user, const char *domain)
1826{
1827	xpc_object_t payload, reply;
1828	int is_innetgr;
1829
1830	if (!_od_running()) return 0;
1831
1832	payload = xpc_dictionary_create(NULL, NULL, 0);
1833	if (payload == NULL) return 0;
1834
1835	xpc_dictionary_set_string(payload, "netgroup", (group ? group : ""));
1836	xpc_dictionary_set_string(payload, "host", (host ? host : ""));
1837	xpc_dictionary_set_string(payload, "user", (user ? user : ""));
1838	xpc_dictionary_set_string(payload, "domain", (domain ? domain : ""));
1839
1840	reply = _od_rpc_call("innetgr", payload, _od_xpc_pipe);
1841	if (reply != NULL) {
1842		is_innetgr = xpc_dictionary_get_bool(reply, OD_RPC_RESULT);
1843		xpc_release(reply);
1844	} else {
1845		is_innetgr = 0;
1846	}
1847
1848	xpc_release(payload);
1849
1850	return is_innetgr;
1851}
1852
1853static si_item_t *
1854ds_alias_byname(si_mod_t *si, const char *name)
1855{
1856	xpc_object_t payload;
1857	si_item_t *item;
1858
1859	if (!_od_running()) return NULL;
1860
1861	payload = _xpc_query_key_string("name", name);
1862	if (payload == NULL) return NULL;
1863
1864	item = _ds_item(si, CATEGORY_ALIAS, "alias_getbyname", NULL, _extract_alias, payload);
1865
1866	xpc_release(payload);
1867	return item;
1868}
1869
1870static si_list_t *
1871ds_alias_all(si_mod_t *si)
1872{
1873	if (!_od_running()) return NULL;
1874	return _ds_list(si, CATEGORY_ALIAS, "alias_getent", NULL, _extract_alias);
1875}
1876
1877static si_item_t *
1878ds_network_byname(si_mod_t *si, const char *name)
1879{
1880	xpc_object_t payload;
1881	si_item_t *item;
1882
1883	if (!_od_running()) return NULL;
1884
1885	payload = _xpc_query_key_string("name", name);
1886	if (payload == NULL) return NULL;
1887
1888	item = _ds_item(si, CATEGORY_NETWORK, "getnetbyname", NULL, _extract_network, payload);
1889
1890	xpc_release(payload);
1891	return item;
1892}
1893
1894static si_item_t *
1895ds_network_byaddr(si_mod_t *si, uint32_t addr)
1896{
1897	unsigned char f1, f2, f3;
1898	char val[64];
1899	xpc_object_t payload;
1900	si_item_t *item;
1901
1902	if (!_od_running()) return NULL;
1903
1904	f1 = addr & 0xff;
1905	addr >>= 8;
1906	f2 = addr & 0xff;
1907	addr >>= 8;
1908	f3 = addr & 0xff;
1909
1910	if (f3 != 0) snprintf(val, sizeof(val), "%u.%u.%u", f3, f2, f1);
1911	else if (f2 != 0) snprintf(val, sizeof(val), "%u.%u", f2, f1);
1912	else snprintf(val, sizeof(val), "%u", f1);
1913
1914	payload = _xpc_query_key_string("net", val);
1915	if (payload == NULL) return NULL;
1916
1917	item = _ds_item(si, CATEGORY_NETWORK, "getnetbyaddr", NULL, _extract_network, payload);
1918
1919	xpc_release(payload);
1920	return item;
1921}
1922
1923static si_list_t *
1924ds_network_all(si_mod_t *si)
1925{
1926	if (!_od_running()) return NULL;
1927	return _ds_list(si, CATEGORY_NETWORK, "getnetent", NULL, _extract_network);
1928}
1929
1930static si_item_t *
1931ds_service_byname(si_mod_t *si, const char *name, const char *proto)
1932{
1933	xpc_object_t payload;
1934	si_item_t *item;
1935	struct servent *s;
1936
1937	if (!_od_running()) return NULL;
1938	if (name == NULL) name = "";
1939	if (proto == NULL) proto = "";
1940
1941	/* Check our local service cache (see ds_addrinfo). */
1942	item = pthread_getspecific(_ds_serv_cache_key);
1943	if (item != NULL)
1944	{
1945		s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
1946		if (string_equal(name, s->s_name)) return si_item_retain(item);
1947	}
1948
1949	payload = xpc_dictionary_create(NULL, NULL, 0);
1950	if (payload == NULL) return NULL;
1951
1952	xpc_dictionary_set_string(payload, "name", name);
1953	xpc_dictionary_set_string(payload, "proto", proto);
1954
1955	item = _ds_item(si, CATEGORY_SERVICE, "getservbyname", NULL, _extract_service, payload);
1956
1957	xpc_release(payload);
1958
1959	return item;
1960}
1961
1962static si_item_t *
1963ds_service_byport(si_mod_t *si, int port, const char *proto)
1964{
1965	xpc_object_t payload;
1966	si_item_t *item;
1967
1968	if (!_od_running()) return NULL;
1969
1970	payload = xpc_dictionary_create(NULL, NULL, 0);
1971	if (payload == NULL) return NULL;
1972
1973	/* swap to native order, API passes network order */
1974	xpc_dictionary_set_int64(payload, "port", ntohs(port));
1975	xpc_dictionary_set_string(payload, "proto", (proto ? proto : ""));
1976
1977	item = _ds_item(si, CATEGORY_SERVICE, "getservbyport", NULL, _extract_service, payload);
1978
1979	xpc_release(payload);
1980
1981	return item;
1982}
1983
1984static si_list_t *
1985ds_service_all(si_mod_t *si)
1986{
1987	if (!_od_running()) return NULL;
1988	return _ds_list(si, CATEGORY_SERVICE, "getservent", NULL, _extract_service);
1989}
1990
1991static si_item_t *
1992ds_protocol_byname(si_mod_t *si, const char *name)
1993{
1994	xpc_object_t payload;
1995	si_item_t *item;
1996
1997	if (!_od_running()) return NULL;
1998
1999	payload = _xpc_query_key_string("name", name);
2000	if (payload == NULL) return NULL;
2001
2002	item = _ds_item(si, CATEGORY_PROTOCOL, "getprotobyname", NULL, _extract_protocol, payload);
2003
2004	xpc_release(payload);
2005	return item;
2006}
2007
2008static si_item_t *
2009ds_protocol_bynumber(si_mod_t *si, int number)
2010{
2011	xpc_object_t payload;
2012	si_item_t *item;
2013
2014	if (!_od_running()) return NULL;
2015
2016	payload = _xpc_query_key_int("number", number);
2017	if (payload == NULL) return NULL;
2018
2019	item = _ds_item(si, CATEGORY_PROTOCOL, "getprotobynumber", NULL, _extract_protocol, payload);
2020
2021	xpc_release(payload);
2022	return item;
2023}
2024
2025static si_list_t *
2026ds_protocol_all(si_mod_t *si)
2027{
2028	return _ds_list(si, CATEGORY_PROTOCOL, "getprotoent", NULL, _extract_protocol);
2029}
2030
2031static si_item_t *
2032ds_rpc_byname(si_mod_t *si, const char *name)
2033{
2034	xpc_object_t payload;
2035	si_item_t *item;
2036
2037	if (!_od_running()) return NULL;
2038
2039	payload = _xpc_query_key_string("name", name);
2040	if (payload == NULL) return NULL;
2041
2042	item = _ds_item(si, CATEGORY_RPC, "getrpcbyname", NULL, _extract_rpc, payload);
2043
2044	xpc_release(payload);
2045	return item;
2046}
2047
2048static si_item_t *
2049ds_rpc_bynumber(si_mod_t *si, int number)
2050{
2051	xpc_object_t payload;
2052	si_item_t *item;
2053
2054	if (!_od_running()) return NULL;
2055
2056	payload = _xpc_query_key_int("number", number);
2057	if (payload == NULL) return NULL;
2058
2059	item = _ds_item(si, CATEGORY_RPC, "getrpcbynumber", NULL, _extract_rpc, payload);
2060
2061	xpc_release(payload);
2062	return item;
2063}
2064
2065static si_list_t *
2066ds_rpc_all(si_mod_t *si)
2067{
2068	return _ds_list(si, CATEGORY_RPC, "getrpcent", NULL, _extract_rpc);
2069}
2070
2071static si_item_t *
2072ds_fs_byspec(si_mod_t *si, const char *name)
2073{
2074	xpc_object_t payload;
2075	si_item_t *item;
2076
2077	if (!_od_running()) return NULL;
2078
2079	payload = _xpc_query_key_string("name", name);
2080	if (payload == NULL) return NULL;
2081
2082	item = _ds_item(si, CATEGORY_FS, "getfsbyname", NULL, _extract_fstab, payload);
2083
2084	xpc_release(payload);
2085	return item;
2086}
2087
2088static si_list_t *
2089ds_fs_all(si_mod_t *si)
2090{
2091	return _ds_list(si, CATEGORY_FS, "getfsent", NULL, _extract_fstab);
2092}
2093
2094static si_item_t *
2095ds_fs_byfile(si_mod_t *si, const char *name)
2096{
2097	si_item_t *item;
2098	si_list_t *list;
2099	uint32_t i;
2100	struct fstab *f;
2101
2102	if (!_od_running()) return NULL;
2103	if (name == NULL) return NULL;
2104
2105	list = ds_fs_all(si);
2106	if (list == NULL) return NULL;
2107
2108	item = NULL;
2109	for (i = 0; (i < list->count) && (item == NULL); i++)
2110	{
2111		f = (struct fstab *)((uintptr_t)(list->entry[i]) + sizeof(si_item_t));
2112		if (string_equal(name, f->fs_file)) item = si_item_retain(list->entry[i]);
2113	}
2114
2115	si_list_release(list);
2116	return item;
2117}
2118
2119static si_item_t *
2120ds_mac_byname(si_mod_t *si, const char *name)
2121{
2122	xpc_object_t payload;
2123	si_item_t *item;
2124
2125	if (!_od_running()) return NULL;
2126
2127	payload = _xpc_query_key_string("name", name);
2128	if (payload == NULL) return NULL;
2129
2130	item = _ds_item(si, CATEGORY_MAC, "getmacbyname", name, _extract_mac_mac, payload);
2131
2132	xpc_release(payload);
2133	return item;
2134}
2135
2136static si_item_t *
2137ds_mac_bymac(si_mod_t *si, const char *mac)
2138{
2139	xpc_object_t payload;
2140	si_item_t *item;
2141	char *cmac;
2142
2143	if (!_od_running()) return NULL;
2144
2145	cmac = si_standardize_mac_address(mac);
2146	if (cmac == NULL) return NULL;
2147
2148	payload = xpc_dictionary_create(NULL, NULL, 0);
2149	if (payload == NULL) return NULL;
2150
2151	payload = _xpc_query_key_string("mac", cmac);
2152	item = _ds_item(si, CATEGORY_MAC, "gethostbymac", cmac, _extract_mac_name, payload);
2153
2154	free(cmac);
2155	xpc_release(payload);
2156
2157	return item;
2158}
2159
2160#pragma mark -
2161
2162si_mod_t *
2163si_module_static_ds(void)
2164{
2165	static const struct si_mod_vtable_s ds_vtable =
2166	{
2167		.sim_is_valid = &_ds_is_valid,
2168
2169		.sim_user_byname = &ds_user_byname,
2170		.sim_user_byuid = &ds_user_byuid,
2171		.sim_user_byuuid = &ds_user_byuuid,
2172		.sim_user_all = &ds_user_all,
2173
2174		.sim_group_byname = &ds_group_byname,
2175		.sim_group_bygid = &ds_group_bygid,
2176		.sim_group_byuuid = &ds_group_byuuid,
2177		.sim_group_all = &ds_group_all,
2178
2179		.sim_grouplist = &ds_grouplist,
2180
2181		.sim_netgroup_byname = &ds_netgroup_byname,
2182		.sim_in_netgroup = &ds_in_netgroup,
2183
2184		.sim_alias_byname = &ds_alias_byname,
2185		.sim_alias_all = &ds_alias_all,
2186
2187		/* host lookups not supported */
2188		.sim_host_byname = NULL,
2189		.sim_host_byaddr = NULL,
2190		.sim_host_all = NULL,
2191
2192		.sim_network_byname = &ds_network_byname,
2193		.sim_network_byaddr = &ds_network_byaddr,
2194		.sim_network_all = &ds_network_all,
2195
2196		.sim_service_byname = &ds_service_byname,
2197		.sim_service_byport = &ds_service_byport,
2198		.sim_service_all = &ds_service_all,
2199
2200		.sim_protocol_byname = &ds_protocol_byname,
2201		.sim_protocol_bynumber = &ds_protocol_bynumber,
2202		.sim_protocol_all = &ds_protocol_all,
2203
2204		.sim_rpc_byname = &ds_rpc_byname,
2205		.sim_rpc_bynumber = &ds_rpc_bynumber,
2206		.sim_rpc_all = &ds_rpc_all,
2207
2208		.sim_fs_byspec = &ds_fs_byspec,
2209		.sim_fs_byfile = &ds_fs_byfile,
2210		.sim_fs_all = &ds_fs_all,
2211
2212		.sim_mac_byname = &ds_mac_byname,
2213		.sim_mac_bymac = &ds_mac_bymac,
2214
2215		/* si_mac_all not supported */
2216		.sim_mac_all = NULL,
2217
2218		/* si_addrinfo not supported */
2219		.sim_wants_addrinfo = NULL,
2220		.sim_addrinfo = NULL,
2221	};
2222
2223	static si_mod_t si =
2224	{
2225		.vers = 1,
2226		.refcount = 1,
2227		.flags = SI_MOD_FLAG_STATIC,
2228
2229		.private = NULL,
2230		.vtable = &ds_vtable,
2231	};
2232
2233	static dispatch_once_t once;
2234	dispatch_once(&once, ^{
2235		pthread_key_create(&_ds_serv_cache_key, _ds_serv_cache_free);
2236
2237		si.name = strdup("ds");
2238		ds_si_private_t *pp = calloc(1, sizeof(ds_si_private_t));
2239
2240		if (pp != NULL)
2241		{
2242			pp->notify_token_global = -1;
2243			pp->notify_token_user = -1;
2244			pp->notify_token_group = -1;
2245			pp->notify_token_service = -1;
2246		}
2247
2248		/*
2249		 * Don't register for notifications if the cache is disabled.
2250		 * notifyd (notably) disables the cache to prevent deadlocks.
2251		 */
2252		if (gL1CacheEnabled != 0)
2253		{
2254			/*
2255			 * Errors in registering for cache invalidation notifications are ignored.
2256			 * If there are failures, the tokens remain set to -1 which just causes
2257			 * cached items to be invalidated.
2258			 */
2259			notify_register_check(kNotifyDSCacheInvalidation, &(pp->notify_token_global));
2260			notify_register_check(kNotifyDSCacheInvalidationUser, &(pp->notify_token_user));
2261			notify_register_check(kNotifyDSCacheInvalidationGroup, &(pp->notify_token_group));
2262			notify_register_check(kNotifyDSCacheInvalidationService, &(pp->notify_token_service));
2263		}
2264
2265		si.private = pp;
2266	});
2267
2268	return &si;
2269}
2270
2271#endif /* DS_AVAILABLE */
2272