Fixed bug which caused PyBLIP to stop sending responses while the connection was closing.
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);
147 return _serviceRef != NULL;
151 + (NSString*) fullNameOfService: (NSString*)serviceName
152 ofType: (NSString*)type
153 inDomain: (NSString*)domain
155 //FIX: Do I need to un-escape the serviceName?
158 char fullName[kDNSServiceMaxDomainName];
159 if (DNSServiceConstructFullName(fullName, serviceName.UTF8String, type.UTF8String, domain.UTF8String) == 0)
160 return [NSString stringWithUTF8String: fullName];
170 #pragma mark SHARED CONNECTION:
173 @interface MYDNSConnection ()
178 @implementation MYDNSConnection
181 MYDNSConnection *sSharedConnection;
186 DNSServiceRef connectionRef = NULL;
187 DNSServiceErrorType err = DNSServiceCreateConnection(&connectionRef);
188 if (err || !connectionRef) {
189 Warn(@"MYDNSConnection: DNSServiceCreateConnection failed, err=%i", err);
193 return [self initWithServiceRef: connectionRef];
197 - (id) initWithServiceRef: (DNSServiceRef)serviceRef
202 _connectionRef = serviceRef;
203 LogTo(DNS,@"INIT %@", self);
213 + (MYDNSConnection*) sharedConnection {
214 @synchronized(self) {
215 if (!sSharedConnection)
216 sSharedConnection = [[[self alloc] init] autorelease];
218 return sSharedConnection;
224 LogTo(DNS,@"DEALLOC %@", self);
235 @synthesize connectionRef=_connectionRef;
237 - (NSString*) description {
238 return $sprintf(@"%@[conn=%p]", self.class,_connectionRef);
243 return YES; // Already opened
245 // Wrap a CFSocket around the service's socket:
246 CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
247 _socket = CFSocketCreateWithNative(NULL,
248 DNSServiceRefSockFD(_connectionRef),
249 kCFSocketReadCallBack,
250 &serviceCallback, &ctxt);
252 CFSocketSetSocketFlags(_socket,
253 CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
254 // Attach the socket to the runloop so the serviceCallback will be invoked:
255 _runLoopSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
256 if( _runLoopSource ) {
257 CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
259 LogTo(DNS,@"Successfully opened %@", self);
265 Warn(@"Failed to connect %@ to runloop", self);
272 @synchronized(self) {
273 if( _runLoopSource ) {
274 CFRunLoopSourceInvalidate(_runLoopSource);
275 CFRelease(_runLoopSource);
276 _runLoopSource = NULL;
279 CFSocketInvalidate(_socket);
283 if( _connectionRef ) {
284 LogTo(DNS,@"Closed %@",self);
285 DNSServiceRefDeallocate(_connectionRef);
286 _connectionRef = NULL;
289 if (self==sSharedConnection)
290 sSharedConnection = nil;
295 - (BOOL) processResult {
296 NSAutoreleasePool *pool = [NSAutoreleasePool new];
297 LogTo(DNS,@"---serviceCallback----");
298 DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef);
300 Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err);
301 //FIX: Are errors here fatal, meaning I should close the connection?
302 // I've run into infinite loops constantly getting kDNSServiceErr_ServiceNotRunning
303 // or kDNSServiceErr_BadReference ...
310 /** CFSocket callback, informing us that _socket has data available, which means
311 that the DNS service has an incoming result to be processed. This will end up invoking
312 the service's specific callback. */
313 static void serviceCallback(CFSocketRef s,
314 CFSocketCallBackType type,
315 CFDataRef address, const void *data, void *clientCallBackInfo)
317 MYDNSConnection *connection = clientCallBackInfo;
318 [connection processResult];
326 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
328 Redistribution and use in source and binary forms, with or without modification, are permitted
329 provided that the following conditions are met:
331 * Redistributions of source code must retain the above copyright notice, this list of conditions
332 and the following disclaimer.
333 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
334 and the following disclaimer in the documentation and/or other materials provided with the
337 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
338 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
339 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
340 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
341 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
342 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
343 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
344 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.