source: rtems-libbsd/mDNSResponder/mDNSMacOSX/BonjourEvents.c @ f761b29

55-freebsd-126-freebsd-12
Last change on this file since f761b29 was f761b29, checked in by Sebastian Huber <sebastian.huber@…>, on 09/19/18 at 06:52:21

mDNSResponder: Update to v625.41.2

The sources can be obtained via:

https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-625.41.2.tar.gz

Update #3522.

  • Property mode set to 100644
File size: 36.2 KB
Line 
1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010-2015 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 -
31static const char*          sPluginIdentifier       = "com.apple.bonjour.events";
32
33// PLIST Keys
34static const CFStringRef sServiceNameKey         = CFSTR("ServiceName");
35static const CFStringRef sServiceTypeKey         = CFSTR("ServiceType");
36static const CFStringRef sServiceDomainKey       = CFSTR("ServiceDomain");
37
38static const CFStringRef sOnServiceAddKey        = CFSTR("OnServiceAdd");
39static const CFStringRef sOnServiceRemoveKey     = CFSTR("OnServiceRemove");
40
41static const CFStringRef sLaunchdTokenKey        = CFSTR("LaunchdToken");
42static 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
78typedef 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
91typedef struct {
92    CFIndex refCount;
93    DNSServiceRef browserRef;
94} NetBrowserInfo;
95
96#pragma mark -
97#pragma mark Prototypes
98#pragma mark -
99// COM Stuff
100static HRESULT  QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv);
101static ULONG    AddRef(void* instance);
102static ULONG    Release(void* instance);
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(
112    UserEventAgentLaunchdAction action,
113    CFNumberRef token,
114    CFTypeRef eventMatchDict,
115    void                      * vContext);
116
117
118// Plugin Guts
119void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters);
120void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken);
121
122NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain);
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);
130
131// Convence Stuff
132const char* CStringFromCFString(CFStringRef string);
133
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);
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
149};
150
151static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = {
152    0,
153    NetBrowserInfoRetain,
154    NetBrowserInfoRelease,
155    NetBrowserInfoCopyDescription,
156    NetBrowserInfoEqual
157};
158
159// COM type definition goop.
160static 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*****************************************************************************/
174static 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*****************************************************************************/
203static ULONG AddRef(void* instance)
204{
205    BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
206    return ++plugin->_refCount;
207}
208
209/*****************************************************************************
210*****************************************************************************/
211static 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*****************************************************************************/
233static 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*****************************************************************************/
262static 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*******************************************************************************/
289void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
290{
291    (void)allocator;
292    BonjourUserEventsPlugin * result = NULL;
293
294    if (typeID && CFEqual(typeID, kUserEventAgentTypeID))
295    {
296        result = Alloc(kUserEventAgentFactoryID);
297    }
298
299    return (void *)result;
300}
301
302#pragma mark -
303#pragma mark Plugin Management
304#pragma mark -
305/*****************************************************************************
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*****************************************************************************/
312static void Install(void *instance)
313{
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
324}
325
326/*****************************************************************************
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*****************************************************************************/
332static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext)
333{
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.
347        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling AddEventToPlugin", sPluginIdentifier, __FUNCTION__);
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.
354        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier, __FUNCTION__);
355        RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token);
356    }
357    else
358    {
359        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s unknown callback event\n", sPluginIdentifier, __FUNCTION__);
360    }
361}
362
363
364#pragma mark -
365#pragma mark Plugin Guts
366#pragma mark -
367
368/*****************************************************************************
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*****************************************************************************/
374void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters)
375{
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    {
446        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to AddEvents", sPluginIdentifier, __FUNCTION__);
447        AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser);
448    }
449
450    if (onRemove)
451    {
452        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier, __FUNCTION__);
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);
461}
462
463/*****************************************************************************
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)
470{
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    {
487        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier, __FUNCTION__);
488        RemoveEventFromArray(onAddEvents, launchdToken);
489
490        // Is the array now empty, clean up
491        if (CFArrayGetCount(onAddEvents) == 0)
492        {
493            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier, __FUNCTION__);
494            CFDictionaryRemoveValue(plugin->_onAddEvents, browser);
495        }
496    }
497
498    if (onRemoveEvents)
499    {
500        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier, __FUNCTION__);
501        RemoveEventFromArray(onRemoveEvents, launchdToken);
502
503        // Is the array now empty, clean up
504        if (CFArrayGetCount(onRemoveEvents) == 0)
505        {
506            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier, __FUNCTION__);
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    {
534        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier, __FUNCTION__, browser);
535        CFDictionaryRemoveValue(plugin->_browsers, browser); // This triggers release and dealloc of the browser
536    }
537    else
538    {
539        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decrementing browsers %p count", sPluginIdentifier, __FUNCTION__, browser);
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);
545}
546
547
548/*****************************************************************************
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*****************************************************************************/
554NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain)
555{
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        {
578            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: found a duplicate browser\n", sPluginIdentifier, __FUNCTION__);
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
608        // Release Memory
609        CFRelease(browserDict);
610    }
611
612    free(dicts);
613    free(browsers);
614
615    return browser;
616}
617
618/*****************************************************************************
619* BrowserForSDRef
620* -
621* This method returns a NetBrowserInfo that matches the calling SDRef passed
622* in via the callback.
623*****************************************************************************/
624NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef)
625{
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;
650}
651
652/*****************************************************************************
653* AddEventDictionary
654* -
655* Adds a event to a browser's event dictionary
656*****************************************************************************/
657
658void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key)
659{
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);
666        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s creating a new array", sPluginIdentifier, __FUNCTION__);
667    }
668    else
669    {
670        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Incrementing refcount", sPluginIdentifier, __FUNCTION__);
671        CFRetain(eventsForBrowser);
672    }
673
674    CFArrayAppendValue(eventsForBrowser, eventDict);
675    CFRelease(eventsForBrowser);
676}
677
678/*****************************************************************************
679* RemoveEventFromArray
680* -
681* Searches a Array of Event Dictionaries to find one with a matching launchd
682* token and remove it.
683*****************************************************************************/
684
685void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken)
686{
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        {
698            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s found token", sPluginIdentifier, __FUNCTION__);
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    }
707    if (i == count) asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s did not find token", sPluginIdentifier, __FUNCTION__);
708}
709
710#pragma mark -
711#pragma mark Net Service Browser Stuff
712#pragma mark -
713
714/*****************************************************************************
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 )
729{
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);
749    if (cfServiceName == NULL)
750    {
751        static int msgCount = 0;
752        if (msgCount < 1000)
753        {
754            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Can not create CFString for serviceName %s", sPluginIdentifier, __FUNCTION__, serviceName);
755            msgCount++;
756        }
757        return;
758    }
759
760    if (flags & kDNSServiceFlagsAdd)
761    {
762        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier, __FUNCTION__);
763        HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents);
764    }
765    else
766    {
767        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier, __FUNCTION__);
768        HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents);
769    }
770
771    CFRelease(cfServiceName);
772}
773
774/*****************************************************************************
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*****************************************************************************/
781void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary)
782{
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
807            asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier, __FUNCTION__);
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    }
816}
817
818#pragma mark -
819#pragma mark Convenience
820#pragma mark -
821
822/*****************************************************************************
823* CStringFromCFString
824* -
825* Silly convenence function for dealing with non-critical CFSTR -> cStr
826* conversions.
827*****************************************************************************/
828
829const char* CStringFromCFString(CFStringRef string)
830{
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
841}
842
843#pragma mark -
844#pragma mark NetBrowserInfo "Object"
845#pragma mark -
846/*****************************************************************************
847* NetBrowserInfoCreate
848* -
849* The method creates a NetBrowserInfo Object and initalizes it.
850*****************************************************************************/
851NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context)
852{
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);
867        if (domainSize)
868        {
869            cDomain = calloc(domainSize, 1);
870            success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
871        }
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
906    asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: created new object %p", sPluginIdentifier, __FUNCTION__, outObj);
907
908    free(cServiceType);
909
910    if (cDomain)
911        free(cDomain);
912
913    return outObj;
914}
915
916/*****************************************************************************
917* NetBrowserInfoRetain
918* -
919* The method retains a NetBrowserInfo object.
920*****************************************************************************/
921const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info)
922{
923    (void)allocator;
924    NetBrowserInfo* obj = (NetBrowserInfo*)info;
925
926    if (!obj)
927        return NULL;
928
929    ++obj->refCount;
930    asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount);
931
932    return obj;
933}
934
935/*****************************************************************************
936* NetBrowserInfoRelease
937* -
938* The method releases a NetBrowserInfo object.
939*****************************************************************************/
940void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info)
941{
942    (void)allocator;
943    NetBrowserInfo* obj = (NetBrowserInfo*)info;
944
945    if (!obj)
946        return;
947
948    if (obj->refCount == 1)
949    {
950        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier, __FUNCTION__, obj->browserRef);
951        DNSServiceRefDeallocate(obj->browserRef);
952        free(obj);
953    }
954    else
955    {
956        --obj->refCount;
957        asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount);
958    }
959
960}
961
962/*****************************************************************************
963* NetBrowserInfoEqual
964* -
965* The method is used to compare two NetBrowserInfo objects for equality.
966*****************************************************************************/
967Boolean NetBrowserInfoEqual(const void *value1, const void *value2)
968{
969    NetBrowserInfo* obj1 = (NetBrowserInfo*)value1;
970    NetBrowserInfo* obj2 = (NetBrowserInfo*)value2;
971
972    if (obj1->browserRef == obj2->browserRef)
973        return true;
974
975    return false;
976}
977
978/*****************************************************************************
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)
985{
986    return (CFHashCode)((NetBrowserInfo*)value)->browserRef;
987}
988
989
990/*****************************************************************************
991* NetBrowserInfoCopyDescription
992* -
993* Make CF happy.
994*****************************************************************************/
995CFStringRef NetBrowserInfoCopyDescription(const void *value)
996{
997    (void)value;
998    return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8);
999}
1000
Note: See TracBrowser for help on using the repository browser.