Bonjour/MYBonjourService.m
author Jens Alfke <jens@mooseyard.com>
Fri Apr 24 10:10:32 2009 -0700 (2009-04-24)
changeset 27 92581f26073e
child 28 732576fa8a0d
permissions -rw-r--r--
* Refactored MYPortMapper to use a new abstract base class MYDNSService; that way I can re-use it later for implementing Bonjour.
* Fixed issue #1: a memory leak in BLIPProperties, reported by codechemist.
     1 //
     2 //  MYBonjourService.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 1/22/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYBonjourService.h"
    10 #import "IPAddress.h"
    11 #import "ConcurrentOperation.h"
    12 #import "Test.h"
    13 #import "Logging.h"
    14 
    15 
    16 NSString* const kBonjourServiceResolvedAddressesNotification = @"BonjourServiceResolvedAddresses";
    17 
    18 
    19 @interface MYBonjourService ()
    20 @property (copy) NSSet* addresses;
    21 @end
    22 
    23 @interface MYBonjourResolveOperation ()
    24 @property (assign) MYBonjourService *service;
    25 @property (retain) NSSet *addresses;
    26 @end
    27 
    28 
    29 
    30 @implementation MYBonjourService
    31 
    32 
    33 - (id) initWithNetService: (NSNetService*)netService
    34 {
    35     self = [super init];
    36     if (self != nil) {
    37         _netService = [netService retain];
    38         _netService.delegate = self;
    39     }
    40     return self;
    41 }
    42 
    43 - (void) dealloc
    44 {
    45     Log(@"DEALLOC %@",self);
    46     _netService.delegate = nil;
    47     [_netService release];
    48     [_txtRecord release];
    49     [_addresses release];
    50     [super dealloc];
    51 }
    52 
    53 
    54 - (NSString*) description
    55 {
    56     return $sprintf(@"%@['%@'.%@%@]", self.class,self.name,_netService.type,_netService.domain);
    57 }
    58 
    59 
    60 - (NSComparisonResult) compare: (id)obj
    61 {
    62     return [self.name caseInsensitiveCompare: [obj name]];
    63 }
    64 
    65 
    66 - (NSNetService*) netService        {return _netService;}
    67 - (BOOL) isEqual: (id)obj           {return [obj isKindOfClass: [MYBonjourService class]] && [_netService isEqual: [obj netService]];}
    68 - (NSUInteger) hash                 {return _netService.hash;}
    69 - (NSString*) name                  {return _netService.name;}
    70 
    71 
    72 - (void) added
    73 {
    74     LogTo(Bonjour,@"Added %@",_netService);
    75 }
    76 
    77 - (void) removed
    78 {
    79     LogTo(Bonjour,@"Removed %@",_netService);
    80     [_netService stopMonitoring];
    81     _netService.delegate = nil;
    82     
    83     if( _resolveOp ) {
    84         [_resolveOp cancel];
    85         [_resolveOp release];
    86         _resolveOp = nil;
    87     }
    88 }
    89 
    90 
    91 #pragma mark -
    92 #pragma mark TXT RECORD:
    93 
    94 
    95 - (NSDictionary*) txtRecord
    96 {
    97     [_netService startMonitoring];
    98     return _txtRecord;
    99 }
   100 
   101 - (void) txtRecordChanged
   102 {
   103     // no-op (this is here for subclassers to override)
   104 }
   105 
   106 - (NSString*) txtStringForKey: (NSString*)key
   107 {
   108     NSData *value = [self.txtRecord objectForKey: key];
   109     if( ! value )
   110         return nil;
   111     if( ! [value isKindOfClass: [NSData class]] ) {
   112         Warn(@"TXT dictionary has unexpected value type: %@",value.class);
   113         return nil;
   114     }
   115     NSString *str = [[NSString alloc] initWithData: value encoding: NSUTF8StringEncoding];
   116     if( ! str )
   117         str = [[NSString alloc] initWithData: value encoding: NSWindowsCP1252StringEncoding];
   118     return [str autorelease];
   119 }
   120 
   121 
   122 - (void)netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data
   123 {
   124     NSDictionary *txtDict = [NSNetService dictionaryFromTXTRecordData: data];
   125     if( ! $equal(txtDict,_txtRecord) ) {
   126         LogTo(Bonjour,@"%@ got TXT record (%u bytes)",self,data.length);
   127         [self willChangeValueForKey: @"txtRecord"];
   128         setObj(&_txtRecord,txtDict);
   129         [self didChangeValueForKey: @"txtRecord"];
   130         [self txtRecordChanged];
   131     }
   132 }
   133 
   134 
   135 #pragma mark -
   136 #pragma mark ADDRESS RESOLUTION:
   137 
   138 
   139 #define kAddressResolveTimeout      10.0
   140 #define kAddressExpirationInterval  60.0
   141 #define kAddressErrorRetryInterval   5.0
   142 
   143 
   144 - (NSSet*) addresses
   145 {
   146     if( _addresses && CFAbsoluteTimeGetCurrent() >= _addressesExpireAt ) {
   147         setObj(&_addresses,nil);            // eww, toss 'em and get new ones
   148         [self resolve];
   149     }
   150     return _addresses;
   151 }
   152 
   153 
   154 - (MYBonjourResolveOperation*) resolve
   155 {
   156     if( ! _resolveOp ) {
   157         LogTo(Bonjour,@"Resolving %@",self);
   158         _resolveOp = [[MYBonjourResolveOperation alloc] init];
   159         _resolveOp.service = self;
   160         [_resolveOp start];
   161         Assert(_netService);
   162         Assert(_netService.delegate=self);
   163         [_netService resolveWithTimeout: kAddressResolveTimeout];
   164     }
   165     return _resolveOp;
   166 }
   167 
   168 - (void) setAddresses: (NSSet*)addresses
   169 {
   170     setObj(&_addresses,addresses);
   171 }
   172 
   173 
   174 - (void) _finishedResolving: (NSSet*)addresses expireIn: (NSTimeInterval)expirationInterval
   175 {
   176     _addressesExpireAt = CFAbsoluteTimeGetCurrent() + expirationInterval;
   177     self.addresses = addresses;
   178     _resolveOp.addresses = addresses;
   179     [_resolveOp finish];
   180     [_resolveOp release];
   181     _resolveOp = nil;
   182 }
   183 
   184 
   185 - (void)netServiceDidResolveAddress:(NSNetService *)sender
   186 {
   187     // Convert the raw sockaddrs into IPAddress objects:
   188     NSMutableSet *addresses = [NSMutableSet setWithCapacity: 2];
   189     for( NSData *rawAddr in _netService.addresses ) {
   190         IPAddress *addr = [[IPAddress alloc] initWithSockAddr: rawAddr.bytes];
   191         if( addr ) {
   192             [addresses addObject: addr];
   193             [addr release];
   194         }
   195     }
   196     LogTo(Bonjour,@"Resolved %@: %@",self,addresses);
   197     [self _finishedResolving: addresses expireIn: kAddressExpirationInterval];
   198 }
   199 
   200 - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
   201 {
   202     LogTo(Bonjour,@"Error resolving %@ -- %@",self,errorDict);
   203     [self _finishedResolving: [NSArray array] expireIn: kAddressErrorRetryInterval];
   204 }
   205 
   206 - (void)netServiceDidStop:(NSNetService *)sender
   207 {
   208     LogTo(Bonjour,@"Resolve stopped for %@",self);
   209     [self _finishedResolving: [NSArray array] expireIn: kAddressErrorRetryInterval];
   210 }
   211 
   212 
   213 @end
   214 
   215 
   216 
   217 
   218 @implementation MYBonjourResolveOperation
   219 
   220 @synthesize service=_service, addresses=_addresses;
   221 
   222 - (void) dealloc
   223 {
   224     [_addresses release];
   225     [super dealloc];
   226 }
   227 
   228 @end
   229 
   230 
   231 /*
   232  Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   233  
   234  Redistribution and use in source and binary forms, with or without modification, are permitted
   235  provided that the following conditions are met:
   236  
   237  * Redistributions of source code must retain the above copyright notice, this list of conditions
   238  and the following disclaimer.
   239  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   240  and the following disclaimer in the documentation and/or other materials provided with the
   241  distribution.
   242  
   243  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   244  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   245  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   246  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   247  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   248   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   249  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   250  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   251  */