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 |
*/
|