Libinfo-406.17.tar.gz
[apple/libinfo.git] / lookup.subproj / si_module.c
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
50 typedef 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
74 static si_mod_t **module_list = NULL;
75 static uint32_t module_count = 0;
76 static pthread_mutex_t module_mutex = PTHREAD_MUTEX_INITIALIZER;
77 static si_async_workunit_t *si_async_worklist = NULL;
78
79 si_mod_t *si_module_static_search(void);
80 si_mod_t *si_module_static_cache(void);
81 si_mod_t *si_module_static_file(void);
82 #ifdef DS_AVAILABLE
83 si_mod_t *si_module_static_ds(void);
84 #endif
85 si_mod_t *si_module_static_mdns(void);
86
87 static void *
88 si_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
104 si_mod_t *
105 si_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
169 si_mod_t *
170 si_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
252 si_mod_t *
253 si_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
263 void
264 si_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
305 const char *
306 si_module_name(si_mod_t *si)
307 {
308 if (si == NULL) return NULL;
309
310 return (const char *)si->name;
311 }
312
313 int
314 si_module_vers(si_mod_t *si)
315 {
316 if (si == NULL) return 0;
317
318 return si->vers;
319 }
320
321 int
322 si_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
493 int
494 si_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
508 si_item_t *
509 si_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
516 si_item_t *
517 si_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
524 si_item_t *
525 si_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
532 si_list_t *
533 si_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
540 si_item_t *
541 si_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
548 si_item_t *
549 si_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
556 si_item_t *
557 si_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
564 si_list_t *
565 si_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
572 si_item_t *
573 si_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
580 si_list_t *
581 si_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
588 int
589 si_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
596 si_item_t *
597 si_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
604 si_list_t *
605 si_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
612 si_item_t *
613 si_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
620 si_item_t *
621 si_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
628 si_list_t *
629 si_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
636 si_item_t *
637 si_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
644 si_item_t *
645 si_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
652 si_list_t *
653 si_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
660 si_item_t *
661 si_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
668 si_item_t *
669 si_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
676 si_list_t *
677 si_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
684 si_item_t *
685 si_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
692 si_item_t *
693 si_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
700 si_list_t *
701 si_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
708 si_item_t *
709 si_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
716 si_item_t *
717 si_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
724 si_list_t *
725 si_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
732 si_item_t *
733 si_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
740 si_item_t *
741 si_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
748 si_list_t *
749 si_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
756 si_item_t *
757 si_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
764 si_item_t *
765 si_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
772 si_list_t *
773 si_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
780 si_item_t *
781 si_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
824 si_list_t *
825 si_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
848 static void
849 si_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
857 static void
858 si_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
875 static si_async_workunit_t*
876 si_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
887 static si_async_workunit_t *
888 si_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
985 static void
986 si_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
1014 static void
1015 si_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
1085 mach_port_t
1086 si_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
1111 void
1112 si_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
1150 void
1151 si_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
1194 char *
1195 si_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 }