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