* Fixed #9: compilation error with iPhone 3.0 SDK.
* MYBonjourRegistration now allows you to set a TXT dictionary with non-NSData key values; they'll be translated to UTF-8 object descriptions. Useful for NSStrings and NSNumbers.
5 // Created by Jens Alfke on 4/23/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYDNSService.h"
10 #import "CollectionUtils.h"
13 #import "ExceptionUtils.h"
18 static void serviceCallback(CFSocketRef s,
19 CFSocketCallBackType type,
22 void *clientCallBackInfo);
25 @implementation MYDNSService
30 Log(@"DEALLOC %@ %p", self.class,self);
44 - (DNSServiceErrorType) error {
48 - (void) setError: (DNSServiceErrorType)error {
50 Warn(@"%@ error := %i", self,error);
55 @synthesize continuous=_continuous, serviceRef=_serviceRef, usePrivateConnection=_usePrivateConnection;
58 - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
59 AssertAbstractMethod();
63 - (void) gotResponse: (DNSServiceErrorType)errorCode {
67 if (errorCode && errorCode != _error) {
68 Log(@"%@ got error %i", self,errorCode);
69 self.error = errorCode;
77 return YES; // already started
83 if (!_usePrivateConnection) {
84 _connection = [[MYDNSConnection sharedConnection] retain];
86 self.error = kDNSServiceErr_Unknown;
89 _serviceRef = _connection.connectionRef;
92 // Ask the subclass to create a DNSServiceRef:
93 _error = [self createServiceRef: &_serviceRef];
96 setObj(&_connection,nil);
98 self.error = kDNSServiceErr_Unknown;
99 LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
104 _connection = [[MYDNSConnection alloc] initWithServiceRef: _serviceRef];
106 LogTo(DNS,@"Started %@",self);
107 return YES; // Succeeded
111 - (BOOL) waitForReply {
115 // Run the runloop until there's either an error or a result:
117 LogTo(DNS,@"Waiting for reply to %@...", self);
118 while( !_gotResponse )
119 if( ! [_connection processResult] )
121 LogTo(DNS,@" ...got reply");
122 return (self.error==0);
129 LogTo(DNS,@"Stopped %@",self);
130 DNSServiceRefDeallocate(_serviceRef);
133 setObj(&_connection,nil);
146 + (NSString*) fullNameOfService: (NSString*)serviceName
147 ofType: (NSString*)type
148 inDomain: (NSString*)domain
150 //FIX: Do I need to un-escape the serviceName?
153 char fullName[kDNSServiceMaxDomainName];
154 if (DNSServiceConstructFullName(fullName, serviceName.UTF8String, type.UTF8String, domain.UTF8String) == 0)
155 return [NSString stringWithUTF8String: fullName];
165 #pragma mark SHARED CONNECTION:
168 @interface MYDNSConnection ()
173 @implementation MYDNSConnection
176 MYDNSConnection *sSharedConnection;
181 DNSServiceRef connectionRef = NULL;
182 DNSServiceErrorType err = DNSServiceCreateConnection(&connectionRef);
183 if (err || !connectionRef) {
184 Warn(@"MYDNSConnection: DNSServiceCreateConnection failed, err=%i", err);
188 return [self initWithServiceRef: connectionRef];
192 - (id) initWithServiceRef: (DNSServiceRef)serviceRef
197 _connectionRef = serviceRef;
198 LogTo(DNS,@"INIT %@", self);
208 + (MYDNSConnection*) sharedConnection {
209 @synchronized(self) {
210 if (!sSharedConnection)
211 sSharedConnection = [[[self alloc] init] autorelease];
213 return sSharedConnection;
219 LogTo(DNS,@"DEALLOC %@", self);
230 @synthesize connectionRef=_connectionRef;
232 - (NSString*) description {
233 return $sprintf(@"%@[conn=%p]", self.class,_connectionRef);
238 return YES; // Already opened
240 // Wrap a CFSocket around the service's socket:
241 CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
242 _socket = CFSocketCreateWithNative(NULL,
243 DNSServiceRefSockFD(_connectionRef),
244 kCFSocketReadCallBack,
245 &serviceCallback, &ctxt);
247 CFSocketSetSocketFlags(_socket,
248 CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
249 // Attach the socket to the runloop so the serviceCallback will be invoked:
250 _runLoopSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
251 if( _runLoopSource ) {
252 CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
254 LogTo(DNS,@"Successfully opened %@", self);
260 Warn(@"Failed to connect %@ to runloop", self);
267 @synchronized(self) {
268 if( _runLoopSource ) {
269 CFRunLoopSourceInvalidate(_runLoopSource);
270 CFRelease(_runLoopSource);
271 _runLoopSource = NULL;
274 CFSocketInvalidate(_socket);
278 if( _connectionRef ) {
279 LogTo(DNS,@"Closed %@",self);
280 DNSServiceRefDeallocate(_connectionRef);
281 _connectionRef = NULL;
284 if (self==sSharedConnection)
285 sSharedConnection = nil;
290 - (BOOL) processResult {
291 NSAutoreleasePool *pool = [NSAutoreleasePool new];
292 LogTo(DNS,@"---serviceCallback----");
293 DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef);
295 Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err);
301 /** CFSocket callback, informing us that _socket has data available, which means
302 that the DNS service has an incoming result to be processed. This will end up invoking
303 the service's specific callback. */
304 static void serviceCallback(CFSocketRef s,
305 CFSocketCallBackType type,
306 CFDataRef address, const void *data, void *clientCallBackInfo)
308 MYDNSConnection *connection = clientCallBackInfo;
309 [connection processResult];
317 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
319 Redistribution and use in source and binary forms, with or without modification, are permitted
320 provided that the following conditions are met:
322 * Redistributions of source code must retain the above copyright notice, this list of conditions
323 and the following disclaimer.
324 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
325 and the following disclaimer in the documentation and/or other materials provided with the
328 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
329 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
330 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
331 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
332 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
333 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
334 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
335 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.