jens@27: // jens@27: // MYDNSService.m jens@27: // MYNetwork jens@27: // jens@27: // Created by Jens Alfke on 4/23/09. jens@27: // Copyright 2009 Jens Alfke. All rights reserved. jens@27: // jens@27: jens@27: #import "MYDNSService.h" jens@27: #import "CollectionUtils.h" jens@27: #import "Logging.h" jens@27: #import "Test.h" jens@27: #import "ExceptionUtils.h" jens@27: jens@27: #import jens@27: jens@27: jens@27: static void serviceCallback(CFSocketRef s, jens@27: CFSocketCallBackType type, jens@27: CFDataRef address, jens@27: const void *data, jens@27: void *clientCallBackInfo); jens@27: jens@27: jens@27: @implementation MYDNSService jens@27: jens@27: jens@27: - (void) dealloc jens@27: { jens@28: Log(@"DEALLOC %@ %p", self.class,self); jens@27: if( _serviceRef ) jens@28: [self cancel]; jens@27: [super dealloc]; jens@27: } jens@27: jens@27: - (void) finalize jens@27: { jens@27: if( _serviceRef ) jens@28: [self cancel]; jens@27: [super finalize]; jens@27: } jens@27: jens@27: jens@28: - (DNSServiceErrorType) error { jens@28: return _error; jens@28: } jens@28: jens@28: - (void) setError: (DNSServiceErrorType)error { jens@28: if (error) jens@28: Warn(@"%@ error := %i", self,error); jens@28: _error = error; jens@28: } jens@28: jens@28: jens@28: @synthesize continuous=_continuous, serviceRef=_serviceRef; jens@27: jens@27: jens@27: - (DNSServiceRef) createServiceRef { jens@27: AssertAbstractMethod(); jens@27: } jens@27: jens@27: jens@28: - (BOOL) start jens@27: { jens@27: if (_serviceRef) jens@28: return YES; // already started jens@28: jens@28: if (_error) jens@28: self.error = 0; jens@28: jens@28: // Ask the subclass to create a DNSServiceRef: jens@27: _serviceRef = [self createServiceRef]; jens@28: jens@27: if (_serviceRef) { jens@27: // Wrap a CFSocket around the service's socket: jens@27: CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL }; jens@27: _socket = CFSocketCreateWithNative(NULL, jens@27: DNSServiceRefSockFD(_serviceRef), jens@27: kCFSocketReadCallBack, jens@27: &serviceCallback, &ctxt); jens@27: if( _socket ) { jens@27: CFSocketSetSocketFlags(_socket, CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate); jens@27: // Attach the socket to the runloop so the serviceCallback will be invoked: jens@27: _socketSource = CFSocketCreateRunLoopSource(NULL, _socket, 0); jens@27: if( _socketSource ) { jens@27: CFRunLoopAddSource(CFRunLoopGetCurrent(), _socketSource, kCFRunLoopCommonModes); jens@28: LogTo(DNS,@"Opening %@ -- service=%p",self,_serviceRef); jens@27: return YES; // success jens@27: } jens@27: } jens@27: } jens@27: if (!_error) jens@27: self.error = kDNSServiceErr_Unknown; jens@27: LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error); jens@28: [self cancel]; jens@27: return NO; jens@27: } jens@27: jens@27: jens@28: - (void) cancel jens@27: { jens@28: [self retain]; // Prevents _socket's dealloc from releasing & deallocing me! jens@27: if( _socketSource ) { jens@27: CFRunLoopSourceInvalidate(_socketSource); jens@27: CFRelease(_socketSource); jens@27: _socketSource = NULL; jens@27: } jens@27: if( _socket ) { jens@27: CFSocketInvalidate(_socket); jens@27: CFRelease(_socket); jens@27: _socket = NULL; jens@27: } jens@27: if( _serviceRef ) { jens@27: LogTo(DNS,@"Stopped %@",self); jens@27: DNSServiceRefDeallocate(_serviceRef); jens@27: _serviceRef = NULL; jens@27: } jens@28: [self release]; jens@27: } jens@27: jens@27: jens@28: - (void) stop jens@27: { jens@28: [self cancel]; jens@27: if (_error) jens@27: self.error = 0; jens@27: } jens@27: jens@27: jens@28: - (BOOL) priv_processResult jens@28: { jens@28: Assert(_serviceRef); jens@28: DNSServiceErrorType err = DNSServiceProcessResult(_serviceRef); jens@28: if (err) { jens@28: // An error here means the socket has failed and should be closed. jens@28: self.error = err; jens@28: [self cancel]; jens@28: return NO; jens@28: } else { jens@28: if (!_continuous) jens@28: [self cancel]; jens@28: return YES; jens@28: } jens@28: } jens@28: jens@28: - (BOOL) waitForReply jens@28: { jens@28: if (!_serviceRef) jens@28: return NO; jens@28: LogTo(DNS,@"Waiting for %@ ...", self); jens@28: BOOL ok = [self priv_processResult]; jens@28: LogTo(DNS,@" ...done waiting"); jens@28: return ok; jens@28: } jens@28: jens@28: jens@27: /** CFSocket callback, informing us that _socket has data available, which means jens@27: that the DNS service has an incoming result to be processed. This will end up invoking jens@27: the service's specific callback. */ jens@27: static void serviceCallback(CFSocketRef s, jens@27: CFSocketCallBackType type, jens@27: CFDataRef address, const void *data, void *clientCallBackInfo) jens@27: { jens@28: NSAutoreleasePool *pool = [NSAutoreleasePool new]; jens@28: @try{ jens@28: [(MYDNSService*)clientCallBackInfo priv_processResult]; jens@28: }catchAndReport(@"PortMapper serviceCallback"); jens@28: [pool drain]; jens@27: } jens@27: jens@27: jens@27: @end jens@27: jens@27: jens@27: /* jens@27: Copyright (c) 2008-2009, Jens Alfke . All rights reserved. jens@27: jens@27: Redistribution and use in source and binary forms, with or without modification, are permitted jens@27: provided that the following conditions are met: jens@27: jens@27: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@27: and the following disclaimer. jens@27: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@27: and the following disclaimer in the documentation and/or other materials provided with the jens@27: distribution. jens@27: jens@27: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@27: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@27: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@27: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@27: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@27: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@27: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@27: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@27: */