1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Bonjour/MYBonjourService.m Fri Apr 24 10:10:32 2009 -0700
1.3 @@ -0,0 +1,251 @@
1.4 +//
1.5 +// MYBonjourService.m
1.6 +// MYNetwork
1.7 +//
1.8 +// Created by Jens Alfke on 1/22/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "MYBonjourService.h"
1.13 +#import "IPAddress.h"
1.14 +#import "ConcurrentOperation.h"
1.15 +#import "Test.h"
1.16 +#import "Logging.h"
1.17 +
1.18 +
1.19 +NSString* const kBonjourServiceResolvedAddressesNotification = @"BonjourServiceResolvedAddresses";
1.20 +
1.21 +
1.22 +@interface MYBonjourService ()
1.23 +@property (copy) NSSet* addresses;
1.24 +@end
1.25 +
1.26 +@interface MYBonjourResolveOperation ()
1.27 +@property (assign) MYBonjourService *service;
1.28 +@property (retain) NSSet *addresses;
1.29 +@end
1.30 +
1.31 +
1.32 +
1.33 +@implementation MYBonjourService
1.34 +
1.35 +
1.36 +- (id) initWithNetService: (NSNetService*)netService
1.37 +{
1.38 + self = [super init];
1.39 + if (self != nil) {
1.40 + _netService = [netService retain];
1.41 + _netService.delegate = self;
1.42 + }
1.43 + return self;
1.44 +}
1.45 +
1.46 +- (void) dealloc
1.47 +{
1.48 + Log(@"DEALLOC %@",self);
1.49 + _netService.delegate = nil;
1.50 + [_netService release];
1.51 + [_txtRecord release];
1.52 + [_addresses release];
1.53 + [super dealloc];
1.54 +}
1.55 +
1.56 +
1.57 +- (NSString*) description
1.58 +{
1.59 + return $sprintf(@"%@['%@'.%@%@]", self.class,self.name,_netService.type,_netService.domain);
1.60 +}
1.61 +
1.62 +
1.63 +- (NSComparisonResult) compare: (id)obj
1.64 +{
1.65 + return [self.name caseInsensitiveCompare: [obj name]];
1.66 +}
1.67 +
1.68 +
1.69 +- (NSNetService*) netService {return _netService;}
1.70 +- (BOOL) isEqual: (id)obj {return [obj isKindOfClass: [MYBonjourService class]] && [_netService isEqual: [obj netService]];}
1.71 +- (NSUInteger) hash {return _netService.hash;}
1.72 +- (NSString*) name {return _netService.name;}
1.73 +
1.74 +
1.75 +- (void) added
1.76 +{
1.77 + LogTo(Bonjour,@"Added %@",_netService);
1.78 +}
1.79 +
1.80 +- (void) removed
1.81 +{
1.82 + LogTo(Bonjour,@"Removed %@",_netService);
1.83 + [_netService stopMonitoring];
1.84 + _netService.delegate = nil;
1.85 +
1.86 + if( _resolveOp ) {
1.87 + [_resolveOp cancel];
1.88 + [_resolveOp release];
1.89 + _resolveOp = nil;
1.90 + }
1.91 +}
1.92 +
1.93 +
1.94 +#pragma mark -
1.95 +#pragma mark TXT RECORD:
1.96 +
1.97 +
1.98 +- (NSDictionary*) txtRecord
1.99 +{
1.100 + [_netService startMonitoring];
1.101 + return _txtRecord;
1.102 +}
1.103 +
1.104 +- (void) txtRecordChanged
1.105 +{
1.106 + // no-op (this is here for subclassers to override)
1.107 +}
1.108 +
1.109 +- (NSString*) txtStringForKey: (NSString*)key
1.110 +{
1.111 + NSData *value = [self.txtRecord objectForKey: key];
1.112 + if( ! value )
1.113 + return nil;
1.114 + if( ! [value isKindOfClass: [NSData class]] ) {
1.115 + Warn(@"TXT dictionary has unexpected value type: %@",value.class);
1.116 + return nil;
1.117 + }
1.118 + NSString *str = [[NSString alloc] initWithData: value encoding: NSUTF8StringEncoding];
1.119 + if( ! str )
1.120 + str = [[NSString alloc] initWithData: value encoding: NSWindowsCP1252StringEncoding];
1.121 + return [str autorelease];
1.122 +}
1.123 +
1.124 +
1.125 +- (void)netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data
1.126 +{
1.127 + NSDictionary *txtDict = [NSNetService dictionaryFromTXTRecordData: data];
1.128 + if( ! $equal(txtDict,_txtRecord) ) {
1.129 + LogTo(Bonjour,@"%@ got TXT record (%u bytes)",self,data.length);
1.130 + [self willChangeValueForKey: @"txtRecord"];
1.131 + setObj(&_txtRecord,txtDict);
1.132 + [self didChangeValueForKey: @"txtRecord"];
1.133 + [self txtRecordChanged];
1.134 + }
1.135 +}
1.136 +
1.137 +
1.138 +#pragma mark -
1.139 +#pragma mark ADDRESS RESOLUTION:
1.140 +
1.141 +
1.142 +#define kAddressResolveTimeout 10.0
1.143 +#define kAddressExpirationInterval 60.0
1.144 +#define kAddressErrorRetryInterval 5.0
1.145 +
1.146 +
1.147 +- (NSSet*) addresses
1.148 +{
1.149 + if( _addresses && CFAbsoluteTimeGetCurrent() >= _addressesExpireAt ) {
1.150 + setObj(&_addresses,nil); // eww, toss 'em and get new ones
1.151 + [self resolve];
1.152 + }
1.153 + return _addresses;
1.154 +}
1.155 +
1.156 +
1.157 +- (MYBonjourResolveOperation*) resolve
1.158 +{
1.159 + if( ! _resolveOp ) {
1.160 + LogTo(Bonjour,@"Resolving %@",self);
1.161 + _resolveOp = [[MYBonjourResolveOperation alloc] init];
1.162 + _resolveOp.service = self;
1.163 + [_resolveOp start];
1.164 + Assert(_netService);
1.165 + Assert(_netService.delegate=self);
1.166 + [_netService resolveWithTimeout: kAddressResolveTimeout];
1.167 + }
1.168 + return _resolveOp;
1.169 +}
1.170 +
1.171 +- (void) setAddresses: (NSSet*)addresses
1.172 +{
1.173 + setObj(&_addresses,addresses);
1.174 +}
1.175 +
1.176 +
1.177 +- (void) _finishedResolving: (NSSet*)addresses expireIn: (NSTimeInterval)expirationInterval
1.178 +{
1.179 + _addressesExpireAt = CFAbsoluteTimeGetCurrent() + expirationInterval;
1.180 + self.addresses = addresses;
1.181 + _resolveOp.addresses = addresses;
1.182 + [_resolveOp finish];
1.183 + [_resolveOp release];
1.184 + _resolveOp = nil;
1.185 +}
1.186 +
1.187 +
1.188 +- (void)netServiceDidResolveAddress:(NSNetService *)sender
1.189 +{
1.190 + // Convert the raw sockaddrs into IPAddress objects:
1.191 + NSMutableSet *addresses = [NSMutableSet setWithCapacity: 2];
1.192 + for( NSData *rawAddr in _netService.addresses ) {
1.193 + IPAddress *addr = [[IPAddress alloc] initWithSockAddr: rawAddr.bytes];
1.194 + if( addr ) {
1.195 + [addresses addObject: addr];
1.196 + [addr release];
1.197 + }
1.198 + }
1.199 + LogTo(Bonjour,@"Resolved %@: %@",self,addresses);
1.200 + [self _finishedResolving: addresses expireIn: kAddressExpirationInterval];
1.201 +}
1.202 +
1.203 +- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
1.204 +{
1.205 + LogTo(Bonjour,@"Error resolving %@ -- %@",self,errorDict);
1.206 + [self _finishedResolving: [NSArray array] expireIn: kAddressErrorRetryInterval];
1.207 +}
1.208 +
1.209 +- (void)netServiceDidStop:(NSNetService *)sender
1.210 +{
1.211 + LogTo(Bonjour,@"Resolve stopped for %@",self);
1.212 + [self _finishedResolving: [NSArray array] expireIn: kAddressErrorRetryInterval];
1.213 +}
1.214 +
1.215 +
1.216 +@end
1.217 +
1.218 +
1.219 +
1.220 +
1.221 +@implementation MYBonjourResolveOperation
1.222 +
1.223 +@synthesize service=_service, addresses=_addresses;
1.224 +
1.225 +- (void) dealloc
1.226 +{
1.227 + [_addresses release];
1.228 + [super dealloc];
1.229 +}
1.230 +
1.231 +@end
1.232 +
1.233 +
1.234 +/*
1.235 + Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
1.236 +
1.237 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.238 + provided that the following conditions are met:
1.239 +
1.240 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.241 + and the following disclaimer.
1.242 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
1.243 + and the following disclaimer in the documentation and/or other materials provided with the
1.244 + distribution.
1.245 +
1.246 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.247 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.248 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.249 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.250 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.251 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.252 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.253 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.254 + */