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 <sys/types.h>
25#include <sys/socket.h>
26#include <net/ethernet.h>
27#include <libkern/OSAtomic.h>
28#include <dlfcn.h>
29#include <string.h>
30#include <stdio.h>
31#include <errno.h>
32#include <pthread.h>
33#include <mach/mach.h>
34#include <dispatch/dispatch.h>
35#include <dispatch/private.h>
36#include "si_module.h"
37
38#define PLUGIN_DIR_PATH "/usr/lib/info"
39#define PLUGIN_BUNDLE_SUFFIX "so"
40
41#define WORKUNIT_CANCELLED     0x00000001
42#define WORKUNIT_RETURNS_LIST  0x00000002
43
44#ifdef __BIG_ENDIAN__
45#define WORKUNIT_CANCELLED_BIT_ADDRESS 31
46#else
47#define WORKUNIT_CANCELLED_BIT_ADDRESS 7
48#endif
49
50typedef struct si_async_workunit_s
51{
52	si_mod_t *si;
53	uint32_t call;
54	char *str1;
55	char *str2;
56	char *str3;
57	uint32_t num1;
58	uint32_t num2;
59	uint32_t num3;
60	uint32_t num4;
61	uint32_t err;
62	/* async support below */
63	uint32_t flags;
64	int32_t refcount;
65	void *callback;
66	void *context;
67	mach_port_t port;
68	mach_port_t send;
69	si_item_t *resitem;
70	si_list_t *reslist;
71	struct si_async_workunit_s *next;
72} si_async_workunit_t;
73
74static si_mod_t **module_list = NULL;
75static uint32_t module_count = 0;
76static pthread_mutex_t module_mutex = PTHREAD_MUTEX_INITIALIZER;
77static si_async_workunit_t *si_async_worklist = NULL;
78
79si_mod_t *si_module_static_search(void);
80si_mod_t *si_module_static_cache(void);
81si_mod_t *si_module_static_file(void);
82#ifdef DS_AVAILABLE
83si_mod_t *si_module_static_ds(void);
84#endif
85si_mod_t *si_module_static_mdns(void);
86
87static void *
88si_mod_dlsym(void *so, const char *name, const char *sym)
89{
90	char *str;
91	void *out;
92
93	if ((so == NULL) || (name == NULL) || (sym == NULL)) return NULL;
94
95	str = NULL;
96	asprintf(&str, "%s_%s", name, sym);
97	if (str == NULL) return NULL;
98
99	out = dlsym(so, str);
100	free(str);
101	return out;
102}
103
104si_mod_t *
105si_module_with_path(const char *path, const char *name)
106{
107	void *so;
108	int (*si_sym_init)(si_mod_t *);
109	void (*si_sym_close)(si_mod_t *);
110	int status;
111	si_mod_t *out;
112	char *outname;
113
114	if ((path == NULL) || (name == NULL))
115	{
116		errno = EINVAL;
117		return NULL;
118	}
119
120	so = dlopen(path, RTLD_LOCAL);
121	if (so == NULL) return NULL;
122
123	si_sym_init = si_mod_dlsym(so, name, "init");
124	if (si_sym_init == NULL)
125	{
126		dlclose(so);
127		errno = ECONNREFUSED;
128		return NULL;
129	}
130
131	si_sym_close = si_mod_dlsym(so, name, "close");
132	if (si_sym_close == NULL)
133	{
134		dlclose(so);
135		errno = ECONNREFUSED;
136		return NULL;
137	}
138
139	out = (si_mod_t *)calloc(1, sizeof(si_mod_t));
140	outname = strdup(name);
141
142	if ((out == NULL) || (outname == NULL))
143	{
144		free(out);
145		free(outname);
146		dlclose(so);
147		errno = ENOMEM;
148		return NULL;
149	}
150
151	out->name = outname;
152	out->refcount = 1;
153	out->flags = 0;
154	out->bundle = so;
155
156	status = si_sym_init(out);
157	if (status != 0)
158	{
159		dlclose(so);
160		free(out);
161		free(outname);
162		errno = ECONNREFUSED;
163		return NULL;
164	}
165
166	return out;
167}
168
169si_mod_t *
170si_module_with_name(const char *name)
171{
172	static struct
173	{
174		const char *name;
175		si_mod_t *(*init)(void);
176		si_mod_t *module;
177	} modules[] =
178	{
179		{ "search", si_module_static_search, NULL },
180		{ "cache", si_module_static_cache, NULL },
181		{ "file", si_module_static_file, NULL },
182#ifdef DS_AVAILABLE
183		{ "ds", si_module_static_ds, NULL },
184#endif
185		{ "mdns", si_module_static_mdns, NULL },
186		{ NULL, NULL },
187	};
188
189	si_mod_t *si = NULL;
190	int i;
191
192	/*
193	 * We don't need to worry about locking during initialization
194	 * because all modules init routine returns static storage.
195	 */
196
197	/* Find the module by name */
198	for (i = 0; modules[i].name != NULL; ++i)
199	{
200		if (string_equal(name, modules[i].name))
201		{
202			si = modules[i].module;
203			if (si == NULL)
204			{
205				si = modules[i].init();
206				modules[i].module = si;
207			}
208
209			break;
210		}
211	}
212
213	if (si != NULL) return si;
214
215	pthread_mutex_lock(&module_mutex);
216	char *path = NULL;
217
218	asprintf(&path, "%s/%s.%s", PLUGIN_DIR_PATH, name, PLUGIN_BUNDLE_SUFFIX);
219
220	if (path == NULL)
221	{
222		errno = ENOMEM;
223		pthread_mutex_unlock(&module_mutex);
224		return NULL;
225	}
226
227	si = si_module_with_path(path, name);
228	free(path);
229
230	if (si == NULL)
231	{
232		pthread_mutex_unlock(&module_mutex);
233		return NULL;
234	}
235
236	/* add out to module_list */
237	module_list = (si_mod_t **)reallocf(module_list, (module_count + 1) * sizeof(si_mod_t *));
238	if (module_list == NULL)
239	{
240		pthread_mutex_unlock(&module_mutex);
241		return si;
242	}
243
244	module_list[module_count] = si;
245	module_count++;
246
247	pthread_mutex_unlock(&module_mutex);
248
249	return si;
250}
251
252si_mod_t *
253si_module_retain(si_mod_t *si)
254{
255	if (si == NULL) return NULL;
256	if (si->flags & SI_MOD_FLAG_STATIC) return si;
257
258	OSAtomicIncrement32Barrier(&si->refcount);
259
260	return si;
261}
262
263void
264si_module_release(si_mod_t *si)
265{
266	int32_t i;
267
268	if (si == NULL) return;
269	if (si->flags & SI_MOD_FLAG_STATIC) return;
270
271	i = OSAtomicDecrement32Barrier(&si->refcount);
272	if (i > 0) return;
273
274	pthread_mutex_lock(&module_mutex);
275
276	for (i = 0; (i < module_count) && (module_list[i] != si); i++);
277	if (i >= module_count)
278	{
279		pthread_mutex_unlock(&module_mutex);
280		return;
281	}
282
283	if (module_count == 1)
284	{
285		free(module_list);
286		module_list = NULL;
287		module_count = 0;
288		pthread_mutex_unlock(&module_mutex);
289		return;
290	}
291
292	for (i++; i < module_count; i++) module_list[i - 1] = module_list[i];
293	module_count--;
294	module_list = (si_mod_t **)reallocf(module_list, module_count * sizeof(si_mod_t *));
295	if (module_list == NULL) module_count = 0;
296
297	pthread_mutex_unlock(&module_mutex);
298
299	if (si->vtable->sim_close != NULL) si->vtable->sim_close(si);
300	if (si->bundle != NULL) dlclose(si->bundle);
301	free(si->name);
302	free(si);
303}
304
305const char *
306si_module_name(si_mod_t *si)
307{
308	if (si == NULL) return NULL;
309
310	return (const char *)si->name;
311}
312
313int
314si_module_vers(si_mod_t *si)
315{
316	if (si == NULL) return 0;
317
318	return si->vers;
319}
320
321int
322si_item_match(si_item_t *item, int cat, const void *name, uint32_t num, int which)
323{
324	int i;
325	union
326	{
327		char *x;
328		struct passwd *u;
329		struct group *g;
330		struct grouplist_s *l;
331		struct aliasent *a;
332		struct hostent *h;
333		struct netent *n;
334		struct servent *s;
335		struct protoent *p;
336		struct rpcent *r;
337		struct fstab *f;
338		struct mac_s *m;
339	} ent;
340
341	if (item == NULL) return 0;
342	if (which == SEL_ALL) return 1;
343	if ((which == SEL_NAME) && (name == NULL)) return 0;
344
345	ent.x = (char *)((uintptr_t)item + sizeof(si_item_t));
346
347	switch (cat)
348	{
349		case CATEGORY_USER:
350		{
351			if ((which == SEL_NAME) && (string_equal(name, ent.u->pw_name))) return 1;
352			else if ((which == SEL_NUMBER) && (num == (uint32_t)ent.u->pw_uid)) return 1;
353			return 0;
354		}
355		case CATEGORY_GROUP:
356		{
357			if ((which == SEL_NAME) && (string_equal(name, ent.g->gr_name))) return 1;
358			else if ((which == SEL_NUMBER) && (num == (uint32_t)ent.g->gr_gid)) return 1;
359			return 0;
360		}
361		case CATEGORY_GROUPLIST:
362		{
363			if ((which == SEL_NAME) && (string_equal(name, ent.l->gl_user))) return 1;
364			return 0;
365		}
366		case CATEGORY_ALIAS:
367		{
368			if ((which == SEL_NAME) && (string_equal(name, ent.a->alias_name))) return 1;
369			return 0;
370		}
371		case CATEGORY_HOST_IPV4:
372		case CATEGORY_HOST_IPV6:
373		{
374			/* N.B. address family is passed in num variable */
375			if (ent.h->h_addrtype != num) return 0;
376			if (which == SEL_NAME)
377			{
378				if (string_equal(name, ent.h->h_name)) return 1;
379				if (ent.h->h_aliases != NULL)
380				{
381					for (i = 0; ent.h->h_aliases[i] != NULL; i++)
382					{
383						if (string_equal(name, ent.h->h_aliases[i])) return 1;
384					}
385				}
386			}
387			else if (which == SEL_NUMBER)
388			{
389				if (memcmp(name, ent.h->h_addr_list[0], ent.h->h_length) == 0) return 1;
390			}
391			return 0;
392		}
393		case CATEGORY_NETWORK:
394		{
395			if (which == SEL_NAME)
396			{
397				if (string_equal(name, ent.n->n_name)) return 1;
398				if (ent.n->n_aliases != NULL)
399				{
400					for (i = 0; ent.n->n_aliases[i] != NULL; i++)
401					{
402						if (string_equal(name, ent.n->n_aliases[i])) return 1;
403					}
404				}
405			}
406			else if (which == SEL_NUMBER)
407			{
408				if (num == ent.n->n_net) return 1;
409			}
410			return 0;
411		}
412		case CATEGORY_SERVICE:
413		{
414			if (which == SEL_NAME)
415			{
416				/* N.B. for SEL_NAME, num is 0 (don't care), 1 (udp) or 2 (tcp) */
417				if ((num == 1) && (string_not_equal("udp", ent.s->s_proto))) return 0;
418				if ((num == 2) && (string_not_equal("tcp", ent.s->s_proto))) return 0;
419
420				if (string_equal(name, ent.s->s_name)) return 1;
421				if (ent.s->s_aliases != NULL)
422				{
423					for (i = 0; ent.s->s_aliases[i] != NULL; i++)
424					{
425						if (string_equal(name, ent.s->s_aliases[i])) return 1;
426					}
427				}
428			}
429			else if (which == SEL_NUMBER)
430			{
431				/* N.B. for SEL_NUMBER, protocol is sent in name variable */
432				if ((name != NULL) && (string_not_equal(name, ent.s->s_proto))) return 0;
433				if (num == ent.s->s_port) return 1;
434			}
435			return 0;
436		}
437		case CATEGORY_PROTOCOL:
438		{
439			if (which == SEL_NAME)
440			{
441				if (string_equal(name, ent.p->p_name)) return 1;
442				if (ent.p->p_aliases != NULL)
443				{
444					for (i = 0; ent.p->p_aliases[i] != NULL; i++)
445					{
446						if (string_equal(name, ent.p->p_aliases[i])) return 1;
447					}
448				}
449			}
450			else if (which == SEL_NUMBER)
451			{
452				if (num == ent.p->p_proto) return 1;
453			}
454			return 0;
455		}
456		case CATEGORY_RPC:
457		{
458			if (which == SEL_NAME)
459			{
460				if (string_equal(name, ent.r->r_name)) return 1;
461				if (ent.r->r_aliases != NULL)
462				{
463					for (i = 0; ent.r->r_aliases[i] != NULL; i++)
464					{
465						if (string_equal(name, ent.r->r_aliases[i])) return 1;
466					}
467				}
468			}
469			else if (which == SEL_NUMBER)
470			{
471				if (num == ent.r->r_number) return 1;
472			}
473			return 0;
474		}
475		case CATEGORY_FS:
476		{
477			if ((which == SEL_NAME) && (string_equal(name, ent.f->fs_spec))) return 1;
478			if ((which == SEL_NUMBER) && (string_equal(name, ent.f->fs_file))) return 1;
479			return 0;
480		}
481		case CATEGORY_MAC:
482		{
483			if ((which == SEL_NAME) && (string_equal(name, ent.m->host))) return 1;
484			if ((which == SEL_NUMBER) && (string_equal(name, ent.m->mac))) return 1;
485			return 0;
486		}
487		default: return 0;
488	}
489
490	return 0;
491}
492
493int
494si_item_is_valid(si_item_t *item)
495{
496	si_mod_t *si;
497
498	if (item == NULL) return 0;
499
500	si = item->src;
501
502	if (si == NULL) return 0;
503	if (si->vtable->sim_is_valid == NULL) return 0;
504
505	return si->vtable->sim_is_valid(si, item);
506}
507
508si_item_t *
509si_user_byname(si_mod_t *si, const char *name)
510{
511	if (si == NULL) return NULL;
512	if (si->vtable->sim_user_byname == NULL) return NULL;
513	return si->vtable->sim_user_byname(si, name);
514}
515
516si_item_t *
517si_user_byuid(si_mod_t *si, uid_t uid)
518{
519	if (si == NULL) return NULL;
520	if (si->vtable->sim_user_byuid == NULL) return NULL;
521	return si->vtable->sim_user_byuid(si, uid);
522}
523
524si_item_t *
525si_user_byuuid(si_mod_t *si, uuid_t uuid)
526{
527	if (si == NULL) return NULL;
528	if (si->vtable->sim_user_byuuid == NULL) return NULL;
529	return si->vtable->sim_user_byuuid(si, uuid);
530}
531
532si_list_t *
533si_user_all(si_mod_t *si)
534{
535	if (si == NULL) return NULL;
536	if (si->vtable->sim_user_all == NULL) return NULL;
537	return si->vtable->sim_user_all(si);
538}
539
540si_item_t *
541si_group_byname(si_mod_t *si, const char *name)
542{
543	if (si == NULL) return NULL;
544	if (si->vtable->sim_group_byname == NULL) return NULL;
545	return si->vtable->sim_group_byname(si, name);
546}
547
548si_item_t *
549si_group_bygid(si_mod_t *si, gid_t gid)
550{
551	if (si == NULL) return NULL;
552	if (si->vtable->sim_group_bygid == NULL) return NULL;
553	return si->vtable->sim_group_bygid(si, gid);
554}
555
556si_item_t *
557si_group_byuuid(si_mod_t *si, uuid_t uuid)
558{
559	if (si == NULL) return NULL;
560	if (si->vtable->sim_group_byuuid == NULL) return NULL;
561	return si->vtable->sim_group_byuuid(si, uuid);
562}
563
564si_list_t *
565si_group_all(si_mod_t *si)
566{
567	if (si == NULL) return NULL;
568	if (si->vtable->sim_group_all == NULL) return NULL;
569	return si->vtable->sim_group_all(si);
570}
571
572si_item_t *
573si_grouplist(si_mod_t *si, const char *name, uint32_t count)
574{
575	if (si == NULL) return NULL;
576	if (si->vtable->sim_grouplist == NULL) return NULL;
577	return si->vtable->sim_grouplist(si, name, count);
578}
579
580si_list_t *
581si_netgroup_byname(struct si_mod_s *si, const char *name)
582{
583	if (si == NULL) return NULL;
584	if (si->vtable->sim_netgroup_byname == NULL) return NULL;
585	return si->vtable->sim_netgroup_byname(si, name);
586}
587
588int
589si_in_netgroup(struct si_mod_s *si, const char *name, const char *host, const char *user, const char *domain)
590{
591	if (si == NULL) return 0;
592	if (si->vtable->sim_in_netgroup == NULL) return 0;
593	return si->vtable->sim_in_netgroup(si, name, host, user, domain);
594}
595
596si_item_t *
597si_alias_byname(si_mod_t *si, const char *name)
598{
599	if (si == NULL) return NULL;
600	if (si->vtable->sim_alias_byname == NULL) return NULL;
601	return si->vtable->sim_alias_byname(si, name);
602}
603
604si_list_t *
605si_alias_all(si_mod_t *si)
606{
607	if (si == NULL) return NULL;
608	if (si->vtable->sim_alias_all == NULL) return NULL;
609	return si->vtable->sim_alias_all(si);
610}
611
612si_item_t *
613si_host_byname(si_mod_t *si, const char *name, int af, const char *interface, uint32_t *err)
614{
615	if (si == NULL) return NULL;
616	if (si->vtable->sim_host_byname == NULL) return NULL;
617	return si->vtable->sim_host_byname(si, name, af, interface, err);
618}
619
620si_item_t *
621si_host_byaddr(si_mod_t *si, const void *addr, int af, const char *interface, uint32_t *err)
622{
623	if (si == NULL) return NULL;
624	if (si->vtable->sim_host_byaddr == NULL) return NULL;
625	return si->vtable->sim_host_byaddr(si, addr, af, interface, err);
626}
627
628si_list_t *
629si_host_all(si_mod_t *si)
630{
631	if (si == NULL) return NULL;
632	if (si->vtable->sim_host_all == NULL) return NULL;
633	return si->vtable->sim_host_all(si);
634}
635
636si_item_t *
637si_mac_byname(struct si_mod_s *si, const char *name)
638{
639	if (si == NULL) return NULL;
640	if (si->vtable->sim_mac_byname == NULL) return NULL;
641	return si->vtable->sim_mac_byname(si, name);
642}
643
644si_item_t *
645si_mac_bymac(struct si_mod_s *si, const char *mac)
646{
647	if (si == NULL) return NULL;
648	if (si->vtable->sim_mac_bymac == NULL) return NULL;
649	return si->vtable->sim_mac_bymac(si, mac);
650}
651
652si_list_t *
653si_mac_all(si_mod_t *si)
654{
655	if (si == NULL) return NULL;
656	if (si->vtable->sim_mac_all == NULL) return NULL;
657	return si->vtable->sim_mac_all(si);
658}
659
660si_item_t *
661si_network_byname(si_mod_t *si, const char *name)
662{
663	if (si == NULL) return NULL;
664	if (si->vtable->sim_network_byname == NULL) return NULL;
665	return si->vtable->sim_network_byname(si, name);
666}
667
668si_item_t *
669si_network_byaddr(si_mod_t *si, uint32_t addr)
670{
671	if (si == NULL) return NULL;
672	if (si->vtable->sim_network_byaddr == NULL) return NULL;
673	return si->vtable->sim_network_byaddr(si, addr);
674}
675
676si_list_t *
677si_network_all(si_mod_t *si)
678{
679	if (si == NULL) return NULL;
680	if (si->vtable->sim_network_all == NULL) return NULL;
681	return si->vtable->sim_network_all(si);
682}
683
684si_item_t *
685si_service_byname(si_mod_t *si, const char *name, const char *proto)
686{
687	if (si == NULL) return NULL;
688	if (si->vtable->sim_service_byname == NULL) return NULL;
689	return si->vtable->sim_service_byname(si, name, proto);
690}
691
692si_item_t *
693si_service_byport(si_mod_t *si, int port, const char *proto)
694{
695	if (si == NULL) return NULL;
696	if (si->vtable->sim_service_byport == NULL) return NULL;
697	return si->vtable->sim_service_byport(si, port, proto);
698}
699
700si_list_t *
701si_service_all(si_mod_t *si)
702{
703	if (si == NULL) return NULL;
704	if (si->vtable->sim_service_all == NULL) return NULL;
705	return si->vtable->sim_service_all(si);
706}
707
708si_item_t *
709si_protocol_byname(si_mod_t *si, const char *name)
710{
711	if (si == NULL) return NULL;
712	if (si->vtable->sim_protocol_byname == NULL) return NULL;
713	return si->vtable->sim_protocol_byname(si, name);
714}
715
716si_item_t *
717si_protocol_bynumber(si_mod_t *si, uint32_t number)
718{
719	if (si == NULL) return NULL;
720	if (si->vtable->sim_protocol_bynumber == NULL) return NULL;
721	return si->vtable->sim_protocol_bynumber(si, number);
722}
723
724si_list_t *
725si_protocol_all(si_mod_t *si)
726{
727	if (si == NULL) return NULL;
728	if (si->vtable->sim_protocol_all == NULL) return NULL;
729	return si->vtable->sim_protocol_all(si);
730}
731
732si_item_t *
733si_rpc_byname(si_mod_t *si, const char *name)
734{
735	if (si == NULL) return NULL;
736	if (si->vtable->sim_rpc_byname == NULL) return NULL;
737	return si->vtable->sim_rpc_byname(si, name);
738}
739
740si_item_t *
741si_rpc_bynumber(si_mod_t *si, int number)
742{
743	if (si == NULL) return NULL;
744	if (si->vtable->sim_rpc_bynumber == NULL) return NULL;
745	return si->vtable->sim_rpc_bynumber(si, number);
746}
747
748si_list_t *
749si_rpc_all(si_mod_t *si)
750{
751	if (si == NULL) return NULL;
752	if (si->vtable->sim_rpc_all == NULL) return NULL;
753	return si->vtable->sim_rpc_all(si);
754}
755
756si_item_t *
757si_fs_byspec(si_mod_t *si, const char *spec)
758{
759	if (si == NULL) return NULL;
760	if (si->vtable->sim_fs_byspec == NULL) return NULL;
761	return si->vtable->sim_fs_byspec(si, spec);
762}
763
764si_item_t *
765si_fs_byfile(si_mod_t *si, const char *file)
766{
767	if (si == NULL) return NULL;
768	if (si->vtable->sim_fs_byfile == NULL) return NULL;
769	return si->vtable->sim_fs_byfile(si, file);
770}
771
772si_list_t *
773si_fs_all(si_mod_t *si)
774{
775	if (si == NULL) return NULL;
776	if (si->vtable->sim_fs_all == NULL) return NULL;
777	return si->vtable->sim_fs_all(si);
778}
779
780si_item_t *
781si_item_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t *err)
782{
783	if (si == NULL) return NULL;
784
785	switch (call)
786	{
787		case SI_CALL_USER_BYNAME: return si_user_byname(si, str1);
788		case SI_CALL_USER_BYUID: return si_user_byuid(si, (uid_t)num1);
789		case SI_CALL_GROUP_BYNAME: return si_group_byname(si, str1);
790		case SI_CALL_GROUP_BYGID: return si_group_bygid(si, (gid_t)num1);
791		case SI_CALL_GROUPLIST: return si_grouplist(si, str1, (int) num1);
792		case SI_CALL_ALIAS_BYNAME: return si_alias_byname(si, str1);
793		case SI_CALL_HOST_BYNAME: return si_host_byname(si, str1, num1, str3, err);
794		case SI_CALL_HOST_BYADDR: return si_host_byaddr(si, (void *)str1, num1, str3, err);
795		case SI_CALL_NETWORK_BYNAME: return si_network_byname(si, str1);
796		case SI_CALL_NETWORK_BYADDR: return si_network_byaddr(si, num1);
797		case SI_CALL_SERVICE_BYNAME: return si_service_byname(si, str1, str2);
798		case SI_CALL_SERVICE_BYPORT: return si_service_byport(si, num1, str2);
799		case SI_CALL_PROTOCOL_BYNAME: return si_protocol_byname(si, str1);
800		case SI_CALL_PROTOCOL_BYNUMBER: return si_protocol_bynumber(si, num1);
801		case SI_CALL_RPC_BYNAME: return si_network_byname(si, str1);
802		case SI_CALL_RPC_BYNUMBER: return si_rpc_bynumber(si, num1);
803		case SI_CALL_FS_BYSPEC: return si_fs_byspec(si, str1);
804		case SI_CALL_FS_BYFILE: return si_fs_byfile(si, str1);
805		case SI_CALL_NAMEINFO: return si_nameinfo(si, (const struct sockaddr *)str1, num1, str3, err);
806		case SI_CALL_IPNODE_BYNAME: return si_ipnode_byname(si, (const char *)str1, num1, num2, str3, err);
807		case SI_CALL_MAC_BYNAME: return si_mac_byname(si, (const char *)str1);
808		case SI_CALL_MAC_BYMAC: return si_mac_bymac(si, (const char *)str1);
809
810		/* Support for DNS async calls */
811		case SI_CALL_DNS_QUERY:
812		case SI_CALL_DNS_SEARCH:
813		{
814			if (si->vtable->sim_item_call == NULL) return NULL;
815			return si->vtable->sim_item_call(si, call, str1, str2, str3, num1, num2, err);
816		}
817
818		default: return NULL;
819	}
820
821	return NULL;
822}
823
824si_list_t *
825si_list_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, uint32_t *err)
826{
827	if (si == NULL) return NULL;
828
829	switch (call)
830	{
831		case SI_CALL_USER_ALL: return si_user_all(si);
832		case SI_CALL_GROUP_ALL: return si_group_all(si);
833		case SI_CALL_ALIAS_ALL: return si_alias_all(si);
834		case SI_CALL_HOST_ALL: return si_host_all(si);
835		case SI_CALL_NETWORK_ALL: return si_network_all(si);
836		case SI_CALL_SERVICE_ALL: return si_service_all(si);
837		case SI_CALL_PROTOCOL_ALL: return si_protocol_all(si);
838		case SI_CALL_RPC_ALL: return si_rpc_all(si);
839		case SI_CALL_FS_ALL: return si_fs_all(si);
840		case SI_CALL_MAC_ALL: return si_mac_all(si);
841		case SI_CALL_ADDRINFO: return si_addrinfo(si, str1, str2, num1, num2, num3, num4, str3, err);
842		default: return NULL;
843	}
844
845	return NULL;
846}
847
848static void
849si_async_worklist_add_unit(si_async_workunit_t *r)
850{
851	pthread_mutex_lock(&module_mutex);
852	r->next = si_async_worklist;
853	si_async_worklist = r;
854	pthread_mutex_unlock(&module_mutex);
855}
856
857static void
858si_async_worklist_remove_unit(si_async_workunit_t *r)
859{
860	si_async_workunit_t *x;
861
862	pthread_mutex_lock(&module_mutex);
863	if (si_async_worklist == r)
864	{
865		si_async_worklist = r->next;
866	}
867	else
868	{
869		for (x = si_async_worklist; (x != NULL) && (x->next != r); x = x->next) {;}
870		if (x != NULL) x->next = r->next;
871	}
872	pthread_mutex_unlock(&module_mutex);
873}
874
875static si_async_workunit_t*
876si_async_worklist_find_unit(mach_port_t p)
877{
878	si_async_workunit_t *r;
879
880	pthread_mutex_lock(&module_mutex);
881	for (r = si_async_worklist; (r != NULL) && (r->port != p); r = r->next) {;}
882	pthread_mutex_unlock(&module_mutex);
883
884	return r;
885}
886
887static si_async_workunit_t *
888si_async_workunit_create(si_mod_t *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, void *callback, void *context)
889{
890	si_async_workunit_t *r;
891	kern_return_t status;
892	mach_port_t reply, send;
893	mach_msg_type_name_t type;
894	char *s1, *s2, *s3;
895
896	s1 = NULL;
897	s2 = NULL;
898	s3 = NULL;
899
900	if (si_call_str1_is_buffer(call))
901	{
902		if (num3 > 0)
903		{
904			s1 = calloc(1, num3);
905			if (s1 == NULL) return NULL;
906			memcpy(s1, str1, num3);
907		}
908	}
909	else if (str1 != NULL)
910	{
911		s1 = strdup(str1);
912		if (s1 == NULL) return NULL;
913	}
914
915	if (str2 != NULL)
916	{
917		s2 = strdup(str2);
918		if (s2 == NULL)
919		{
920			if (s1 != NULL) free(s1);
921			return NULL;
922		}
923	}
924
925	if (str3 != NULL)
926	{
927		s3 = strdup(str3);
928		if (s3 == NULL)
929		{
930			if (s1 != NULL) free(s1);
931			if (s2 != NULL) free(s2);
932			return NULL;
933		}
934	}
935
936	r = (si_async_workunit_t *)calloc(1, sizeof(si_async_workunit_t));
937	if (r == NULL)
938	{
939		if (s1 != NULL) free(s1);
940		if (s2 != NULL) free(s2);
941		if (s3 != NULL) free(s3);
942		return NULL;
943	}
944
945	reply = MACH_PORT_NULL;
946	send = MACH_PORT_NULL;
947	type = 0;
948
949	status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply);
950	if (status == KERN_SUCCESS) status = mach_port_extract_right(mach_task_self(), reply, MACH_MSG_TYPE_MAKE_SEND_ONCE, &send, &type);
951	if (status != KERN_SUCCESS)
952	{
953		if (reply != MACH_PORT_NULL) mach_port_mod_refs(mach_task_self(), reply, MACH_PORT_RIGHT_RECEIVE, -1);
954		if (s1 != NULL) free(s1);
955		if (s2 != NULL) free(s2);
956		if (s3 != NULL) free(s3);
957		free(r);
958		return NULL;
959	}
960
961	r->si = si;
962	r->call = call;
963	r->str1 = s1;
964	r->str2 = s2;
965	r->str3 = s3;
966	r->num1 = num1;
967	r->num2 = num2;
968	r->num3 = num3;
969	r->num4 = num4;
970
971	r->refcount = 2;
972	r->flags = 0;
973	if (si_call_returns_list(call)) r->flags |= WORKUNIT_RETURNS_LIST;
974
975	r->callback = callback;
976	r->context = context;
977	r->port = reply;
978	r->send = send;
979
980	si_async_worklist_add_unit(r);
981
982	return r;
983}
984
985static void
986si_async_workunit_release(si_async_workunit_t *r)
987{
988	if (r == NULL) return;
989
990	if (OSAtomicDecrement32Barrier(&(r->refcount)) != 0) return;
991
992#ifdef CALL_TRACE
993	fprintf(stderr, "** %s freeing worklist item %p\n", __func__, r);
994#endif
995
996	si_async_worklist_remove_unit(r);
997
998	if (r->resitem != NULL) si_item_release(r->resitem);
999	if (r->reslist != NULL) si_list_release(r->reslist);
1000
1001	if (r->str1 != NULL) free(r->str1);
1002	if (r->str2 != NULL) free(r->str2);
1003	if (r->str3 != NULL) free(r->str3);
1004
1005	/* release send-once right if it has not been used */
1006	if (r->send != MACH_PORT_NULL) mach_port_deallocate(mach_task_self(), r->send);
1007
1008	/* release receive right */
1009	mach_port_mod_refs(mach_task_self(), r->port, MACH_PORT_RIGHT_RECEIVE, -1);
1010
1011	free(r);
1012}
1013
1014static void
1015si_async_launchpad(si_async_workunit_t *r)
1016{
1017	kern_return_t status;
1018	mach_msg_empty_send_t msg;
1019
1020#ifdef CALL_TRACE
1021	fprintf(stderr, "** %s starting worklist item %p\n", __func__, r);
1022#endif
1023
1024	if (r->flags & WORKUNIT_CANCELLED)
1025	{
1026		si_async_workunit_release(r);
1027#ifdef CALL_TRACE
1028		fprintf(stderr, "** %s worklist item %p was cancelled early\n", __func__, r);
1029#endif
1030		return;
1031	}
1032
1033	if (r->flags & WORKUNIT_RETURNS_LIST) r->reslist = si_list_call(r->si, r->call, r->str1, r->str2, r->str3, r->num1, r->num2, r->num3, r->num4, &(r->err));
1034	else r->resitem = si_item_call(r->si, r->call, r->str1, r->str2, r->str3, r->num1, r->num2, &(r->err));
1035
1036	/*
1037	 * Test and set the cancelled flag.
1038	 * If it was set, then this work item was cancelled.
1039	 * Otherwise, setting it here prevents si_async_cancel from cancelling:
1040	 * too late to cancel now!
1041	 */
1042	if (OSAtomicTestAndSetBarrier(WORKUNIT_CANCELLED_BIT_ADDRESS, &(r->flags)) == 1)
1043	{
1044		si_async_workunit_release(r);
1045#ifdef CALL_TRACE
1046		fprintf(stderr, "** %s worklist item %p was cancelled in flight\n", __func__, r);
1047#endif
1048		return;
1049	}
1050#ifdef CALL_TRACE
1051	else fprintf(stderr, "** %s worklist item %p flags are now 0x%08x\n", __func__, r, r->flags);
1052#endif
1053
1054	memset(&msg, 0, sizeof(mach_msg_empty_send_t));
1055
1056	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSGH_BITS_ZERO);
1057	msg.header.msgh_remote_port = r->send;
1058	r->send = MACH_PORT_NULL;
1059	msg.header.msgh_local_port = MACH_PORT_NULL;
1060	msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
1061	msg.header.msgh_id = r->call;
1062
1063	status = mach_msg(&(msg.header), MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1064	if (status != MACH_MSG_SUCCESS)
1065	{
1066		/* receiver failed - clean up to avoid a port leak */
1067		mach_msg_destroy(&(msg.header));
1068#ifdef CALL_TRACE
1069		fprintf(stderr, "** %s mach message send failed for worklist item %p\n", __func__, r);
1070#endif
1071	}
1072
1073	si_async_workunit_release(r);
1074
1075	/*
1076	 * The client is now responsible for calling si_async_handle_reply,
1077	 * which will invoke the client's callback and then release the workunit.
1078	 */
1079
1080#ifdef CALL_TRACE
1081	fprintf(stderr, "** %s completed async worklist item %p\n", __func__, r);
1082#endif
1083}
1084
1085mach_port_t
1086si_async_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, void *callback, void *context)
1087{
1088	si_async_workunit_t *req;
1089
1090	if (si == NULL) return MACH_PORT_NULL;
1091	if (callback == NULL) return MACH_PORT_NULL;
1092
1093	/* if module does async on it's own, hand off the call */
1094	if (si->vtable->sim_async_call != NULL)
1095	{
1096		return si->vtable->sim_async_call(si, call, str1, str2, str3, num1, num2, num3, num4, callback, context);
1097	}
1098
1099	req = si_async_workunit_create(si, call, str1, str2, str3, num1, num2, num3, num4, callback, context);
1100	if (req == NULL) return MACH_PORT_NULL;
1101
1102	/* queue the work on the global low-priority dispatch queue */
1103#ifdef CALL_TRACE
1104	fprintf(stderr, "** %s dispatching worklist item %p\n", __func__, req);
1105#endif
1106	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_OVERCOMMIT), ^{ si_async_launchpad(req); });
1107
1108	return req->port;
1109}
1110
1111void
1112si_async_cancel(mach_port_t p)
1113{
1114	si_async_workunit_t *r;
1115
1116	r = si_async_worklist_find_unit(p);
1117	if (r == NULL)
1118	{
1119#ifdef CALL_TRACE
1120		fprintf(stderr, "** %s can't find worklist item\n", __func__);
1121#endif
1122		return;
1123	}
1124
1125	/*
1126	 * Test and set the WORKUNIT_CANCELLED flag.
1127	 * If it was already set, this work item has been executed - too late to really cancel.
1128	 */
1129	if (OSAtomicTestAndSetBarrier(WORKUNIT_CANCELLED_BIT_ADDRESS, &(r->flags)) == 1)
1130	{
1131		/* already executed */
1132#ifdef CALL_TRACE
1133		fprintf(stderr, "** %s worklist item %p has executed\n", __func__, r);
1134#endif
1135	}
1136
1137#ifdef CALL_TRACE
1138	fprintf(stderr, "** %s calling worklist item %p callback SI_STATUS_CALL_CANCELLED\n", __func__, r);
1139#endif
1140
1141	if (r->callback != NULL)
1142	{
1143		if (r->flags & WORKUNIT_RETURNS_LIST) ((list_async_callback)(r->callback))(NULL, SI_STATUS_CALL_CANCELLED, r->context);
1144		else ((item_async_callback)(r->callback))(NULL, SI_STATUS_CALL_CANCELLED, r->context);
1145	}
1146
1147	si_async_workunit_release(r);
1148}
1149
1150void
1151si_async_handle_reply(mach_msg_header_t *msg)
1152{
1153	si_async_workunit_t *r;
1154	mach_port_t reply = msg->msgh_local_port;
1155
1156	r = si_async_worklist_find_unit(reply);
1157	if (r == NULL)
1158	{
1159#ifdef CALL_TRACE
1160		fprintf(stderr, "** %s can't find worklist item\n", __func__);
1161#endif
1162		return;
1163	}
1164
1165#ifdef CALL_TRACE
1166	fprintf(stderr, "** %s worklist item %p flags are 0x%08x\n", __func__, r, r->flags);
1167#endif
1168	if ((r->flags & WORKUNIT_CANCELLED) == 0)
1169	{
1170#ifdef CALL_TRACE
1171		fprintf(stderr, "** %s workunit thread is still active\n", __func__);
1172#endif
1173		return;
1174	}
1175
1176	if (r->callback != NULL)
1177	{
1178		if (r->flags & WORKUNIT_RETURNS_LIST) ((list_async_callback)(r->callback))(r->reslist, r->err, r->context);
1179		else ((item_async_callback)(r->callback))(r->resitem, r->err, r->context);
1180
1181		r->reslist = NULL;
1182		r->resitem = NULL;
1183	}
1184	else
1185	{
1186#ifdef CALL_TRACE
1187		fprintf(stderr, "** %s workunit has no callback\n", __func__);
1188#endif
1189	}
1190
1191	si_async_workunit_release(r);
1192}
1193
1194char *
1195si_standardize_mac_address(const char *addr)
1196{
1197	char e[6][3];
1198	char *out;
1199	struct ether_addr *ether;
1200	int i;
1201
1202	if (addr == NULL) return NULL;
1203
1204	/* ether_aton isn't thread-safe */
1205	pthread_mutex_lock(&module_mutex);
1206
1207	ether = ether_aton(addr);
1208	if (ether == NULL)
1209	{
1210		pthread_mutex_unlock(&module_mutex);
1211		return NULL;
1212	}
1213
1214	for (i = 0; i < 6; i++)
1215	{
1216		if (ether->ether_addr_octet[i] <= 15)
1217		{
1218			sprintf(e[i], "0%x", ether->ether_addr_octet[i]);
1219		}
1220		else
1221		{
1222			sprintf(e[i], "%x", ether->ether_addr_octet[i]);
1223		}
1224	}
1225
1226	pthread_mutex_unlock(&module_mutex);
1227
1228	out = NULL;
1229	asprintf(&out, "%s:%s:%s:%s:%s:%s", e[0], e[1], e[2], e[3], e[4], e[5]);
1230	return out;
1231}
1232