]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/BonjourEvents.c
mDNSResponder-379.38.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / BonjourEvents.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <CoreFoundation/CoreFoundation.h>
19 #include <CoreFoundation/CFXPCBridge.h>
20 #include <dns_sd.h>
21 #include <UserEventAgentInterface.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <asl.h>
25 #include <xpc/xpc.h>
26
27
28 #pragma mark -
29 #pragma mark Types
30 #pragma mark -
31 static const char* sPluginIdentifier = "com.apple.bonjour.events";
32
33 // PLIST Keys
34 static const CFStringRef sServiceNameKey = CFSTR("ServiceName");
35 static const CFStringRef sServiceTypeKey = CFSTR("ServiceType");
36 static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain");
37
38 static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd");
39 static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove");
40
41 static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken");
42 static const CFStringRef sLaunchdDictKey = CFSTR("LaunchdDict");
43
44
45 /************************************************
46 * Launch Event Dictionary (input from launchd)
47 * Passed To: ManageEventsCallback
48 *-----------------------------------------------
49 * Typing in this dictionary is not enforced
50 * above us. So this may not be true. Type check
51 * all input before using it.
52 *-----------------------------------------------
53 * sServiceNameKey - CFString (Optional)
54 * sServiceTypeKey - CFString
55 * sServiceDomainKey - CFString
56 *
57 * One or more of the following.
58 *-----------------------------------
59 * sOnServiceAddKey - CFBoolean
60 * sOnServiceRemoveKey - CFBoolean
61 * sWhileServiceExistsKey - CFBoolean
62 ************************************************/
63
64 /************************************************
65 * Browser Dictionary
66 *-----------------------------------------------
67 * sServiceDomainKey - CFString
68 * sServiceTypeKey - CFString
69 ************************************************/
70
71 /************************************************
72 * Event Dictionary
73 *-----------------------------------------------
74 * sServiceNameKey - CFString (Optional)
75 * sLaunchdTokenKey - CFNumber
76 ************************************************/
77
78 typedef struct {
79 UserEventAgentInterfaceStruct* _UserEventAgentInterface;
80 CFUUIDRef _factoryID;
81 UInt32 _refCount;
82
83 void* _pluginContext;
84
85 CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries.
86 CFMutableDictionaryRef _browsers; // A Dictionary of Browser Dictionaries where the resposible browser is the key.
87 CFMutableDictionaryRef _onAddEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service appearing.
88 CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service disappearing.
89 } BonjourUserEventsPlugin;
90
91 typedef struct {
92 CFIndex refCount;
93 DNSServiceRef browserRef;
94 } NetBrowserInfo;
95
96 #pragma mark -
97 #pragma mark Prototypes
98 #pragma mark -
99 // COM Stuff
100 static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv);
101 static ULONG AddRef(void* instance);
102 static ULONG Release(void* instance);
103
104 static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID);
105 static void Dealloc(BonjourUserEventsPlugin* plugin);
106
107 void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID);
108
109 // Plugin Management
110 static void Install(void* instance);
111 static void ManageEventsCallback(
112 UserEventAgentLaunchdAction action,
113 CFNumberRef token,
114 CFTypeRef eventMatchDict,
115 void * vContext);
116
117
118 // Plugin Guts
119 void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters);
120 void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken);
121
122 NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain);
123 NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef);
124 void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key);
125 void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken);
126
127 // Net Service Browser Stuff
128 void ServiceBrowserCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, const char* replyDomain, void* context);
129 void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary);
130
131 // Convence Stuff
132 const char* CStringFromCFString(CFStringRef string);
133
134 // NetBrowserInfo "Object"
135 NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context);
136 const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info);
137 void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info);
138 Boolean NetBrowserInfoEqual(const void *value1, const void *value2);
139 CFHashCode NetBrowserInfoHash(const void *value);
140 CFStringRef NetBrowserInfoCopyDescription(const void *value);
141
142 static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = {
143 0,
144 NetBrowserInfoRetain,
145 NetBrowserInfoRelease,
146 NetBrowserInfoCopyDescription,
147 NetBrowserInfoEqual,
148 NetBrowserInfoHash
149 };
150
151 static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = {
152 0,
153 NetBrowserInfoRetain,
154 NetBrowserInfoRelease,
155 NetBrowserInfoCopyDescription,
156 NetBrowserInfoEqual
157 };
158
159 // COM type definition goop.
160 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
161 NULL, // Required padding for COM
162 QueryInterface, // Query Interface
163 AddRef, // AddRef()
164 Release, // Release()
165 Install // Install
166 };
167
168 #pragma mark -
169 #pragma mark COM Management
170 #pragma mark -
171
172 /*****************************************************************************
173 *****************************************************************************/
174 static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
175 {
176 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
177
178 // Test the requested ID against the valid interfaces.
179 if(CFEqual(interfaceID, kUserEventAgentInterfaceID))
180 {
181 ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
182 *ppv = myInstance;
183 CFRelease(interfaceID);
184 return S_OK;
185 }
186 else if(CFEqual(interfaceID, IUnknownUUID))
187 {
188 ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
189 *ppv = myInstance;
190 CFRelease(interfaceID);
191 return S_OK;
192 }
193 else // Requested interface unknown, bail with error.
194 {
195 *ppv = NULL;
196 CFRelease(interfaceID);
197 return E_NOINTERFACE;
198 }
199 }
200
201 /*****************************************************************************
202 *****************************************************************************/
203 static ULONG AddRef(void* instance)
204 {
205 BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
206 return ++plugin->_refCount;
207 }
208
209 /*****************************************************************************
210 *****************************************************************************/
211 static ULONG Release(void* instance)
212 {
213 BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
214
215 if (plugin->_refCount != 0)
216 --plugin->_refCount;
217
218 if (plugin->_refCount == 0)
219 {
220 Dealloc(instance);
221 return 0;
222 }
223
224 return plugin->_refCount;
225 }
226
227 /*****************************************************************************
228 * Alloc
229 * -
230 * Functionas as both +[alloc] and -[init] for the plugin. Add any
231 * initalization of member variables here.
232 *****************************************************************************/
233 static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID)
234 {
235 BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin));
236
237 plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
238 plugin->_pluginContext = NULL;
239
240 if (factoryID)
241 {
242 plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID);
243 CFPlugInAddInstanceForFactory(factoryID);
244 }
245
246 plugin->_refCount = 1;
247 plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks);
248 plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
249 plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
250 plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
251
252 return plugin;
253 }
254
255 /*****************************************************************************
256 * Dealloc
257 * -
258 * Much like Obj-C dealloc this method is responsible for releasing any object
259 * this plugin is holding. Unlike ObjC, you call directly free() instead of
260 * [super dalloc].
261 *****************************************************************************/
262 static void Dealloc(BonjourUserEventsPlugin* plugin)
263 {
264 CFUUIDRef factoryID = plugin->_factoryID;
265
266 if (factoryID)
267 {
268 CFPlugInRemoveInstanceForFactory(factoryID);
269 CFRelease(factoryID);
270 }
271
272 if (plugin->_tokenToBrowserMap)
273 CFRelease(plugin->_tokenToBrowserMap);
274
275 if (plugin->_browsers)
276 CFRelease(plugin->_browsers);
277
278 if (plugin->_onAddEvents)
279 CFRelease(plugin->_onAddEvents);
280
281 if (plugin->_onRemoveEvents)
282 CFRelease(plugin->_onRemoveEvents);
283
284 free(plugin);
285 }
286
287 /*******************************************************************************
288 *******************************************************************************/
289 void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
290 {
291 (void)allocator;
292 BonjourUserEventsPlugin * result = NULL;
293
294 if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) {
295 result = Alloc(kUserEventAgentFactoryID);
296 }
297
298 return (void *)result;
299 }
300
301 #pragma mark -
302 #pragma mark Plugin Management
303 #pragma mark -
304 /*****************************************************************************
305 * Install
306 * -
307 * This is invoked once when the plugin is loaded to do initial setup and
308 * allow us to register with launchd. If UserEventAgent crashes, the plugin
309 * will need to be reloaded, and hence this will get invoked again.
310 *****************************************************************************/
311 static void Install(void *instance)
312 {
313 BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
314
315 plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin);
316
317 if (!plugin->_pluginContext)
318 {
319 fprintf(stderr, "%s:%s failed to register for launch events.\n", sPluginIdentifier, __FUNCTION__);
320 return;
321 }
322
323 }
324
325 /*****************************************************************************
326 * ManageEventsCallback
327 * -
328 * This is invoked when launchd loads a event dictionary and needs to inform
329 * us what a daemon / agent is looking for.
330 *****************************************************************************/
331 static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext)
332 {
333 if (action == kUserEventAgentLaunchdAdd)
334 {
335 if (!eventMatchDict)
336 {
337 fprintf(stderr, "%s:%s empty dictionary\n", sPluginIdentifier, __FUNCTION__);
338 return;
339 }
340 if (CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID())
341 {
342 fprintf(stderr, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier, __FUNCTION__, action);
343 return;
344 }
345 // Launchd wants us to add a launch event for this token and matching dictionary.
346 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling AddEventToPlugin", sPluginIdentifier, __FUNCTION__);
347 AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict);
348 }
349 else if (action == kUserEventAgentLaunchdRemove)
350 {
351 // Launchd wants us to remove the event hook we setup for this token / matching dictionary.
352 // Note: eventMatchDict can be NULL for Remove.
353 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier, __FUNCTION__);
354 RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token);
355 }
356 else
357 {
358 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s unknown callback event\n", sPluginIdentifier, __FUNCTION__);
359 }
360 }
361
362
363 #pragma mark -
364 #pragma mark Plugin Guts
365 #pragma mark -
366
367 /*****************************************************************************
368 * AddEventToPlugin
369 * -
370 * This method is invoked when launchd wishes the plugin to setup a launch
371 * event matching the parameters in the dictionary.
372 *****************************************************************************/
373 void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters)
374 {
375 CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey);
376 CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey);
377 CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey);
378 CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey);
379 CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey);
380
381 Boolean onAdd = false;
382 Boolean onRemove = false;
383
384 if (cfOnAdd && CFGetTypeID(cfOnAdd) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd))
385 onAdd = true;
386
387 if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove))
388 onRemove = true;
389
390 // A type is required. If none is specified, BAIL
391 if (!type || CFGetTypeID(type) != CFStringGetTypeID())
392 {
393 fprintf(stderr, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier, __FUNCTION__);
394 return;
395 }
396
397 // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore.
398 if (!onAdd && !onRemove)
399 {
400 fprintf(stderr, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier, __FUNCTION__);
401 return;
402 }
403
404 // If no domain is specified, assume local.
405 if (!domain)
406 {
407 domain = CFSTR("local");
408 }
409 else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fail
410 {
411 fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__);
412 return;
413 }
414
415 // If we have a name filter, but it's not a string. This event is broken, bail.
416 if (name && CFGetTypeID(name) != CFStringGetTypeID())
417 {
418 fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__);
419 return;
420 }
421
422 // Get us a browser
423 NetBrowserInfo* browser = CreateBrowser(plugin, type, domain);
424
425 if (!browser)
426 {
427 fprintf(stderr, "%s:%s cannot create browser\n", sPluginIdentifier, __FUNCTION__);
428 return;
429 }
430
431 // Create Event Dictionary
432 CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
433
434 // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs
435 // the token and UserEventAgentSetFireEvent needs both the token and the dictionary
436 CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken);
437 CFDictionarySetValue(eventDictionary, sLaunchdDictKey, eventParameters);
438
439 if (name)
440 CFDictionarySetValue(eventDictionary, sServiceNameKey, name);
441
442 // Add to the correct dictionary.
443 if (onAdd)
444 {
445 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to AddEvents", sPluginIdentifier, __FUNCTION__);
446 AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser);
447 }
448
449 if (onRemove)
450 {
451 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier, __FUNCTION__);
452 AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser);
453 }
454
455 // Add Token Mapping
456 CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser);
457
458 // Release Memory
459 CFRelease(eventDictionary);
460 }
461
462 /*****************************************************************************
463 * RemoveEventFromPlugin
464 * -
465 * This method is invoked when launchd wishes the plugin to setup a launch
466 * event matching the parameters in the dictionary.
467 *****************************************************************************/
468 void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken)
469 {
470 NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken);
471 Boolean othersUsingBrowser = false;
472
473 if (!browser)
474 {
475 long long value = 0;
476 CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value);
477 fprintf(stderr, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier, __FUNCTION__, value);
478 return;
479 }
480
481 CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser);
482 CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser);
483
484 if (onAddEvents)
485 {
486 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier, __FUNCTION__);
487 RemoveEventFromArray(onAddEvents, launchdToken);
488
489 // Is the array now empty, clean up
490 if (CFArrayGetCount(onAddEvents) == 0)
491 {
492 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier, __FUNCTION__);
493 CFDictionaryRemoveValue(plugin->_onAddEvents, browser);
494 }
495 }
496
497 if (onRemoveEvents)
498 {
499 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier, __FUNCTION__);
500 RemoveEventFromArray(onRemoveEvents, launchdToken);
501
502 // Is the array now empty, clean up
503 if (CFArrayGetCount(onRemoveEvents) == 0)
504 {
505 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier, __FUNCTION__);
506 CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser);
507 }
508 }
509
510 // Remove ourselves from the token dictionary.
511 CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken);
512
513 // Check to see if anyone else is using this browser.
514 CFIndex i;
515 CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap);
516 NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
517
518 // Fetch the values of the token dictionary
519 CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers);
520
521 for (i = 0; i < count; ++i)
522 {
523 if (NetBrowserInfoEqual(browsers[i], browser))
524 {
525 othersUsingBrowser = true;
526 break;
527 }
528 }
529
530 // If no one else is useing our browser, clean up!
531 if (!othersUsingBrowser)
532 {
533 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier, __FUNCTION__, browser);
534 CFDictionaryRemoveValue(plugin->_browsers, browser); // This triggers release and dealloc of the browser
535 }
536 else
537 {
538 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decrementing browsers %p count", sPluginIdentifier, __FUNCTION__, browser);
539 // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser)
540 NetBrowserInfoRelease(NULL, browser);
541 }
542
543 free(browsers);
544 }
545
546
547 /*****************************************************************************
548 * CreateBrowser
549 * -
550 * This method returns a NetBrowserInfo that is looking for a type of
551 * service in a domain. If no browser exists, it will create one and return it.
552 *****************************************************************************/
553 NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain)
554 {
555 CFIndex i;
556 CFIndex count = CFDictionaryGetCount(plugin->_browsers);
557 NetBrowserInfo* browser = NULL;
558 CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef));
559 NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
560
561 // Fetch the values of the browser dictionary
562 CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts);
563
564
565 // Loop thru the browsers list and see if we can find a matching one.
566 for (i = 0; i < count; ++i)
567 {
568 CFDictionaryRef browserDict = dicts[i];
569
570 CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey);
571 CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey);
572
573 // If we have a matching browser, break
574 if ((CFStringCompare(browserType, type, kCFCompareCaseInsensitive) == kCFCompareEqualTo) &&
575 (CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
576 {
577 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: found a duplicate browser\n", sPluginIdentifier, __FUNCTION__);
578 browser = browsers[i];
579 NetBrowserInfoRetain(NULL, browser);
580 break;
581 }
582 }
583
584 // No match found, lets create one!
585 if (!browser)
586 {
587
588 browser = NetBrowserInfoCreate(type, domain, plugin);
589
590 if (!browser)
591 {
592 fprintf(stderr, "%s:%s failed to search for %s.%s", sPluginIdentifier, __FUNCTION__, CStringFromCFString(type), CStringFromCFString(domain));
593 free(dicts);
594 free(browsers);
595 return NULL;
596 }
597
598 // Service browser created, lets add this to ourselves to the dictionary.
599 CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
600
601 CFDictionarySetValue(browserDict, sServiceTypeKey, type);
602 CFDictionarySetValue(browserDict, sServiceDomainKey, domain);
603
604 // Add the dictionary to the browsers dictionary.
605 CFDictionarySetValue(plugin->_browsers, browser, browserDict);
606
607 NetBrowserInfoRelease(NULL, browser);
608
609 // Release Memory
610 CFRelease(browserDict);
611 }
612
613 free(dicts);
614 free(browsers);
615
616 return browser;
617 }
618
619 /*****************************************************************************
620 * BrowserForSDRef
621 * -
622 * This method returns a NetBrowserInfo that matches the calling SDRef passed
623 * in via the callback.
624 *****************************************************************************/
625 NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef)
626 {
627 CFIndex i;
628 CFIndex count = CFDictionaryGetCount(plugin->_browsers);
629 NetBrowserInfo* browser = NULL;
630 NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
631
632 // Fetch the values of the browser dictionary
633 CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL);
634
635 // Loop thru the browsers list and see if we can find a matching one.
636 for (i = 0; i < count; ++i)
637 {
638 NetBrowserInfo* currentBrowser = browsers[i];
639
640 if (currentBrowser->browserRef == sdRef)
641 {
642 browser = currentBrowser;
643 break;
644 }
645 }
646
647
648 free(browsers);
649
650 return browser;
651 }
652
653 /*****************************************************************************
654 * AddEventDictionary
655 * -
656 * Adds a event to a browser's event dictionary
657 *****************************************************************************/
658
659 void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key)
660 {
661 CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key);
662
663 if (!eventsForBrowser) // We have no events for this browser yet, lets add him.
664 {
665 eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
666 CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser);
667 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s creating a new array", sPluginIdentifier, __FUNCTION__);
668 }
669 else
670 {
671 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Incrementing refcount", sPluginIdentifier, __FUNCTION__);
672 CFRetain(eventsForBrowser);
673 }
674
675 CFArrayAppendValue(eventsForBrowser, eventDict);
676 CFRelease(eventsForBrowser);
677 }
678
679 /*****************************************************************************
680 * RemoveEventFromArray
681 * -
682 * Searches a Array of Event Dictionaries to find one with a matching launchd
683 * token and remove it.
684 *****************************************************************************/
685
686 void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken)
687 {
688 CFIndex i;
689 CFIndex count = CFArrayGetCount(array);
690
691 // Loop thru looking for us.
692 for (i = 0; i < count; )
693 {
694 CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i);
695 CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
696
697 if (CFEqual(token, launchdToken)) // This is the same event?
698 {
699 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s found token", sPluginIdentifier, __FUNCTION__);
700 CFArrayRemoveValueAtIndex(array, i); // Remove the event,
701 break; // The token should only exist once, so it makes no sense to continue.
702 }
703 else
704 {
705 ++i; // If it's not us, advance.
706 }
707 }
708 if (i == count) asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s did not find token", sPluginIdentifier, __FUNCTION__);
709 }
710
711 #pragma mark -
712 #pragma mark Net Service Browser Stuff
713 #pragma mark -
714
715 /*****************************************************************************
716 * ServiceBrowserCallback
717 * -
718 * This method is the heart of the plugin. It's the runloop callback annoucing
719 * the appearence and disappearance of network services.
720 *****************************************************************************/
721
722 void ServiceBrowserCallback (DNSServiceRef sdRef,
723 DNSServiceFlags flags,
724 uint32_t interfaceIndex,
725 DNSServiceErrorType errorCode,
726 const char* serviceName,
727 const char* regtype,
728 const char* replyDomain,
729 void* context )
730 {
731 (void)interfaceIndex;
732 (void)regtype;
733 (void)replyDomain;
734 BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context;
735 NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef);
736
737 if (!browser) // Missing browser?
738 {
739 fprintf(stderr, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier, __FUNCTION__);
740 return;
741 }
742
743 if (errorCode != kDNSServiceErr_NoError)
744 {
745 fprintf(stderr, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier, __FUNCTION__, errorCode);
746 return;
747 }
748
749 CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8);
750
751 if (flags & kDNSServiceFlagsAdd)
752 {
753 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier, __FUNCTION__);
754 HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents);
755 }
756 else
757 {
758 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier, __FUNCTION__);
759 HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents);
760 }
761
762 CFRelease(cfServiceName);
763 }
764
765 /*****************************************************************************
766 * HandleTemporaryEventsForService
767 * -
768 * This method handles the firing of one shot events. Aka. Events that are
769 * signaled when a service appears / disappears. They have a temporarly
770 * signaled state.
771 *****************************************************************************/
772 void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary)
773 {
774 CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in.
775 CFIndex i;
776 CFIndex count;
777
778 if (!events) // Somehow we have a orphan browser...
779 return;
780
781 count = CFArrayGetCount(events);
782
783 // Go thru the events and run filters, notifity if they pass.
784 for (i = 0; i < count; ++i)
785 {
786 CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i);
787 CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey);
788 CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
789 CFDictionaryRef dict = (CFDictionaryRef) CFDictionaryGetValue(eventDict, sLaunchdDictKey);
790
791 // Currently we only filter on service name, that makes this as simple as...
792 if (!eventServiceName || CFEqual(serviceName, eventServiceName))
793 {
794 uint64_t tokenUint64;
795 // Signal Event: This is edge trigger. When the action has been taken, it will not
796 // be remembered anymore.
797
798 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier, __FUNCTION__);
799 CFNumberGetValue(token, kCFNumberLongLongType, &tokenUint64);
800
801 xpc_object_t jobRequest = _CFXPCCreateXPCObjectFromCFObject(dict);
802
803 UserEventAgentFireEvent(plugin->_pluginContext, tokenUint64, jobRequest);
804 xpc_release(jobRequest);
805 }
806 }
807 }
808
809 #pragma mark -
810 #pragma mark Convenience
811 #pragma mark -
812
813 /*****************************************************************************
814 * CStringFromCFString
815 * -
816 * Silly convenence function for dealing with non-critical CFSTR -> cStr
817 * conversions.
818 *****************************************************************************/
819
820 const char* CStringFromCFString(CFStringRef string)
821 {
822 const char* defaultString = "??????";
823 const char* cstring;
824
825 if (!string)
826 return defaultString;
827
828 cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
829
830 return (cstring) ? cstring : defaultString;
831
832 }
833
834 #pragma mark -
835 #pragma mark NetBrowserInfo "Object"
836 #pragma mark -
837 /*****************************************************************************
838 * NetBrowserInfoCreate
839 * -
840 * The method creates a NetBrowserInfo Object and initalizes it.
841 *****************************************************************************/
842 NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context)
843 {
844 NetBrowserInfo* outObj = NULL;
845 DNSServiceRef browserRef = NULL;
846 char* cServiceType = NULL;
847 char* cDomain = NULL;
848 Boolean success = true;
849
850 CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8);
851 cServiceType = calloc(serviceSize, 1);
852 success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8);
853
854
855 if (domain)
856 {
857 CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8);
858 cDomain = calloc(serviceSize, 1);
859 success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
860 }
861
862 if (!success)
863 {
864 fprintf(stderr, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier, __FUNCTION__);
865 free(cServiceType);
866
867 if (cDomain)
868 free(cDomain);
869
870 return NULL;
871 }
872
873 DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context);
874
875 if (err != kDNSServiceErr_NoError)
876 {
877 fprintf(stderr, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier, __FUNCTION__, cServiceType, cDomain);
878 free(cServiceType);
879
880 if (cDomain)
881 free(cDomain);
882
883 return NULL;
884 }
885
886 DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue());
887
888
889 outObj = malloc(sizeof(NetBrowserInfo));
890
891 outObj->refCount = 1;
892 outObj->browserRef = browserRef;
893
894 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: created new object %p", sPluginIdentifier, __FUNCTION__, outObj);
895
896 free(cServiceType);
897
898 if (cDomain)
899 free(cDomain);
900
901 return outObj;
902 }
903
904 /*****************************************************************************
905 * NetBrowserInfoRetain
906 * -
907 * The method retains a NetBrowserInfo object.
908 *****************************************************************************/
909 const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info)
910 {
911 (void)allocator;
912 NetBrowserInfo* obj = (NetBrowserInfo*)info;
913
914 if (!obj)
915 return NULL;
916
917 ++obj->refCount;
918 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount);
919
920 return obj;
921 }
922
923 /*****************************************************************************
924 * NetBrowserInfoRelease
925 * -
926 * The method releases a NetBrowserInfo object.
927 *****************************************************************************/
928 void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info)
929 {
930 (void)allocator;
931 NetBrowserInfo* obj = (NetBrowserInfo*)info;
932
933 if (!obj)
934 return;
935
936 if (obj->refCount == 1)
937 {
938 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier, __FUNCTION__, obj->browserRef);
939 DNSServiceRefDeallocate(obj->browserRef);
940 free(obj);
941 }
942 else
943 {
944 --obj->refCount;
945 asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount);
946 }
947
948 }
949
950 /*****************************************************************************
951 * NetBrowserInfoEqual
952 * -
953 * The method is used to compare two NetBrowserInfo objects for equality.
954 *****************************************************************************/
955 Boolean NetBrowserInfoEqual(const void *value1, const void *value2)
956 {
957 NetBrowserInfo* obj1 = (NetBrowserInfo*)value1;
958 NetBrowserInfo* obj2 = (NetBrowserInfo*)value2;
959
960 if (obj1->browserRef == obj2->browserRef)
961 return true;
962
963 return false;
964 }
965
966 /*****************************************************************************
967 * NetBrowserInfoHash
968 * -
969 * The method is used to make a hash for the object. We can cheat and use the
970 * browser pointer.
971 *****************************************************************************/
972 CFHashCode NetBrowserInfoHash(const void *value)
973 {
974 return (CFHashCode)((NetBrowserInfo*)value)->browserRef;
975 }
976
977
978 /*****************************************************************************
979 * NetBrowserInfoCopyDescription
980 * -
981 * Make CF happy.
982 *****************************************************************************/
983 CFStringRef NetBrowserInfoCopyDescription(const void *value)
984 {
985 (void)value;
986 return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8);
987 }
988