Imported Upstream version 878.70.2
[platform/upstream/mdnsresponder.git] / mDNSMacOSX / DomainBrowser / Shared / _CNDomainBrowser.m
1 /*
2  *
3  * Copyright (c) 2016 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 #import "_CNDomainBrowser.h"
19 #import "CNDomainBrowserPathUtils.h"
20 #include <dns_sd.h>
21
22 const NSString *    _CNSubDomainKey_defaultFlag         = @"defaultFlag";
23 const NSString *    _CNSubDomainKey_subPath             = @"subPath";
24 const NSString *    _CNSubDomainKey_reverseDomainPath   = @"reverseDomainPath";
25
26 @interface _CNDomainBrowser ()
27
28 @property (assign) DNSServiceRef                    browseDomainR;
29 @property (strong) NSMutableDictionary *            browseDomainD;
30
31 @property (weak)   id<_CNDomainBrowserDelegate>    delegate;
32
33 @end
34
35 @implementation _CNDomainBrowser
36
37 void enumReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context);
38
39 - (instancetype)initWithDelegate:(id<_CNDomainBrowserDelegate>)delegate
40 {
41     if (self = [super init])
42     {
43         _delegate = delegate;
44         [self _commonInit];
45     }
46     return(self);
47 }
48
49 - (void)_commonInit
50 {
51     self.browseDomainD = [NSMutableDictionary dictionary];
52     self.callbackQueue = dispatch_get_main_queue();
53 }
54
55 - (void)dealloc
56 {
57     [self stopBrowser];
58 }
59
60 - (void)startBrowser
61 {
62     if (!_browseDomainR)
63     {
64         dispatch_async(dispatch_get_main_queue(), ^{
65             dispatch_queue_t queue = dispatch_queue_create("DNSServiceEnumerateDomains", DISPATCH_QUEUE_PRIORITY_DEFAULT);
66             dispatch_set_context(queue, (void *)CFBridgingRetain(self));
67             dispatch_set_finalizer_f(queue, finalizer);
68             
69             DNSServiceRef ref;
70             if (DNSServiceEnumerateDomains(&ref, _browseRegistration ? kDNSServiceFlagsRegistrationDomains : kDNSServiceFlagsBrowseDomains, 0, enumReply, (__bridge void *)self))
71                 NSLog(@"DNSServiceEnumerateDomains failed");
72             else
73             {
74                 _browseDomainR = ref;
75                 (void)DNSServiceSetDispatchQueue(_browseDomainR, queue);
76             }
77         });
78     }
79 }
80
81 - (void)stopBrowser
82 {    
83     if (_browseDomainR)
84     {
85         DNSServiceRefDeallocate(_browseDomainR);
86         _browseDomainR = nil;
87     }
88 }
89
90 - (NSArray *)defaultDomainPath
91 {
92     NSArray * revDomainArray = nil;
93     
94     NSArray *defaults = [[self.browseDomainD allValues] filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"(%K == %@)", _CNSubDomainKey_defaultFlag, @YES]];
95     if (defaults.count)     revDomainArray = defaults[0][_CNSubDomainKey_reverseDomainPath];
96     if (!revDomainArray)        revDomainArray = [NSArray arrayWithObject: @"local"];   //      If no defaults found
97     
98     return(revDomainArray);
99 }
100
101 - (NSArray *)flattenedDNSDomains
102 {
103     return([self.browseDomainD allKeys]);
104 }
105
106 - (NSArray *)subDomainsAtDomainPath:(NSArray *)domainPath
107 {
108     NSMutableDictionary * subs = [NSMutableDictionary dictionary];
109     for (NSDictionary * next in [self.browseDomainD allValues])
110     {
111         NSArray * bdomain = next[_CNSubDomainKey_reverseDomainPath];
112         if (bdomain.count > domainPath.count)
113         {
114             BOOL        match = YES;
115             for (NSUInteger i = 0 ; i < domainPath.count ; i++)
116             {
117                 if (![bdomain[i] isEqualToString: domainPath[i]])       { match = NO;   break; }
118             }
119             if (match)
120             {
121                 NSString * key = bdomain[domainPath.count];
122                 [subs setObject: @{ _CNSubDomainKey_subPath: key, _CNSubDomainKey_defaultFlag: next[_CNSubDomainKey_defaultFlag] } forKey: key];
123             }
124         }
125     }
126     return([subs allValues]);
127 }
128
129 - (void) reloadBrowser
130 {
131     if ([_delegate respondsToSelector: @selector(bonjourBrowserDomainUpdate:)])
132     {
133         dispatch_async(self.callbackQueue, ^{
134             [_delegate bonjourBrowserDomainUpdate: [self defaultDomainPath]];
135         });
136     }
137 }
138
139 - (BOOL)isBrowsing
140 {
141     return(_browseDomainR != nil);
142 }
143
144 #pragma mark - Dispatch
145
146 static void finalizer(void * context)
147 {
148     _CNDomainBrowser *self = (__bridge _CNDomainBrowser *)context;
149     NSLog(@"finalizer: %@", self);
150     (void)CFBridgingRelease((__bridge void *)self);
151 }
152
153 #pragma mark - Commands
154
155 #pragma mark - Static Callbacks
156
157 void enumReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
158                const char *replyDomain, void *context)
159 {
160         (void)sdRef;
161         (void)interfaceIndex;
162         (void)errorCode;
163         
164     if (!*replyDomain) return;
165     
166     _CNDomainBrowser *self = (__bridge _CNDomainBrowser *)context;
167     NSString *key = [NSString stringWithUTF8String: replyDomain];
168     
169     if (self.ignoreLocal && [key isEqualToString: @"local."])   return;
170     if (self.ignoreBTMM && [key hasSuffix: @".members.btmm.icloud.com."])   return;
171     
172     if (!(flags & kDNSServiceFlagsAdd))
173     {
174         [self.browseDomainD removeObjectForKey:key];
175     }
176     else
177     {
178         NSArray * pathArray = DNSDomainToDomainPath(key);
179         [self.browseDomainD setObject: @{ _CNSubDomainKey_reverseDomainPath: pathArray,
180                                           _CNSubDomainKey_defaultFlag: (flags & kDNSServiceFlagsDefault) ? @YES : @NO }
181                                forKey: key];
182     }
183     
184     if (!(flags & kDNSServiceFlagsMoreComing))
185     {
186         [self reloadBrowser];
187     }
188 }
189
190 @end