Merged Jens' latest changes.
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 ()
24 @property (copy) NSString *hostname;
25 @property UInt16 port;
29 @implementation MYBonjourService
32 - (id) initWithName: (NSString*)serviceName
34 domain: (NSString*)domain
35 interface: (UInt32)interfaceIndex
42 _name = [serviceName copy];
44 _domain = [domain copy];
45 _fullName = [[[self class] fullNameOfService: _name ofType: _type inDomain: _domain] retain];
46 _interfaceIndex = interfaceIndex;
54 [_addressLookup stop];
55 [_addressLookup release];
65 @synthesize name=_name, type=_type, domain=_domain, fullName=_fullName,
66 hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex;
69 - (NSString*) description {
70 return $sprintf(@"%@[%@]", self.class,self.fullName);
74 - (NSComparisonResult) compare: (id)obj {
75 return [_name caseInsensitiveCompare: [obj name]];
78 - (BOOL) isEqual: (id)obj {
79 if ([obj isKindOfClass: [MYBonjourService class]]) {
80 MYBonjourService *service = obj;
81 return [_name caseInsensitiveCompare: [service name]] == 0
82 && $equal(_type, service->_type)
83 && $equal(_domain, service->_domain)
84 && _interfaceIndex == service->_interfaceIndex;
91 return _name.hash ^ _type.hash ^ _domain.hash;
96 LogTo(Bonjour,@"Added %@",self);
100 LogTo(Bonjour,@"Removed %@",self);
107 [_addressLookup stop];
111 - (NSString*) hostname {
112 if (!_startedResolve )
118 if (!_startedResolve )
125 #pragma mark TXT RECORD:
128 - (NSDictionary*) txtRecord {
130 _txtQuery = [[MYBonjourQuery alloc] initWithBonjourService: self
131 recordType: kDNSServiceType_TXT];
132 _txtQuery.continuous = YES;
138 - (void) txtRecordChanged {
139 // no-op (this is here for subclassers to override)
142 - (NSString*) txtStringForKey: (NSString*)key {
143 NSData *value = [self.txtRecord objectForKey: key];
146 if( ! [value isKindOfClass: [NSData class]] ) {
147 Warn(@"TXT dictionary has unexpected value type: %@",value.class);
150 NSString *str = [[NSString alloc] initWithData: value encoding: NSUTF8StringEncoding];
152 str = [[NSString alloc] initWithData: value encoding: NSWindowsCP1252StringEncoding];
153 return [str autorelease];
156 - (void) setTxtData: (NSData*)txtData {
157 NSDictionary *txtRecord = txtData ?[NSNetService dictionaryFromTXTRecordData: txtData] :nil;
158 if (!$equal(txtRecord,_txtRecord)) {
159 LogTo(Bonjour,@"%@ TXT = %@", self,txtRecord);
160 [self willChangeValueForKey: @"txtRecord"];
161 setObj(&_txtRecord, txtRecord);
162 [self didChangeValueForKey: @"txtRecord"];
163 [self txtRecordChanged];
168 - (void) queryDidUpdate: (MYBonjourQuery*)query {
169 if (query==_txtQuery)
170 [self setTxtData: query.recordData];
175 #pragma mark HOSTNAME/PORT RESOLUTION:
178 - (void) priv_resolvedHostname: (NSString*)hostname
180 txtRecord: (NSData*)txtData
182 LogTo(Bonjour, @"%@: hostname=%@, port=%u, txt=%u bytes",
183 self, hostname, port, txtData.length);
185 if (port!=_port || !$equal(hostname,_hostname)) {
186 self.hostname = hostname;
190 [self setTxtData: txtData];
193 - (void) gotResponse: (DNSServiceErrorType)errorCode {
194 [super gotResponse: errorCode];
195 [_addressLookup _serviceGotResponse];
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] _initWithBonjourService: self];
240 _addressLookup.interfaceIndex = _interfaceIndex;
242 // (Re)start the lookup if it's expired:
243 if (_addressLookup && _addressLookup.timeToLive <= 0.0)
244 [_addressLookup start];
245 return _addressLookup;
249 - (MYBonjourQuery*) queryForRecord: (UInt16)recordType {
250 MYBonjourQuery *query = [[[MYBonjourQuery alloc] initWithBonjourService: self recordType: recordType]
252 return [query start] ?query :nil;
261 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
263 Redistribution and use in source and binary forms, with or without modification, are permitted
264 provided that the following conditions are met:
266 * Redistributions of source code must retain the above copyright notice, this list of conditions
267 and the following disclaimer.
268 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
269 and the following disclaimer in the documentation and/or other materials provided with the
272 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
273 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
274 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
275 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
276 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
277 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
278 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
279 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.