PortMapper/MYDNSService.m
author Jens Alfke <jens@mooseyard.com>
Mon Apr 27 09:03:56 2009 -0700 (2009-04-27)
changeset 28 732576fa8a0d
parent 27 92581f26073e
child 31 1d6924779df7
permissions -rw-r--r--
Rewrote the Bonjour classes, using the low-level <dns_sd.h> API. They're now subclasses of MYDNSService.
     1 //
     2 //  MYDNSService.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 4/23/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYDNSService.h"
    10 #import "CollectionUtils.h"
    11 #import "Logging.h"
    12 #import "Test.h"
    13 #import "ExceptionUtils.h"
    14 
    15 #import <dns_sd.h>
    16 
    17 
    18 static void serviceCallback(CFSocketRef s, 
    19                             CFSocketCallBackType type,
    20                             CFDataRef address,
    21                             const void *data,
    22                             void *clientCallBackInfo);
    23 
    24 
    25 @implementation MYDNSService
    26 
    27 
    28 - (void) dealloc
    29 {
    30     Log(@"DEALLOC %@ %p", self.class,self);
    31     if( _serviceRef )
    32         [self cancel];
    33     [super dealloc];
    34 }
    35 
    36 - (void) finalize
    37 {
    38     if( _serviceRef )
    39         [self cancel];
    40     [super finalize];
    41 }
    42 
    43 
    44 - (DNSServiceErrorType) error {
    45     return _error;
    46 }
    47 
    48 - (void) setError: (DNSServiceErrorType)error {
    49     if (error)
    50         Warn(@"%@ error := %i", self,error);
    51     _error = error;
    52 }
    53 
    54 
    55 @synthesize continuous=_continuous, serviceRef=_serviceRef;
    56 
    57 
    58 - (DNSServiceRef) createServiceRef {
    59     AssertAbstractMethod();
    60 }
    61 
    62 
    63 - (BOOL) start
    64 {
    65     if (_serviceRef)
    66         return YES;     // already started
    67 
    68     if (_error)
    69         self.error = 0;
    70 
    71     // Ask the subclass to create a DNSServiceRef:
    72     _serviceRef = [self createServiceRef];
    73     
    74     if (_serviceRef) {
    75         // Wrap a CFSocket around the service's socket:
    76         CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
    77         _socket = CFSocketCreateWithNative(NULL, 
    78                                            DNSServiceRefSockFD(_serviceRef), 
    79                                            kCFSocketReadCallBack, 
    80                                            &serviceCallback, &ctxt);
    81         if( _socket ) {
    82             CFSocketSetSocketFlags(_socket, CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
    83             // Attach the socket to the runloop so the serviceCallback will be invoked:
    84             _socketSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
    85             if( _socketSource ) {
    86                 CFRunLoopAddSource(CFRunLoopGetCurrent(), _socketSource, kCFRunLoopCommonModes);
    87                 LogTo(DNS,@"Opening %@ -- service=%p",self,_serviceRef);
    88                 return YES; // success
    89             }
    90         }
    91     }
    92     if (!_error)
    93         self.error = kDNSServiceErr_Unknown;
    94     LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
    95     [self cancel];
    96     return NO;
    97 }
    98 
    99 
   100 - (void) cancel
   101 {
   102     [self retain];            // Prevents _socket's dealloc from releasing & deallocing me!
   103     if( _socketSource ) {
   104         CFRunLoopSourceInvalidate(_socketSource);
   105         CFRelease(_socketSource);
   106         _socketSource = NULL;
   107     }
   108     if( _socket ) {
   109         CFSocketInvalidate(_socket);
   110         CFRelease(_socket);
   111         _socket = NULL;
   112     }
   113     if( _serviceRef ) {
   114         LogTo(DNS,@"Stopped %@",self);
   115         DNSServiceRefDeallocate(_serviceRef);
   116         _serviceRef = NULL;
   117     }
   118     [self release];
   119 }
   120 
   121 
   122 - (void) stop
   123 {
   124     [self cancel];
   125     if (_error)
   126         self.error = 0;
   127 }
   128 
   129 
   130 - (BOOL) priv_processResult
   131 {
   132     Assert(_serviceRef);
   133     DNSServiceErrorType err = DNSServiceProcessResult(_serviceRef);
   134     if (err) {
   135         // An error here means the socket has failed and should be closed.
   136         self.error = err;
   137         [self cancel];
   138         return NO;
   139     } else {
   140         if (!_continuous)
   141             [self cancel];
   142         return YES;
   143     }
   144 }
   145 
   146 - (BOOL) waitForReply
   147 {
   148     if (!_serviceRef)
   149         return NO;
   150     LogTo(DNS,@"Waiting for %@ ...", self);
   151     BOOL ok = [self priv_processResult];
   152     LogTo(DNS,@"    ...done waiting");
   153     return ok;
   154 }    
   155 
   156 
   157 /** CFSocket callback, informing us that _socket has data available, which means
   158     that the DNS service has an incoming result to be processed. This will end up invoking
   159     the service's specific callback. */
   160 static void serviceCallback(CFSocketRef s, 
   161                             CFSocketCallBackType type,
   162                             CFDataRef address, const void *data, void *clientCallBackInfo)
   163 {
   164     NSAutoreleasePool *pool = [NSAutoreleasePool new];
   165     @try{
   166         [(MYDNSService*)clientCallBackInfo priv_processResult];
   167     }catchAndReport(@"PortMapper serviceCallback");
   168     [pool drain];
   169 }
   170 
   171 
   172 @end
   173 
   174 
   175 /*
   176  Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   177  
   178  Redistribution and use in source and binary forms, with or without modification, are permitted
   179  provided that the following conditions are met:
   180  
   181  * Redistributions of source code must retain the above copyright notice, this list of conditions
   182  and the following disclaimer.
   183  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   184  and the following disclaimer in the documentation and/or other materials provided with the
   185  distribution.
   186  
   187  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   188  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   189  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   190  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   191  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   192   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   193  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   194  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   195  */