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