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