1.1 --- a/PortMapper/MYDNSService.m Mon Apr 27 09:03:56 2009 -0700
1.2 +++ b/PortMapper/MYDNSService.m Sat May 16 14:10:15 2009 -0700
1.3 @@ -20,7 +20,7 @@
1.4 CFDataRef address,
1.5 const void *data,
1.6 void *clientCallBackInfo);
1.7 -
1.8 +
1.9
1.10 @implementation MYDNSService
1.11
1.12 @@ -52,14 +52,25 @@
1.13 }
1.14
1.15
1.16 -@synthesize continuous=_continuous, serviceRef=_serviceRef;
1.17 +@synthesize continuous=_continuous, serviceRef=_serviceRef, usePrivateConnection=_usePrivateConnection;
1.18
1.19
1.20 -- (DNSServiceRef) createServiceRef {
1.21 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
1.22 AssertAbstractMethod();
1.23 }
1.24
1.25
1.26 +- (void) gotResponse: (DNSServiceErrorType)errorCode {
1.27 + _gotResponse = YES;
1.28 + if (!_continuous)
1.29 + [self cancel];
1.30 + if (errorCode && errorCode != _error) {
1.31 + Log(@"%@ got error %i", self,errorCode);
1.32 + self.error = errorCode;
1.33 + }
1.34 +}
1.35 +
1.36 +
1.37 - (BOOL) start
1.38 {
1.39 if (_serviceRef)
1.40 @@ -67,55 +78,60 @@
1.41
1.42 if (_error)
1.43 self.error = 0;
1.44 + _gotResponse = NO;
1.45
1.46 + if (!_usePrivateConnection) {
1.47 + _connection = [[MYDNSConnection sharedConnection] retain];
1.48 + if (!_connection) {
1.49 + self.error = kDNSServiceErr_Unknown;
1.50 + return NO;
1.51 + }
1.52 + _serviceRef = _connection.connectionRef;
1.53 + }
1.54 +
1.55 // Ask the subclass to create a DNSServiceRef:
1.56 - _serviceRef = [self createServiceRef];
1.57 + _error = [self createServiceRef: &_serviceRef];
1.58 + if (_error) {
1.59 + _serviceRef = NULL;
1.60 + setObj(&_connection,nil);
1.61 + if (!_error)
1.62 + self.error = kDNSServiceErr_Unknown;
1.63 + LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
1.64 + return NO;
1.65 + }
1.66
1.67 - if (_serviceRef) {
1.68 - // Wrap a CFSocket around the service's socket:
1.69 - CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
1.70 - _socket = CFSocketCreateWithNative(NULL,
1.71 - DNSServiceRefSockFD(_serviceRef),
1.72 - kCFSocketReadCallBack,
1.73 - &serviceCallback, &ctxt);
1.74 - if( _socket ) {
1.75 - CFSocketSetSocketFlags(_socket, CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
1.76 - // Attach the socket to the runloop so the serviceCallback will be invoked:
1.77 - _socketSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
1.78 - if( _socketSource ) {
1.79 - CFRunLoopAddSource(CFRunLoopGetCurrent(), _socketSource, kCFRunLoopCommonModes);
1.80 - LogTo(DNS,@"Opening %@ -- service=%p",self,_serviceRef);
1.81 - return YES; // success
1.82 - }
1.83 - }
1.84 - }
1.85 - if (!_error)
1.86 - self.error = kDNSServiceErr_Unknown;
1.87 - LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
1.88 - [self cancel];
1.89 - return NO;
1.90 + if (!_connection)
1.91 + _connection = [[MYDNSConnection alloc] initWithServiceRef: _serviceRef];
1.92 +
1.93 + LogTo(DNS,@"Started %@",self);
1.94 + return YES; // Succeeded
1.95 +}
1.96 +
1.97 +
1.98 +- (BOOL) waitForReply {
1.99 + if( ! _serviceRef )
1.100 + if( ! [self start] )
1.101 + return NO;
1.102 + // Run the runloop until there's either an error or a result:
1.103 + _gotResponse = NO;
1.104 + LogTo(DNS,@"Waiting for reply to %@...", self);
1.105 + while( !_gotResponse )
1.106 + if( ! [_connection processResult] )
1.107 + break;
1.108 + LogTo(DNS,@" ...got reply");
1.109 + return (self.error==0);
1.110 }
1.111
1.112
1.113 - (void) cancel
1.114 {
1.115 - [self retain]; // Prevents _socket's dealloc from releasing & deallocing me!
1.116 - if( _socketSource ) {
1.117 - CFRunLoopSourceInvalidate(_socketSource);
1.118 - CFRelease(_socketSource);
1.119 - _socketSource = NULL;
1.120 - }
1.121 - if( _socket ) {
1.122 - CFSocketInvalidate(_socket);
1.123 - CFRelease(_socket);
1.124 - _socket = NULL;
1.125 - }
1.126 if( _serviceRef ) {
1.127 LogTo(DNS,@"Stopped %@",self);
1.128 DNSServiceRefDeallocate(_serviceRef);
1.129 _serviceRef = NULL;
1.130 +
1.131 + setObj(&_connection,nil);
1.132 }
1.133 - [self release];
1.134 }
1.135
1.136
1.137 @@ -127,31 +143,159 @@
1.138 }
1.139
1.140
1.141 -- (BOOL) priv_processResult
1.142 ++ (NSString*) fullNameOfService: (NSString*)serviceName
1.143 + ofType: (NSString*)type
1.144 + inDomain: (NSString*)domain
1.145 {
1.146 - Assert(_serviceRef);
1.147 - DNSServiceErrorType err = DNSServiceProcessResult(_serviceRef);
1.148 - if (err) {
1.149 - // An error here means the socket has failed and should be closed.
1.150 - self.error = err;
1.151 - [self cancel];
1.152 - return NO;
1.153 - } else {
1.154 - if (!_continuous)
1.155 - [self cancel];
1.156 - return YES;
1.157 + //FIX: Do I need to un-escape the serviceName?
1.158 + Assert(type);
1.159 + Assert(domain);
1.160 + char fullName[kDNSServiceMaxDomainName];
1.161 + if (DNSServiceConstructFullName(fullName, serviceName.UTF8String, type.UTF8String, domain.UTF8String) == 0)
1.162 + return [NSString stringWithUTF8String: fullName];
1.163 + else
1.164 + return nil;
1.165 +}
1.166 +
1.167 +
1.168 +@end
1.169 +
1.170 +
1.171 +#pragma mark -
1.172 +#pragma mark SHARED CONNECTION:
1.173 +
1.174 +
1.175 +@interface MYDNSConnection ()
1.176 +- (BOOL) open;
1.177 +@end
1.178 +
1.179 +
1.180 +@implementation MYDNSConnection
1.181 +
1.182 +
1.183 +MYDNSConnection *sSharedConnection;
1.184 +
1.185 +
1.186 +- (id) init
1.187 +{
1.188 + DNSServiceRef connectionRef = NULL;
1.189 + DNSServiceErrorType err = DNSServiceCreateConnection(&connectionRef);
1.190 + if (err || !connectionRef) {
1.191 + Warn(@"MYDNSConnection: DNSServiceCreateConnection failed, err=%i", err);
1.192 + [self release];
1.193 + return nil;
1.194 + }
1.195 + return [self initWithServiceRef: connectionRef];
1.196 +}
1.197 +
1.198 +
1.199 +- (id) initWithServiceRef: (DNSServiceRef)serviceRef
1.200 +{
1.201 + Assert(serviceRef);
1.202 + self = [super init];
1.203 + if (self != nil) {
1.204 + _connectionRef = serviceRef;
1.205 + LogTo(DNS,@"INIT %@", self);
1.206 + if (![self open]) {
1.207 + [self release];
1.208 + return nil;
1.209 + }
1.210 + }
1.211 + return self;
1.212 +}
1.213 +
1.214 +
1.215 ++ (MYDNSConnection*) sharedConnection {
1.216 + @synchronized(self) {
1.217 + if (!sSharedConnection)
1.218 + sSharedConnection = [[[self alloc] init] autorelease];
1.219 + }
1.220 + return sSharedConnection;
1.221 +}
1.222 +
1.223 +
1.224 +- (void) dealloc
1.225 +{
1.226 + LogTo(DNS,@"DEALLOC %@", self);
1.227 + [self close];
1.228 + [super dealloc];
1.229 +}
1.230 +
1.231 +- (void) finalize {
1.232 + [self close];
1.233 + [super finalize];
1.234 +}
1.235 +
1.236 +
1.237 +@synthesize connectionRef=_connectionRef;
1.238 +
1.239 +- (NSString*) description {
1.240 + return $sprintf(@"%@[conn=%p]", self.class,_connectionRef);
1.241 +}
1.242 +
1.243 +- (BOOL) open {
1.244 + if (_runLoopSource)
1.245 + return YES; // Already opened
1.246 +
1.247 + // Wrap a CFSocket around the service's socket:
1.248 + CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
1.249 + _socket = CFSocketCreateWithNative(NULL,
1.250 + DNSServiceRefSockFD(_connectionRef),
1.251 + kCFSocketReadCallBack,
1.252 + &serviceCallback, &ctxt);
1.253 + if( _socket ) {
1.254 + CFSocketSetSocketFlags(_socket,
1.255 + CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
1.256 + // Attach the socket to the runloop so the serviceCallback will be invoked:
1.257 + _runLoopSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
1.258 + if( _runLoopSource ) {
1.259 + CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
1.260 + // Success!
1.261 + LogTo(DNS,@"Successfully opened %@", self);
1.262 + return YES;
1.263 + }
1.264 + }
1.265 +
1.266 + // Failure:
1.267 + Warn(@"Failed to connect %@ to runloop", self);
1.268 + [self close];
1.269 + return NO;
1.270 +}
1.271 +
1.272 +
1.273 +- (void) close {
1.274 + @synchronized(self) {
1.275 + if( _runLoopSource ) {
1.276 + CFRunLoopSourceInvalidate(_runLoopSource);
1.277 + CFRelease(_runLoopSource);
1.278 + _runLoopSource = NULL;
1.279 + }
1.280 + if( _socket ) {
1.281 + CFSocketInvalidate(_socket);
1.282 + CFRelease(_socket);
1.283 + _socket = NULL;
1.284 + }
1.285 + if( _connectionRef ) {
1.286 + LogTo(DNS,@"Closed %@",self);
1.287 + DNSServiceRefDeallocate(_connectionRef);
1.288 + _connectionRef = NULL;
1.289 + }
1.290 +
1.291 + if (self==sSharedConnection)
1.292 + sSharedConnection = nil;
1.293 }
1.294 }
1.295
1.296 -- (BOOL) waitForReply
1.297 -{
1.298 - if (!_serviceRef)
1.299 - return NO;
1.300 - LogTo(DNS,@"Waiting for %@ ...", self);
1.301 - BOOL ok = [self priv_processResult];
1.302 - LogTo(DNS,@" ...done waiting");
1.303 - return ok;
1.304 -}
1.305 +
1.306 +- (BOOL) processResult {
1.307 + NSAutoreleasePool *pool = [NSAutoreleasePool new];
1.308 + LogTo(DNS,@"---serviceCallback----");
1.309 + DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef);
1.310 + if (err)
1.311 + Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err);
1.312 + [pool drain];
1.313 + return !err;
1.314 +}
1.315
1.316
1.317 /** CFSocket callback, informing us that _socket has data available, which means
1.318 @@ -161,11 +305,8 @@
1.319 CFSocketCallBackType type,
1.320 CFDataRef address, const void *data, void *clientCallBackInfo)
1.321 {
1.322 - NSAutoreleasePool *pool = [NSAutoreleasePool new];
1.323 - @try{
1.324 - [(MYDNSService*)clientCallBackInfo priv_processResult];
1.325 - }catchAndReport(@"PortMapper serviceCallback");
1.326 - [pool drain];
1.327 + MYDNSConnection *connection = clientCallBackInfo;
1.328 + [connection processResult];
1.329 }
1.330
1.331