Connections opened by listeners now close correctly.
5 // Created by Jens Alfke on 1/22/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
9 #import "MYBonjourService.h"
10 #import "MYBonjourQuery.h"
11 #import "MYAddressLookup.h"
13 #import "ConcurrentOperation.h"
16 #import "ExceptionUtils.h"
20 NSString* const kBonjourServiceResolvedAddressesNotification = @"BonjourServiceResolvedAddresses";
23 @interface MYBonjourService ()
27 @implementation MYBonjourService
30 - (id) initWithName: (NSString*)serviceName
32 domain: (NSString*)domain
33 interface: (UInt32)interfaceIndex
40 _name = [serviceName copy];
42 _domain = [domain copy];
43 _fullName = [[[self class] fullNameOfService: _name ofType: _type inDomain: _domain] retain];
44 _interfaceIndex = interfaceIndex;
52 [_addressLookup stop];
53 [_addressLookup release];
63 @synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, interfaceIndex=_interfaceIndex;
66 - (NSString*) description {
67 return $sprintf(@"%@[%@]", self.class,self.fullName);
71 - (NSComparisonResult) compare: (id)obj {
72 return [_name caseInsensitiveCompare: [obj name]];
75 - (BOOL) isEqual: (id)obj {
76 if ([obj isKindOfClass: [MYBonjourService class]]) {
77 MYBonjourService *service = obj;
78 return [_name caseInsensitiveCompare: [service name]] == 0
79 && $equal(_type, service->_type)
80 && $equal(_domain, service->_domain)
81 && _interfaceIndex == service->_interfaceIndex;
88 return _name.hash ^ _type.hash ^ _domain.hash;
93 LogTo(Bonjour,@"Added %@",self);
97 LogTo(Bonjour,@"Removed %@",self);
104 [_addressLookup stop];
108 - (void) priv_finishResolve {
109 // If I haven't finished my resolve yet, run it *synchronously* now so I can return a valid value:
110 if (!_startedResolve )
116 - (NSString*) hostname {
117 if (!_hostname) [self priv_finishResolve];
122 if (!_port) [self priv_finishResolve];
128 #pragma mark TXT RECORD:
131 - (NSDictionary*) txtRecord {
133 _txtQuery = [[MYBonjourQuery alloc] initWithBonjourService: self
134 recordType: kDNSServiceType_TXT];
135 _txtQuery.continuous = YES;
141 - (void) txtRecordChanged {
142 // no-op (this is here for subclassers to override)
145 - (NSString*) txtStringForKey: (NSString*)key {
146 NSData *value = [self.txtRecord objectForKey: key];
149 if( ! [value isKindOfClass: [NSData class]] ) {
150 Warn(@"TXT dictionary has unexpected value type: %@",value.class);
153 NSString *str = [[NSString alloc] initWithData: value encoding: NSUTF8StringEncoding];
155 str = [[NSString alloc] initWithData: value encoding: NSWindowsCP1252StringEncoding];
156 return [str autorelease];
159 - (void) setTxtData: (NSData*)txtData {
160 NSDictionary *txtRecord = txtData ?[NSNetService dictionaryFromTXTRecordData: txtData] :nil;
161 if (!$equal(txtRecord,_txtRecord)) {
162 LogTo(Bonjour,@"%@ TXT = %@", self,txtRecord);
163 [self willChangeValueForKey: @"txtRecord"];
164 setObj(&_txtRecord, txtRecord);
165 [self didChangeValueForKey: @"txtRecord"];
166 [self txtRecordChanged];
171 - (void) queryDidUpdate: (MYBonjourQuery*)query {
172 if (query==_txtQuery)
173 [self setTxtData: query.recordData];
178 #pragma mark HOSTNAME/PORT RESOLUTION:
181 - (void) priv_resolvedHostname: (NSString*)hostname
183 txtRecord: (NSData*)txtData
185 LogTo(Bonjour, @"%@: hostname=%@, port=%u, txt=%u bytes",
186 self, hostname, port, txtData.length);
188 // Don't call a setter method to set these properties: the getters are synchronous, so
189 // I might already be blocked in a call to one of them, in which case creating a KV
190 // notification could cause trouble...
191 _hostname = hostname.copy;
194 // TXT getter is async, though, so I can use a setter to announce the data's availability:
195 [self setTxtData: txtData];
199 static void resolveCallback(DNSServiceRef sdRef,
200 DNSServiceFlags flags,
201 uint32_t interfaceIndex,
202 DNSServiceErrorType errorCode,
203 const char *fullname,
204 const char *hosttarget,
207 const unsigned char *txtRecord,
210 MYBonjourService *service = context;
212 //LogTo(Bonjour, @"resolveCallback for %@ (err=%i)", service,errorCode);
214 NSData *txtData = nil;
216 txtData = [NSData dataWithBytes: txtRecord length: txtLen];
217 [service priv_resolvedHostname: [NSString stringWithUTF8String: hosttarget]
221 }catchAndReport(@"MYBonjourResolver query callback");
222 [service gotResponse: errorCode];
226 - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
227 _startedResolve = YES;
228 return DNSServiceResolve(sdRefPtr,
229 kDNSServiceFlagsShareConnection,
231 _name.UTF8String, _type.UTF8String, _domain.UTF8String,
232 &resolveCallback, self);
236 - (MYAddressLookup*) addressLookup {
237 if (!_addressLookup) {
238 // Create the lookup the first time this is called:
239 _addressLookup = [[MYAddressLookup alloc] initWithHostname: self.hostname];
240 _addressLookup.port = _port;
241 _addressLookup.interfaceIndex = _interfaceIndex;
243 // (Re)start the lookup if it's expired:
244 if (_addressLookup && _addressLookup.timeToLive <= 0.0)
245 [_addressLookup start];
246 return _addressLookup;
250 - (MYBonjourQuery*) queryForRecord: (UInt16)recordType {
251 MYBonjourQuery *query = [[[MYBonjourQuery alloc] initWithBonjourService: self recordType: recordType]
253 return [query start] ?query :nil;
262 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
264 Redistribution and use in source and binary forms, with or without modification, are permitted
265 provided that the following conditions are met:
267 * Redistributions of source code must retain the above copyright notice, this list of conditions
268 and the following disclaimer.
269 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
270 and the following disclaimer in the documentation and/or other materials provided with the
273 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
274 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
275 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
276 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
277 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
278 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
280 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.