More work on Bonjour classes. They now support registering services.
1.1 --- a/Bonjour/MYAddressLookup.h Tue Apr 28 10:36:28 2009 -0700
1.2 +++ b/Bonjour/MYAddressLookup.h Wed Apr 29 13:29:31 2009 -0700
1.3 @@ -19,14 +19,17 @@
1.4 CFAbsoluteTime _expires;
1.5 }
1.6
1.7 -/** Initializes the lookup with a DNS hostname. */
1.8 +/** Initializes the lookup with a DNS hostname.
1.9 + (If you've got a Bonjour service already, as a MYBonjourService object, it's more convenient
1.10 + to access its addressLookup property instead of creating your own instance.) */
1.11 - (id) initWithHostname: (NSString*)hostname;
1.12
1.13 /** The port number; this will be copied into the resulting IPAddress objects.
1.14 Defaults to zero, but you can set it before calling -start. */
1.15 @property UInt16 port;
1.16
1.17 -/** The index of the network interface. You usually don't need to set this. */
1.18 +/** The index of the network interface to use, or zero (the default) for any interface.
1.19 + You usually don't need to set this. */
1.20 @property UInt16 interfaceIndex;
1.21
1.22 /** The resulting address(es) of the host, as HostAddress objects. */
2.1 --- a/Bonjour/MYAddressLookup.m Tue Apr 28 10:36:28 2009 -0700
2.2 +++ b/Bonjour/MYAddressLookup.m Wed Apr 29 13:29:31 2009 -0700
2.3 @@ -60,17 +60,17 @@
2.4 sockaddr: sockaddr
2.5 port: _port];
2.6 if (address) {
2.7 - if (flags & kDNSServiceFlagsAdd)
2.8 + if (flags & kDNSServiceFlagsAdd) {
2.9 + LogTo(DNS,@"%@ got %@ [TTL = %u]", self, address, ttl);
2.10 [_addresses addObject: address];
2.11 - else
2.12 + } else {
2.13 + LogTo(DNS,@"%@ lost %@ [TTL = %u]", self, address, ttl);
2.14 [_addresses removeObject: address];
2.15 + }
2.16 [address release];
2.17 }
2.18
2.19 _expires = CFAbsoluteTimeGetCurrent() + ttl;
2.20 -
2.21 - if (!(flags & kDNSServiceFlagsMoreComing))
2.22 - LogTo(DNS,@"Got addresses of %@: %@ [TTL = %u]", self, _addresses, ttl);
2.23 }
2.24
2.25
2.26 @@ -91,17 +91,17 @@
2.27 else
2.28 [lookup priv_resolvedAddress: address ttl: ttl flags: flags];
2.29 }catchAndReport(@"MYDNSLookup query callback");
2.30 + [lookup gotResponse: errorCode];
2.31 }
2.32
2.33
2.34 -- (DNSServiceRef) createServiceRef {
2.35 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
2.36 [_addresses removeAllObjects];
2.37 - DNSServiceRef serviceRef = NULL;
2.38 - self.error = DNSServiceGetAddrInfo(&serviceRef, 0,
2.39 - _interfaceIndex, 0,
2.40 - _hostname.UTF8String,
2.41 - &lookupCallback, self);
2.42 - return serviceRef;
2.43 + return DNSServiceGetAddrInfo(sdRefPtr,
2.44 + kDNSServiceFlagsShareConnection,
2.45 + _interfaceIndex, 0,
2.46 + _hostname.UTF8String,
2.47 + &lookupCallback, self);
2.48 }
2.49
2.50
2.51 @@ -120,3 +120,26 @@
2.52 [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 10]];
2.53 [lookup release];
2.54 }
2.55 +
2.56 +
2.57 +/*
2.58 + Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
2.59 +
2.60 + Redistribution and use in source and binary forms, with or without modification, are permitted
2.61 + provided that the following conditions are met:
2.62 +
2.63 + * Redistributions of source code must retain the above copyright notice, this list of conditions
2.64 + and the following disclaimer.
2.65 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
2.66 + and the following disclaimer in the documentation and/or other materials provided with the
2.67 + distribution.
2.68 +
2.69 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
2.70 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2.71 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
2.72 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2.73 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2.74 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2.75 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
2.76 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2.77 + */
3.1 --- a/Bonjour/MYBonjourBrowser.h Tue Apr 28 10:36:28 2009 -0700
3.2 +++ b/Bonjour/MYBonjourBrowser.h Wed Apr 29 13:29:31 2009 -0700
3.3 @@ -7,6 +7,7 @@
3.4 //
3.5
3.6 #import "MYDNSService.h"
3.7 +@class MYBonjourRegistration;
3.8
3.9
3.10 /** Searches for Bonjour services of a specific type. */
3.11 @@ -17,6 +18,8 @@
3.12 BOOL _browsing;
3.13 Class _serviceClass;
3.14 NSMutableSet *_services, *_addServices, *_rmvServices;
3.15 + BOOL _pendingUpdate;
3.16 + MYBonjourRegistration *_myRegistration;
3.17 }
3.18
3.19 /** Initializes a new BonjourBrowser.
3.20 @@ -37,4 +40,10 @@
3.21 to a subclass of that. */
3.22 @property Class serviceClass;
3.23
3.24 +/** My own registration for this service type.
3.25 + This object is created on demand and won't be started up until you tell it to.
3.26 + Before starting it, you'll need to set its port, and optionally its name.
3.27 + Your own registration will _not_ be visible in the set of services.*/
3.28 +@property (readonly) MYBonjourRegistration *myRegistration;
3.29 +
3.30 @end
4.1 --- a/Bonjour/MYBonjourBrowser.m Tue Apr 28 10:36:28 2009 -0700
4.2 +++ b/Bonjour/MYBonjourBrowser.m Wed Apr 29 13:29:31 2009 -0700
4.3 @@ -8,6 +8,7 @@
4.4
4.5 #import "MYBonjourBrowser.h"
4.6 #import "MYBonjourService.h"
4.7 +#import "MYBonjourRegistration.h"
4.8 #import "ExceptionUtils.h"
4.9 #import "Test.h"
4.10 #import "Logging.h"
4.11 @@ -51,6 +52,8 @@
4.12 - (void) dealloc
4.13 {
4.14 LogTo(Bonjour,@"DEALLOC BonjourBrowser");
4.15 + [_myRegistration cancel];
4.16 + [_myRegistration release];
4.17 [_serviceType release];
4.18 [_services release];
4.19 [_addServices release];
4.20 @@ -68,12 +71,12 @@
4.21 }
4.22
4.23
4.24 -- (DNSServiceRef) createServiceRef {
4.25 - DNSServiceRef serviceRef = NULL;
4.26 - self.error = DNSServiceBrowse(&serviceRef, 0, 0,
4.27 - _serviceType.UTF8String, NULL,
4.28 - &browseCallback, self);
4.29 - return serviceRef;
4.30 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
4.31 + return DNSServiceBrowse(sdRefPtr,
4.32 + kDNSServiceFlagsShareConnection,
4.33 + 0,
4.34 + _serviceType.UTF8String, NULL,
4.35 + &browseCallback, self);
4.36 }
4.37
4.38
4.39 @@ -93,8 +96,15 @@
4.40 type: regtype
4.41 domain: domain
4.42 interface: interfaceIndex];
4.43 + if ([_myRegistration isSameAsService: service]) {
4.44 + // This is an echo of my own registration, so ignore it
4.45 + LogTo(Bonjour,@"%@ ignoring echo %@", self,service);
4.46 + [service release];
4.47 + return;
4.48 + }
4.49 MYBonjourService *existingService = [_services member: service];
4.50 if( existingService ) {
4.51 + // Use existing service object instead of creating a new one
4.52 [service release];
4.53 service = [existingService retain];
4.54 }
4.55 @@ -114,14 +124,17 @@
4.56 [addTo addObject: service];
4.57 [service release];
4.58
4.59 - // After a round of updates is done, do the update:
4.60 - if( ! (flags & kDNSServiceFlagsMoreComing) )
4.61 - [self _updateServiceList];
4.62 + // Schedule a (single) call to _updateServiceList:
4.63 + if (!_pendingUpdate) {
4.64 + [self performSelector: @selector(_updateServiceList) withObject: nil afterDelay: 0];
4.65 + _pendingUpdate = YES;
4.66 + }
4.67 }
4.68
4.69
4.70 - (void) _updateServiceList
4.71 {
4.72 + _pendingUpdate = NO;
4.73 if( _rmvServices.count ) {
4.74 [self willChangeValueForKey: @"services"
4.75 withSetMutation: NSKeyValueMinusSetMutation
4.76 @@ -156,17 +169,30 @@
4.77 const char *replyDomain,
4.78 void *context)
4.79 {
4.80 + MYBonjourBrowser *browser = context;
4.81 @try{
4.82 //LogTo(Bonjour,@"browseCallback (error=%i, name='%s')", errorCode,serviceName);
4.83 - if (errorCode)
4.84 - [(MYBonjourBrowser*)context priv_gotError: errorCode];
4.85 - else
4.86 - [(MYBonjourBrowser*)context priv_gotServiceName: [NSString stringWithUTF8String: serviceName]
4.87 - type: [NSString stringWithUTF8String: regtype]
4.88 - domain: [NSString stringWithUTF8String: replyDomain]
4.89 - interface: interfaceIndex
4.90 - flags: flags];
4.91 + if (!errorCode)
4.92 + [browser priv_gotServiceName: [NSString stringWithUTF8String: serviceName]
4.93 + type: [NSString stringWithUTF8String: regtype]
4.94 + domain: [NSString stringWithUTF8String: replyDomain]
4.95 + interface: interfaceIndex
4.96 + flags: flags];
4.97 }catchAndReport(@"Bonjour");
4.98 + [browser gotResponse: errorCode];
4.99 +}
4.100 +
4.101 +
4.102 +- (void) cancel {
4.103 + [_myRegistration stop];
4.104 + [super cancel];
4.105 +}
4.106 +
4.107 +
4.108 +- (MYBonjourRegistration *) myRegistration {
4.109 + if (!_myRegistration)
4.110 + _myRegistration = [[MYBonjourRegistration alloc] initWithServiceType: _serviceType port: 0];
4.111 + return _myRegistration;
4.112 }
4.113
4.114
4.115 @@ -198,6 +224,10 @@
4.116 [_browser addObserver: self forKeyPath: @"services" options: NSKeyValueObservingOptionNew context: NULL];
4.117 [_browser addObserver: self forKeyPath: @"browsing" options: NSKeyValueObservingOptionNew context: NULL];
4.118 [_browser start];
4.119 +
4.120 + MYBonjourRegistration *myReg = _browser.myRegistration;
4.121 + myReg.port = 12346;
4.122 + Assert([myReg start]);
4.123 }
4.124 return self;
4.125 }
5.1 --- a/Bonjour/MYBonjourQuery.m Tue Apr 28 10:36:28 2009 -0700
5.2 +++ b/Bonjour/MYBonjourQuery.m Wed Apr 29 13:29:31 2009 -0700
5.3 @@ -105,31 +105,55 @@
5.4 uint32_t ttl,
5.5 void *context)
5.6 {
5.7 + MYBonjourQuery *query = context;
5.8 @try{
5.9 //LogTo(Bonjour, @"queryCallback for %@ (err=%i)", context,errorCode);
5.10 - if (errorCode)
5.11 - [(MYBonjourQuery*)context setError: errorCode];
5.12 - else
5.13 - [(MYBonjourQuery*)context priv_gotRecordBytes: rdata
5.14 - length: rdlen
5.15 - type: rrtype
5.16 - ttl: ttl
5.17 - flags: flags];
5.18 + if (!errorCode)
5.19 + [query priv_gotRecordBytes: rdata
5.20 + length: rdlen
5.21 + type: rrtype
5.22 + ttl: ttl
5.23 + flags: flags];
5.24 }catchAndReport(@"MYBonjourResolver query callback");
5.25 + [query gotResponse: errorCode];
5.26 }
5.27
5.28
5.29 -- (DNSServiceRef) createServiceRef {
5.30 - DNSServiceRef serviceRef = NULL;
5.31 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
5.32 const char *fullName = _bonjourService.fullName.UTF8String;
5.33 if (fullName)
5.34 - self.error = DNSServiceQueryRecord(&serviceRef, 0,
5.35 - _bonjourService.interfaceIndex,
5.36 - fullName,
5.37 - _recordType, kDNSServiceClass_IN,
5.38 - &queryCallback, self);
5.39 - return serviceRef;
5.40 + return DNSServiceQueryRecord(sdRefPtr,
5.41 + kDNSServiceFlagsShareConnection,
5.42 + _bonjourService.interfaceIndex,
5.43 + fullName,
5.44 + _recordType, kDNSServiceClass_IN,
5.45 + &queryCallback, self);
5.46 + else
5.47 + return kDNSServiceErr_NoSuchName;
5.48 }
5.49
5.50
5.51 @end
5.52 +
5.53 +
5.54 +/*
5.55 + Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
5.56 +
5.57 + Redistribution and use in source and binary forms, with or without modification, are permitted
5.58 + provided that the following conditions are met:
5.59 +
5.60 + * Redistributions of source code must retain the above copyright notice, this list of conditions
5.61 + and the following disclaimer.
5.62 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
5.63 + and the following disclaimer in the documentation and/or other materials provided with the
5.64 + distribution.
5.65 +
5.66 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
5.67 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
5.68 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
5.69 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
5.70 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
5.71 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5.72 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
5.73 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5.74 + */
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/Bonjour/MYBonjourRegistration.h Wed Apr 29 13:29:31 2009 -0700
6.3 @@ -0,0 +1,81 @@
6.4 +//
6.5 +// MYBonjourRegistration.h
6.6 +// MYNetwork
6.7 +//
6.8 +// Created by Jens Alfke on 4/27/09.
6.9 +// Copyright 2009 Jens Alfke. All rights reserved.
6.10 +//
6.11 +
6.12 +#import "MYDNSService.h"
6.13 +@class MYBonjourService;
6.14 +
6.15 +
6.16 +/** Registers a local network service with Bonjour, so it can be browsed by other computers. */
6.17 +@interface MYBonjourRegistration : MYDNSService
6.18 +{
6.19 + NSString *_name, *_type, *_domain;
6.20 + UInt16 _port;
6.21 + BOOL _autoRename;
6.22 + BOOL _registered;
6.23 + NSMutableDictionary *_txtRecord;
6.24 +}
6.25 +
6.26 +/** Initializes a new registration.
6.27 + If you're also browsing for the same service type, you should instead get an instance of this via
6.28 + the MYBonjourBrowser's 'myRegistration' property -- that way the browser knows about the
6.29 + registration and won't echo it back to you.
6.30 + Either way, don't forget to call -start! */
6.31 +- (id) initWithServiceType: (NSString*)serviceType port: (UInt16)port;
6.32 +
6.33 +/** The name to register this service under.
6.34 + This is often left nil, in which case the user's chosen "Computer Name" (from the Sharing system
6.35 + pref pane) will be used.
6.36 + This can only be set before calling -start! */
6.37 +@property (copy) NSString *name;
6.38 +
6.39 +/** The registration's service type. */
6.40 +@property (readonly) NSString *type;
6.41 +
6.42 +/** The registration's IP port number.
6.43 + You'll need to set this if you got this object from MYBonjourBrowser's 'myRegistration' property,
6.44 + as that object doesn't have a pre-set port number yet.
6.45 + This can only be set before calling -start! */
6.46 +@property UInt16 port;
6.47 +
6.48 +/** The registration's full name -- the name, type and domain concatenated together. */
6.49 +@property (readonly) NSString *fullName;
6.50 +
6.51 +/** Is the registration currently active? */
6.52 +@property (readonly) BOOL registered;
6.53 +
6.54 +/** The service's metadata dictionary, stored in its DNS TXT record */
6.55 +@property (copy) NSDictionary *txtRecord;
6.56 +
6.57 +/** Convenience to store a string value in a single TXT record key. */
6.58 +- (void) setString: (NSString*)value forTxtKey: (NSString*)key;
6.59 +
6.60 +
6.61 +/** @name Expert
6.62 + * Advanced methods you likely won't need
6.63 + */
6.64 +//@{
6.65 +
6.66 +/** The registration's domain name.
6.67 + This is almost always left nil, in which case the default registration domain is used
6.68 + (usually ".local".)
6.69 + This can only be set before calling -start! */
6.70 +@property (copy) NSString *domain;
6.71 +
6.72 +/** Determines what to do if there's a name conflict with an already-registered service on the
6.73 + network.
6.74 + If set to YES (the default), a number will be appended to the name to make it unique.
6.75 + If set to NO, the registration will fail, and you can choose a different name and try again.
6.76 + This can only be set before calling -start! */
6.77 +@property BOOL autoRename;
6.78 +
6.79 +/** Is this browsed service an echo of this local registration? (Compares fullNames.) */
6.80 +- (BOOL) isSameAsService: (MYBonjourService*)service;
6.81 +
6.82 +//@}
6.83 +
6.84 +@end
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/Bonjour/MYBonjourRegistration.m Wed Apr 29 13:29:31 2009 -0700
7.3 @@ -0,0 +1,289 @@
7.4 +//
7.5 +// MYBonjourRegistration.m
7.6 +// MYNetwork
7.7 +//
7.8 +// Created by Jens Alfke on 4/27/09.
7.9 +// Copyright 2009 Jens Alfke. All rights reserved.
7.10 +//
7.11 +
7.12 +#import "MYBonjourRegistration.h"
7.13 +#import "MYBonjourService.h"
7.14 +#import "ExceptionUtils.h"
7.15 +#import "Test.h"
7.16 +#import "Logging.h"
7.17 +#import <dns_sd.h>
7.18 +
7.19 +
7.20 +#define kTXTTTL 60 // TTL in seconds for TXT records I register
7.21 +
7.22 +
7.23 +@interface MYBonjourRegistration ()
7.24 +@property BOOL registered;
7.25 +@end
7.26 +
7.27 +
7.28 +@implementation MYBonjourRegistration
7.29 +
7.30 +
7.31 +static NSMutableDictionary *sAllRegistrations;
7.32 +
7.33 +
7.34 ++ (void) priv_addRegistration: (MYBonjourRegistration*)reg {
7.35 + if (!sAllRegistrations)
7.36 + sAllRegistrations = [[NSMutableDictionary alloc] init];
7.37 + [sAllRegistrations setObject: reg forKey: reg.fullName];
7.38 +}
7.39 +
7.40 ++ (void) priv_removeRegistration: (MYBonjourRegistration*)reg {
7.41 + [sAllRegistrations removeObjectForKey: reg.fullName];
7.42 +}
7.43 +
7.44 ++ (MYBonjourRegistration*) registrationWithFullName: (NSString*)fullName {
7.45 + return [sAllRegistrations objectForKey: fullName];
7.46 +}
7.47 +
7.48 +
7.49 +- (id) initWithServiceType: (NSString*)serviceType port: (UInt16)port
7.50 +{
7.51 + self = [super init];
7.52 + if (self != nil) {
7.53 + self.continuous = YES;
7.54 + self.usePrivateConnection = YES; // DNSServiceUpdateRecord doesn't work with shared conn :(
7.55 + _type = [serviceType copy];
7.56 + _port = port;
7.57 + _autoRename = YES;
7.58 + }
7.59 + return self;
7.60 +}
7.61 +
7.62 +- (void) dealloc {
7.63 + [_name release];
7.64 + [_type release];
7.65 + [_domain release];
7.66 + [super dealloc];
7.67 +}
7.68 +
7.69 +
7.70 +@synthesize name=_name, type=_type, domain=_domain, port=_port, autoRename=_autoRename;
7.71 +@synthesize registered=_registered;
7.72 +
7.73 +
7.74 +- (NSString*) fullName {
7.75 + return [[self class] fullNameOfService: _name ofType: _type inDomain: _domain];
7.76 +}
7.77 +
7.78 +
7.79 +- (BOOL) isSameAsService: (MYBonjourService*)service {
7.80 + return _name && _domain && [self.fullName isEqualToString: service.fullName];
7.81 +}
7.82 +
7.83 +
7.84 +- (NSString*) description
7.85 +{
7.86 + return $sprintf(@"%@['%@'.%@%@]", self.class,_name,_type,_domain);
7.87 +}
7.88 +
7.89 +
7.90 +- (void) priv_registeredAsName: (NSString*)name
7.91 + type: (NSString*)regtype
7.92 + domain: (NSString*)domain
7.93 +{
7.94 + if (!$equal(name,_name))
7.95 + self.name = name;
7.96 + if (!$equal(domain,_domain))
7.97 + self.domain = domain;
7.98 + LogTo(Bonjour,@"Registered %@", self);
7.99 + self.registered = YES;
7.100 + [[self class] priv_addRegistration: self];
7.101 +}
7.102 +
7.103 +
7.104 +static void regCallback(DNSServiceRef sdRef,
7.105 + DNSServiceFlags flags,
7.106 + DNSServiceErrorType errorCode,
7.107 + const char *name,
7.108 + const char *regtype,
7.109 + const char *domain,
7.110 + void *context)
7.111 +{
7.112 + MYBonjourRegistration *reg = context;
7.113 + @try{
7.114 + if (!errorCode)
7.115 + [reg priv_registeredAsName: [NSString stringWithUTF8String: name]
7.116 + type: [NSString stringWithUTF8String: regtype]
7.117 + domain: [NSString stringWithUTF8String: domain]];
7.118 + }catchAndReport(@"MYBonjourRegistration callback");
7.119 + [reg gotResponse: errorCode];
7.120 +}
7.121 +
7.122 +
7.123 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
7.124 + DNSServiceFlags flags = 0;
7.125 + if (!_autoRename)
7.126 + flags |= kDNSServiceFlagsNoAutoRename;
7.127 + NSData *txtData = nil;
7.128 + if (_txtRecord)
7.129 + txtData = [NSNetService dataFromTXTRecordDictionary: _txtRecord];
7.130 + return DNSServiceRegister(sdRefPtr,
7.131 + flags,
7.132 + 0,
7.133 + _name.UTF8String, // _name is likely to be nil
7.134 + _type.UTF8String,
7.135 + _domain.UTF8String, // _domain is most likely nil
7.136 + NULL,
7.137 + htons(_port),
7.138 + txtData.length,
7.139 + txtData.bytes,
7.140 + ®Callback,
7.141 + self);
7.142 +}
7.143 +
7.144 +
7.145 +- (void) cancel {
7.146 + [super cancel];
7.147 + if (_registered) {
7.148 + [[self class] priv_removeRegistration: self];
7.149 + self.registered = NO;
7.150 + }
7.151 +}
7.152 +
7.153 +
7.154 +- (void) updateTxtRecord {
7.155 + [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
7.156 + if (self.serviceRef) {
7.157 + NSData *data = [NSNetService dataFromTXTRecordDictionary: _txtRecord];
7.158 + Assert(data!=nil, @"Can't convert dictionary to TXT record");
7.159 + DNSServiceErrorType err = DNSServiceUpdateRecord(self.serviceRef,
7.160 + NULL,
7.161 + 0,
7.162 + data.length,
7.163 + data.bytes,
7.164 + kTXTTTL);
7.165 + if (err)
7.166 + Warn(@"%@ failed to update TXT (err=%i)", self,err);
7.167 + else
7.168 + LogTo(Bonjour,@"%@ updated TXT to %@", self,data);
7.169 + }
7.170 +}
7.171 +
7.172 +
7.173 +- (NSDictionary*) txtRecord {
7.174 + return _txtRecord;
7.175 +}
7.176 +
7.177 +- (void) setTxtRecord: (NSDictionary*)txtDict {
7.178 + if (!$equal(_txtRecord,txtDict)) {
7.179 + if (txtDict)
7.180 + [_txtRecord setDictionary: txtDict];
7.181 + else
7.182 + setObj(&_txtRecord,nil);
7.183 + [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
7.184 + [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
7.185 + }
7.186 +}
7.187 +
7.188 +- (void) setString: (NSString*)value forTxtKey: (NSString*)key
7.189 +{
7.190 + NSData *data = [value dataUsingEncoding: NSUTF8StringEncoding];
7.191 + if (!$equal(data, [_txtRecord objectForKey: key])) {
7.192 + if (data) {
7.193 + if (!_txtRecord) _txtRecord = [[NSMutableDictionary alloc] init];
7.194 + [_txtRecord setObject: data forKey: key];
7.195 + } else
7.196 + [_txtRecord removeObjectForKey: key];
7.197 + [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
7.198 + [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
7.199 + }
7.200 +}
7.201 +
7.202 +@end
7.203 +
7.204 +
7.205 +
7.206 +
7.207 +#pragma mark -
7.208 +#pragma mark TESTING:
7.209 +
7.210 +#if DEBUG
7.211 +
7.212 +#import "MYBonjourQuery.h"
7.213 +#import "MYAddressLookup.h"
7.214 +
7.215 +@interface BonjourRegTester : NSObject
7.216 +{
7.217 + MYBonjourRegistration *_reg;
7.218 + BOOL _updating;
7.219 +}
7.220 +@end
7.221 +
7.222 +@implementation BonjourRegTester
7.223 +
7.224 +- (void) updateTXT {
7.225 + NSDictionary *txt = $dict({@"time", $sprintf(@"%.3lf", CFAbsoluteTimeGetCurrent())});
7.226 + _reg.txtRecord = txt;
7.227 + [self performSelector: @selector(updateTXT) withObject: nil afterDelay: 3.0];
7.228 +}
7.229 +
7.230 +- (id) init
7.231 +{
7.232 + self = [super init];
7.233 + if (self != nil) {
7.234 + _reg = [[MYBonjourRegistration alloc] initWithServiceType: @"_foo._tcp" port: 12345];
7.235 + [_reg addObserver: self forKeyPath: @"registered" options: NSKeyValueObservingOptionNew context: NULL];
7.236 + [_reg addObserver: self forKeyPath: @"name" options: NSKeyValueObservingOptionNew context: NULL];
7.237 +
7.238 + [self updateTXT];
7.239 + [_reg start];
7.240 + }
7.241 + return self;
7.242 +}
7.243 +
7.244 +- (void) dealloc
7.245 +{
7.246 + [_reg stop];
7.247 + [_reg removeObserver: self forKeyPath: @"registered"];
7.248 + [_reg removeObserver: self forKeyPath: @"name"];
7.249 + [_reg release];
7.250 + [super dealloc];
7.251 +}
7.252 +
7.253 +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
7.254 +{
7.255 + LogTo(Bonjour,@"Observed change in %@: %@",keyPath,change);
7.256 +}
7.257 +
7.258 +@end
7.259 +
7.260 +TestCase(BonjourReg) {
7.261 + EnableLogTo(Bonjour,YES);
7.262 + EnableLogTo(DNS,YES);
7.263 + [NSRunLoop currentRunLoop]; // create runloop
7.264 + BonjourRegTester *tester = [[BonjourRegTester alloc] init];
7.265 + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 15]];
7.266 + [tester release];
7.267 +}
7.268 +
7.269 +#endif
7.270 +
7.271 +
7.272 +/*
7.273 + Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
7.274 +
7.275 + Redistribution and use in source and binary forms, with or without modification, are permitted
7.276 + provided that the following conditions are met:
7.277 +
7.278 + * Redistributions of source code must retain the above copyright notice, this list of conditions
7.279 + and the following disclaimer.
7.280 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
7.281 + and the following disclaimer in the documentation and/or other materials provided with the
7.282 + distribution.
7.283 +
7.284 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
7.285 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
7.286 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
7.287 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
7.288 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
7.289 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7.290 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
7.291 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7.292 + */
8.1 --- a/Bonjour/MYBonjourService.h Tue Apr 28 10:36:28 2009 -0700
8.2 +++ b/Bonjour/MYBonjourService.h Wed Apr 29 13:29:31 2009 -0700
8.3 @@ -33,13 +33,34 @@
8.4 /** The service's domain. */
8.5 @property (readonly) NSString *domain;
8.6
8.7 +/** The service's full name -- the name, type and domain concatenated together. */
8.8 +@property (readonly,copy) NSString* fullName;
8.9 +
8.10 +/** The index of the network interface on which this service was found. */
8.11 +@property (readonly) uint32_t interfaceIndex;
8.12 +
8.13 +
8.14 +/** @name Addressing
8.15 + * Getting the IP address of the service
8.16 + */
8.17 +//@{
8.18 +
8.19 +/** The hostname of the machine providing this service. */
8.20 @property (readonly, copy) NSString *hostname;
8.21
8.22 +/** The IP port number of this service on its host. */
8.23 @property (readonly) UInt16 port;
8.24
8.25 -@property (readonly) uint32_t interfaceIndex;
8.26 +/** Returns a MYDNSLookup object that resolves the raw IP address(es) of this service.
8.27 + Subsequent calls to this method will always return the same object. */
8.28 +- (MYAddressLookup*) addressLookup;
8.29
8.30 -@property (readonly,copy) NSString* fullName;
8.31 +//@}
8.32 +
8.33 +
8.34 +/** @name TXT and other DNS records
8.35 + */
8.36 +//@{
8.37
8.38 /** The service's metadata dictionary, from its DNS TXT record */
8.39 @property (readonly,copy) NSDictionary *txtRecord;
8.40 @@ -47,42 +68,41 @@
8.41 /** A convenience to access a single property from the TXT record. */
8.42 - (NSString*) txtStringForKey: (NSString*)key;
8.43
8.44 -/** Returns a MYDNSLookup object that resolves the IP address(es) of this service.
8.45 - Subsequent calls to this method will always return the same object. */
8.46 -- (MYAddressLookup*) addressLookup;
8.47 -
8.48 /** Starts a new MYBonjourQuery for the specified DNS record type of this service.
8.49 @param recordType The DNS record type, e.g. kDNSServiceType_TXT; see the enum in <dns_sd.h>. */
8.50 - (MYBonjourQuery*) queryForRecord: (UInt16)recordType;
8.51
8.52 +//@}
8.53
8.54 -// Protected methods, for subclass use only:
8.55
8.56 -// (for subclasses to override, but not call):
8.57 +/** @name Protected
8.58 + * Advanced methods that may be overridden by subclasses, but should not be called directly.
8.59 + */
8.60 +//@{
8.61 +
8.62 +/** Designated initializer. You probably don't want to create MYBonjourService instances yourself,
8.63 + but if you subclass you might need to override this initializer. */
8.64 - (id) initWithName: (NSString*)serviceName
8.65 type: (NSString*)type
8.66 domain: (NSString*)domain
8.67 - interface: (uint32_t)interfaceIndex;
8.68 + interface: (UInt32)interfaceIndex;
8.69
8.70 +/** Called when this service is officially added to its browser's service set.
8.71 + You can override this, but be sure to call the superclass method. */
8.72 - (void) added;
8.73 +
8.74 +/** Called when this service is officially removed to its browser's service set.
8.75 + You can override this, but be sure to call the superclass method. */
8.76 - (void) removed;
8.77 +
8.78 +/** Called when this service's TXT record changes.
8.79 + You can override this, but be sure to call the superclass method. */
8.80 - (void) txtRecordChanged;
8.81
8.82 -// Internal:
8.83 -
8.84 +/** Called when a query started by this service updates.
8.85 + You can override this, but be sure to call the superclass method. */
8.86 - (void) queryDidUpdate: (MYBonjourQuery*)query;
8.87
8.88 -@end
8.89 -
8.90 -
8.91 -
8.92 -@interface MYBonjourResolveOperation : ConcurrentOperation
8.93 -{
8.94 - MYBonjourService *_service;
8.95 - NSSet *_addresses;
8.96 -}
8.97 -
8.98 -@property (readonly) MYBonjourService *service;
8.99 -@property (readonly,retain) NSSet *addresses;
8.100 +//@}
8.101
8.102 @end
9.1 --- a/Bonjour/MYBonjourService.m Tue Apr 28 10:36:28 2009 -0700
9.2 +++ b/Bonjour/MYBonjourService.m Wed Apr 29 13:29:31 2009 -0700
9.3 @@ -30,36 +30,41 @@
9.4 - (id) initWithName: (NSString*)serviceName
9.5 type: (NSString*)type
9.6 domain: (NSString*)domain
9.7 - interface: (uint32)interfaceIndex
9.8 + interface: (UInt32)interfaceIndex
9.9 {
9.10 + Assert(serviceName);
9.11 + Assert(type);
9.12 + Assert(domain);
9.13 self = [super init];
9.14 if (self != nil) {
9.15 _name = [serviceName copy];
9.16 _type = [type copy];
9.17 _domain = [domain copy];
9.18 + _fullName = [[[self class] fullNameOfService: _name ofType: _type inDomain: _domain] retain];
9.19 _interfaceIndex = interfaceIndex;
9.20 }
9.21 return self;
9.22 }
9.23
9.24 - (void) dealloc {
9.25 - [_name release];
9.26 - [_type release];
9.27 - [_domain release];
9.28 - [_hostname release];
9.29 [_txtQuery stop];
9.30 [_txtQuery release];
9.31 [_addressLookup stop];
9.32 [_addressLookup release];
9.33 + [_name release];
9.34 + [_type release];
9.35 + [_domain release];
9.36 + [_fullName release];
9.37 + [_hostname release];
9.38 [super dealloc];
9.39 }
9.40
9.41
9.42 -@synthesize name=_name, type=_type, domain=_domain, interfaceIndex=_interfaceIndex;
9.43 +@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, interfaceIndex=_interfaceIndex;
9.44
9.45
9.46 - (NSString*) description {
9.47 - return $sprintf(@"%@['%@'.%@%@]", self.class,_name,_type,_domain);
9.48 + return $sprintf(@"%@[%@]", self.class,self.fullName);
9.49 }
9.50
9.51
9.52 @@ -101,18 +106,13 @@
9.53
9.54
9.55 - (void) priv_finishResolve {
9.56 - // If I haven't finished my resolve yet, run it synchronously now so I can return a valid value:
9.57 + // If I haven't finished my resolve yet, run it *synchronously* now so I can return a valid value:
9.58 if (!_startedResolve )
9.59 [self start];
9.60 if (self.serviceRef)
9.61 [self waitForReply];
9.62 }
9.63
9.64 -- (NSString*) fullName {
9.65 - if (!_fullName) [self priv_finishResolve];
9.66 - return _fullName;
9.67 -}
9.68 -
9.69 - (NSString*) hostname {
9.70 if (!_hostname) [self priv_finishResolve];
9.71 return _hostname;
9.72 @@ -129,9 +129,12 @@
9.73
9.74
9.75 - (NSDictionary*) txtRecord {
9.76 - // If I haven't started my resolve yet, start it now. (_txtRecord will be nil till it finishes.)
9.77 - if (!_startedResolve)
9.78 - [self start];
9.79 + if (!_txtQuery) {
9.80 + _txtQuery = [[MYBonjourQuery alloc] initWithBonjourService: self
9.81 + recordType: kDNSServiceType_TXT];
9.82 + _txtQuery.continuous = YES;
9.83 + [_txtQuery start];
9.84 + }
9.85 return _txtRecord;
9.86 }
9.87
9.88 @@ -172,32 +175,24 @@
9.89
9.90
9.91 #pragma mark -
9.92 -#pragma mark FULLNAME/HOSTNAME/PORT RESOLUTION:
9.93 +#pragma mark HOSTNAME/PORT RESOLUTION:
9.94
9.95
9.96 -- (void) priv_resolvedFullName: (NSString*)fullName
9.97 - hostname: (NSString*)hostname
9.98 +- (void) priv_resolvedHostname: (NSString*)hostname
9.99 port: (uint16_t)port
9.100 txtRecord: (NSData*)txtData
9.101 {
9.102 - LogTo(Bonjour, @"%@: fullname='%@', hostname=%@, port=%u, txt=%u bytes",
9.103 - self, fullName, hostname, port, txtData.length);
9.104 + LogTo(Bonjour, @"%@: hostname=%@, port=%u, txt=%u bytes",
9.105 + self, hostname, port, txtData.length);
9.106
9.107 // Don't call a setter method to set these properties: the getters are synchronous, so
9.108 // I might already be blocked in a call to one of them, in which case creating a KV
9.109 // notification could cause trouble...
9.110 - _fullName = fullName.copy;
9.111 _hostname = hostname.copy;
9.112 _port = port;
9.113
9.114 // TXT getter is async, though, so I can use a setter to announce the data's availability:
9.115 [self setTxtData: txtData];
9.116 -
9.117 - // Now that I know my full name, I can start a persistent query to track the TXT:
9.118 - _txtQuery = [[MYBonjourQuery alloc] initWithBonjourService: self
9.119 - recordType: kDNSServiceType_TXT];
9.120 - _txtQuery.continuous = YES;
9.121 - [_txtQuery start];
9.122 }
9.123
9.124
9.125 @@ -215,29 +210,26 @@
9.126 MYBonjourService *service = context;
9.127 @try{
9.128 //LogTo(Bonjour, @"resolveCallback for %@ (err=%i)", service,errorCode);
9.129 - if (errorCode) {
9.130 - [service setError: errorCode];
9.131 - } else {
9.132 + if (!errorCode) {
9.133 NSData *txtData = nil;
9.134 if (txtRecord)
9.135 txtData = [NSData dataWithBytes: txtRecord length: txtLen];
9.136 - [service priv_resolvedFullName: [NSString stringWithUTF8String: fullname]
9.137 - hostname: [NSString stringWithUTF8String: hosttarget]
9.138 + [service priv_resolvedHostname: [NSString stringWithUTF8String: hosttarget]
9.139 port: ntohs(port)
9.140 txtRecord: txtData];
9.141 }
9.142 }catchAndReport(@"MYBonjourResolver query callback");
9.143 + [service gotResponse: errorCode];
9.144 }
9.145
9.146
9.147 -- (DNSServiceRef) createServiceRef {
9.148 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
9.149 _startedResolve = YES;
9.150 - DNSServiceRef serviceRef = NULL;
9.151 - self.error = DNSServiceResolve(&serviceRef, 0,
9.152 - _interfaceIndex,
9.153 - _name.UTF8String, _type.UTF8String, _domain.UTF8String,
9.154 - &resolveCallback, self);
9.155 - return serviceRef;
9.156 + return DNSServiceResolve(sdRefPtr,
9.157 + kDNSServiceFlagsShareConnection,
9.158 + _interfaceIndex,
9.159 + _name.UTF8String, _type.UTF8String, _domain.UTF8String,
9.160 + &resolveCallback, self);
9.161 }
9.162
9.163
10.1 --- a/MYNetwork-iPhone.xcodeproj/project.pbxproj Tue Apr 28 10:36:28 2009 -0700
10.2 +++ b/MYNetwork-iPhone.xcodeproj/project.pbxproj Wed Apr 29 13:29:31 2009 -0700
10.3 @@ -41,6 +41,12 @@
10.4 278C1B2F0F9F865800954AE1 /* PortMapperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 278C1B2D0F9F865800954AE1 /* PortMapperTest.m */; };
10.5 278C1B350F9F86A100954AE1 /* MYUtilities_Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 278C1B330F9F86A100954AE1 /* MYUtilities_Debug.xcconfig */; };
10.6 278C1B360F9F86A100954AE1 /* MYUtilities_Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 278C1B340F9F86A100954AE1 /* MYUtilities_Release.xcconfig */; };
10.7 + 27D915BF0FA8EABC002B0DEC /* MYDNSService.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915BC0FA8EABC002B0DEC /* MYDNSService.m */; };
10.8 + 27D915C00FA8EABC002B0DEC /* MYAddressLookup.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915BE0FA8EABC002B0DEC /* MYAddressLookup.m */; };
10.9 + 27D915C90FA8EAD0002B0DEC /* MYBonjourBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915C20FA8EAD0002B0DEC /* MYBonjourBrowser.m */; };
10.10 + 27D915CA0FA8EAD0002B0DEC /* MYBonjourService.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915C40FA8EAD0002B0DEC /* MYBonjourService.m */; };
10.11 + 27D915CB0FA8EAD0002B0DEC /* MYBonjourQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915C60FA8EAD0002B0DEC /* MYBonjourQuery.m */; };
10.12 + 27D915CC0FA8EAD0002B0DEC /* MYBonjourRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D915C80FA8EAD0002B0DEC /* MYBonjourRegistration.m */; };
10.13 280E754F0DD40C5E005A515E /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 280E754C0DD40C5E005A515E /* MainWindow.xib */; };
10.14 /* End PBXBuildFile section */
10.15
10.16 @@ -107,6 +113,18 @@
10.17 278C1B2D0F9F865800954AE1 /* PortMapperTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PortMapperTest.m; sourceTree = "<group>"; };
10.18 278C1B330F9F86A100954AE1 /* MYUtilities_Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYUtilities_Debug.xcconfig; sourceTree = "<group>"; };
10.19 278C1B340F9F86A100954AE1 /* MYUtilities_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYUtilities_Release.xcconfig; sourceTree = "<group>"; };
10.20 + 27D915BB0FA8EABC002B0DEC /* MYDNSService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYDNSService.h; path = PortMapper/MYDNSService.h; sourceTree = "<group>"; };
10.21 + 27D915BC0FA8EABC002B0DEC /* MYDNSService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYDNSService.m; path = PortMapper/MYDNSService.m; sourceTree = "<group>"; };
10.22 + 27D915BD0FA8EABC002B0DEC /* MYAddressLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYAddressLookup.h; path = Bonjour/MYAddressLookup.h; sourceTree = "<group>"; };
10.23 + 27D915BE0FA8EABC002B0DEC /* MYAddressLookup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYAddressLookup.m; path = Bonjour/MYAddressLookup.m; sourceTree = "<group>"; };
10.24 + 27D915C10FA8EAD0002B0DEC /* MYBonjourBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYBonjourBrowser.h; path = Bonjour/MYBonjourBrowser.h; sourceTree = "<group>"; };
10.25 + 27D915C20FA8EAD0002B0DEC /* MYBonjourBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYBonjourBrowser.m; path = Bonjour/MYBonjourBrowser.m; sourceTree = "<group>"; };
10.26 + 27D915C30FA8EAD0002B0DEC /* MYBonjourService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYBonjourService.h; path = Bonjour/MYBonjourService.h; sourceTree = "<group>"; };
10.27 + 27D915C40FA8EAD0002B0DEC /* MYBonjourService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYBonjourService.m; path = Bonjour/MYBonjourService.m; sourceTree = "<group>"; };
10.28 + 27D915C50FA8EAD0002B0DEC /* MYBonjourQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYBonjourQuery.h; path = Bonjour/MYBonjourQuery.h; sourceTree = "<group>"; };
10.29 + 27D915C60FA8EAD0002B0DEC /* MYBonjourQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYBonjourQuery.m; path = Bonjour/MYBonjourQuery.m; sourceTree = "<group>"; };
10.30 + 27D915C70FA8EAD0002B0DEC /* MYBonjourRegistration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYBonjourRegistration.h; path = Bonjour/MYBonjourRegistration.h; sourceTree = "<group>"; };
10.31 + 27D915C80FA8EAD0002B0DEC /* MYBonjourRegistration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYBonjourRegistration.m; path = Bonjour/MYBonjourRegistration.m; sourceTree = "<group>"; };
10.32 280E754C0DD40C5E005A515E /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = "<group>"; };
10.33 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iPhone/main.m; sourceTree = "<group>"; };
10.34 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
10.35 @@ -149,11 +167,11 @@
10.36 270E9AA00EE61113003F17CA /* MYNetwork */ = {
10.37 isa = PBXGroup;
10.38 children = (
10.39 - 270E9AA10EE61113003F17CA /* IPAddress.h */,
10.40 - 270E9AA20EE61113003F17CA /* IPAddress.m */,
10.41 + 27D915B90FA8EA85002B0DEC /* Addressing */,
10.42 + 27D915BA0FA8EA98002B0DEC /* Bonjour */,
10.43 + 278C1B2A0F9F865800954AE1 /* PortMapper */,
10.44 270E9AA30EE61113003F17CA /* TCP */,
10.45 270E9AAF0EE61113003F17CA /* BLIP */,
10.46 - 278C1B2A0F9F865800954AE1 /* PortMapper */,
10.47 );
10.48 name = MYNetwork;
10.49 sourceTree = "<group>";
10.50 @@ -219,8 +237,7 @@
10.51 270E9ADA0EE6111A003F17CA /* GoogleToolboxSubset */,
10.52 );
10.53 name = MYUtilities;
10.54 - path = ../MYUtilities;
10.55 - sourceTree = SOURCE_ROOT;
10.56 + sourceTree = MYUtilities;
10.57 };
10.58 270E9ADA0EE6111A003F17CA /* GoogleToolboxSubset */ = {
10.59 isa = PBXGroup;
10.60 @@ -253,6 +270,34 @@
10.61 path = PortMapper;
10.62 sourceTree = "<group>";
10.63 };
10.64 + 27D915B90FA8EA85002B0DEC /* Addressing */ = {
10.65 + isa = PBXGroup;
10.66 + children = (
10.67 + 270E9AA10EE61113003F17CA /* IPAddress.h */,
10.68 + 270E9AA20EE61113003F17CA /* IPAddress.m */,
10.69 + 27D915BB0FA8EABC002B0DEC /* MYDNSService.h */,
10.70 + 27D915BC0FA8EABC002B0DEC /* MYDNSService.m */,
10.71 + 27D915BD0FA8EABC002B0DEC /* MYAddressLookup.h */,
10.72 + 27D915BE0FA8EABC002B0DEC /* MYAddressLookup.m */,
10.73 + );
10.74 + name = Addressing;
10.75 + sourceTree = "<group>";
10.76 + };
10.77 + 27D915BA0FA8EA98002B0DEC /* Bonjour */ = {
10.78 + isa = PBXGroup;
10.79 + children = (
10.80 + 27D915C10FA8EAD0002B0DEC /* MYBonjourBrowser.h */,
10.81 + 27D915C20FA8EAD0002B0DEC /* MYBonjourBrowser.m */,
10.82 + 27D915C30FA8EAD0002B0DEC /* MYBonjourService.h */,
10.83 + 27D915C40FA8EAD0002B0DEC /* MYBonjourService.m */,
10.84 + 27D915C50FA8EAD0002B0DEC /* MYBonjourQuery.h */,
10.85 + 27D915C60FA8EAD0002B0DEC /* MYBonjourQuery.m */,
10.86 + 27D915C70FA8EAD0002B0DEC /* MYBonjourRegistration.h */,
10.87 + 27D915C80FA8EAD0002B0DEC /* MYBonjourRegistration.m */,
10.88 + );
10.89 + name = Bonjour;
10.90 + sourceTree = "<group>";
10.91 + };
10.92 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
10.93 isa = PBXGroup;
10.94 children = (
10.95 @@ -387,6 +432,12 @@
10.96 270E9BA20EE64B4E003F17CA /* MyViewController.m in Sources */,
10.97 278C1B2E0F9F865800954AE1 /* MYPortMapper.m in Sources */,
10.98 278C1B2F0F9F865800954AE1 /* PortMapperTest.m in Sources */,
10.99 + 27D915BF0FA8EABC002B0DEC /* MYDNSService.m in Sources */,
10.100 + 27D915C00FA8EABC002B0DEC /* MYAddressLookup.m in Sources */,
10.101 + 27D915C90FA8EAD0002B0DEC /* MYBonjourBrowser.m in Sources */,
10.102 + 27D915CA0FA8EAD0002B0DEC /* MYBonjourService.m in Sources */,
10.103 + 27D915CB0FA8EAD0002B0DEC /* MYBonjourQuery.m in Sources */,
10.104 + 27D915CC0FA8EAD0002B0DEC /* MYBonjourRegistration.m in Sources */,
10.105 );
10.106 runOnlyForDeploymentPostprocessing = 0;
10.107 };
10.108 @@ -417,7 +468,7 @@
10.109 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
10.110 EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "*.nib *.lproj *.framework *.gch *.xcode* (*) CVS .svn .hg";
10.111 ONLY_ACTIVE_ARCH = YES;
10.112 - SDKROOT = iphonesimulator2.0;
10.113 + SDKROOT = iphonesimulator2.2.1;
10.114 };
10.115 name = Debug;
10.116 };
10.117 @@ -427,7 +478,7 @@
10.118 buildSettings = {
10.119 ARCHS = "$(ARCHS_STANDARD_32_BIT)";
10.120 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
10.121 - SDKROOT = iphonesimulator2.0;
10.122 + SDKROOT = iphonesimulator2.2.1;
10.123 };
10.124 name = Release;
10.125 };
11.1 --- a/MYNetwork.xcodeproj/project.pbxproj Tue Apr 28 10:36:28 2009 -0700
11.2 +++ b/MYNetwork.xcodeproj/project.pbxproj Wed Apr 29 13:29:31 2009 -0700
11.3 @@ -25,6 +25,9 @@
11.4 270461470DE491A6003D9D3F /* Target.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461460DE491A6003D9D3F /* Target.m */; };
11.5 270461890DE49634003D9D3F /* CollectionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461870DE49634003D9D3F /* CollectionUtils.m */; };
11.6 2706F1D90F9D3EF300292CCF /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2706F1D80F9D3EF300292CCF /* SecurityInterface.framework */; };
11.7 + 273B457B0FA681EE00276298 /* MYBonjourRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = 273B45790FA681EE00276298 /* MYBonjourRegistration.h */; };
11.8 + 273B457C0FA681EE00276298 /* MYBonjourRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 273B457A0FA681EE00276298 /* MYBonjourRegistration.m */; };
11.9 + 273B457D0FA681EE00276298 /* MYBonjourRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = 273B457A0FA681EE00276298 /* MYBonjourRegistration.m */; };
11.10 2777C9110F7602A7007F8D30 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2777C9100F7602A7007F8D30 /* Security.framework */; };
11.11 2779048B0DE9204300C6D295 /* BLIPEchoClient.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2779048A0DE9204300C6D295 /* BLIPEchoClient.xib */; };
11.12 277905240DE9E5BC00C6D295 /* BLIPEchoServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 277903D60DE8EE4800C6D295 /* BLIPEchoServer.m */; };
11.13 @@ -165,6 +168,8 @@
11.14 270462C10DE4A64B003D9D3F /* MYUtilitiesTest_main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYUtilitiesTest_main.m; sourceTree = "<group>"; };
11.15 270462C30DE4A65B003D9D3F /* BLIP Overview.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "BLIP Overview.txt"; path = "BLIP/BLIP Overview.txt"; sourceTree = "<group>"; wrapsLines = 1; };
11.16 2706F1D80F9D3EF300292CCF /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = System/Library/Frameworks/SecurityInterface.framework; sourceTree = SDKROOT; };
11.17 + 273B45790FA681EE00276298 /* MYBonjourRegistration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYBonjourRegistration.h; sourceTree = "<group>"; };
11.18 + 273B457A0FA681EE00276298 /* MYBonjourRegistration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYBonjourRegistration.m; sourceTree = "<group>"; };
11.19 274122DD0F9CDD1600F21842 /* MYUtilities_Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYUtilities_Debug.xcconfig; sourceTree = "<group>"; };
11.20 274122DE0F9CDD1600F21842 /* MYUtilities_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYUtilities_Release.xcconfig; sourceTree = "<group>"; };
11.21 2777C9100F7602A7007F8D30 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
11.22 @@ -182,8 +187,8 @@
11.23 2780F20B0FA194BD00C0FB83 /* MYDNSService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYDNSService.m; path = PortMapper/MYDNSService.m; sourceTree = "<group>"; };
11.24 2780F4360FA28F4400C0FB83 /* MYBonjourQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYBonjourQuery.h; sourceTree = "<group>"; };
11.25 2780F4370FA28F4400C0FB83 /* MYBonjourQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYBonjourQuery.m; sourceTree = "<group>"; };
11.26 - 2780F49F0FA2C59000C0FB83 /* MYAddressLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYAddressLookup.h; sourceTree = "<group>"; };
11.27 - 2780F4A00FA2C59000C0FB83 /* MYAddressLookup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYAddressLookup.m; sourceTree = "<group>"; };
11.28 + 2780F49F0FA2C59000C0FB83 /* MYAddressLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MYAddressLookup.h; path = Bonjour/MYAddressLookup.h; sourceTree = "<group>"; };
11.29 + 2780F4A00FA2C59000C0FB83 /* MYAddressLookup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MYAddressLookup.m; path = Bonjour/MYAddressLookup.m; sourceTree = "<group>"; };
11.30 278C1A340F9F687800954AE1 /* PortMapperTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PortMapperTest.m; sourceTree = "<group>"; };
11.31 278C1A350F9F687800954AE1 /* MYPortMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPortMapper.h; sourceTree = "<group>"; };
11.32 278C1A360F9F687800954AE1 /* MYPortMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPortMapper.m; sourceTree = "<group>"; };
11.33 @@ -389,6 +394,8 @@
11.34 270461020DE49030003D9D3F /* IPAddress.m */,
11.35 2780F20A0FA194BD00C0FB83 /* MYDNSService.h */,
11.36 2780F20B0FA194BD00C0FB83 /* MYDNSService.m */,
11.37 + 2780F49F0FA2C59000C0FB83 /* MYAddressLookup.h */,
11.38 + 2780F4A00FA2C59000C0FB83 /* MYAddressLookup.m */,
11.39 );
11.40 name = Addressing;
11.41 sourceTree = "<group>";
11.42 @@ -412,8 +419,8 @@
11.43 278C1BA10F9F92EA00954AE1 /* MYBonjourService.m */,
11.44 2780F4360FA28F4400C0FB83 /* MYBonjourQuery.h */,
11.45 2780F4370FA28F4400C0FB83 /* MYBonjourQuery.m */,
11.46 - 2780F49F0FA2C59000C0FB83 /* MYAddressLookup.h */,
11.47 - 2780F4A00FA2C59000C0FB83 /* MYAddressLookup.m */,
11.48 + 273B45790FA681EE00276298 /* MYBonjourRegistration.h */,
11.49 + 273B457A0FA681EE00276298 /* MYBonjourRegistration.m */,
11.50 );
11.51 path = Bonjour;
11.52 sourceTree = "<group>";
11.53 @@ -438,6 +445,7 @@
11.54 2780F20C0FA194BD00C0FB83 /* MYDNSService.h in Headers */,
11.55 2780F4380FA28F4400C0FB83 /* MYBonjourQuery.h in Headers */,
11.56 2780F4A10FA2C59000C0FB83 /* MYAddressLookup.h in Headers */,
11.57 + 273B457B0FA681EE00276298 /* MYBonjourRegistration.h in Headers */,
11.58 );
11.59 runOnlyForDeploymentPostprocessing = 0;
11.60 };
11.61 @@ -595,6 +603,7 @@
11.62 2780F20D0FA194BD00C0FB83 /* MYDNSService.m in Sources */,
11.63 2780F4390FA28F4400C0FB83 /* MYBonjourQuery.m in Sources */,
11.64 2780F4A20FA2C59000C0FB83 /* MYAddressLookup.m in Sources */,
11.65 + 273B457C0FA681EE00276298 /* MYBonjourRegistration.m in Sources */,
11.66 );
11.67 runOnlyForDeploymentPostprocessing = 0;
11.68 };
11.69 @@ -631,6 +640,7 @@
11.70 2780F20E0FA194BD00C0FB83 /* MYDNSService.m in Sources */,
11.71 2780F43A0FA28F4400C0FB83 /* MYBonjourQuery.m in Sources */,
11.72 2780F4A30FA2C59000C0FB83 /* MYAddressLookup.m in Sources */,
11.73 + 273B457D0FA681EE00276298 /* MYBonjourRegistration.m in Sources */,
11.74 );
11.75 runOnlyForDeploymentPostprocessing = 0;
11.76 };
12.1 --- a/PortMapper/MYDNSService.h Tue Apr 28 10:36:28 2009 -0700
12.2 +++ b/PortMapper/MYDNSService.h Wed Apr 29 13:29:31 2009 -0700
12.3 @@ -8,17 +8,20 @@
12.4
12.5 #import <Foundation/Foundation.h>
12.6 #import <CoreFoundation/CFSocket.h>
12.7 +@class MYDNSConnection;
12.8
12.9
12.10 /** Abstract superclass for services based on DNSServiceRefs, such as MYPortMapper. */
12.11 @interface MYDNSService : NSObject
12.12 {
12.13 @private
12.14 + BOOL _usePrivateConnection;
12.15 + MYDNSConnection *_connection;
12.16 struct _DNSServiceRef_t *_serviceRef;
12.17 CFSocketRef _socket;
12.18 CFRunLoopSourceRef _socketSource;
12.19 SInt32 _error;
12.20 - BOOL _continuous;
12.21 + BOOL _continuous, _gotResponse;
12.22 }
12.23
12.24 /** If NO (the default), the service will stop after it gets a result.
12.25 @@ -40,12 +43,30 @@
12.26 This property is KV observable. */
12.27 @property int32_t error;
12.28
12.29 +
12.30 +/** Utility to construct a service's full name. */
12.31 ++ (NSString*) fullNameOfService: (NSString*)serviceName
12.32 + ofType: (NSString*)type
12.33 + inDomain: (NSString*)domain;
12.34 +
12.35 +
12.36 +
12.37 // PROTECTED:
12.38
12.39 +
12.40 +@property BOOL usePrivateConnection;
12.41 +
12.42 /** Subclass must implement this abstract method to create a new DNSServiceRef.
12.43 This method is called by -open.
12.44 - If an error occurs, the method should set self.error and return NULL.*/
12.45 -- (struct _DNSServiceRef_t*) createServiceRef;
12.46 + The implementation MUST pass the given sdRefPtr directly to the DNSService function
12.47 + that creates the new ref, without setting it to NULL first.
12.48 + It MUST also set the kDNSServiceFlagsShareConnection flag. */
12.49 +- (int32_t/*DNSServiceErrorType*/) createServiceRef: (struct _DNSServiceRef_t**)sdRefPtr;
12.50 +
12.51 +/** Subclass's callback must call this method after doing its own work.
12.52 + This method will update the error state, and will stop the service if it's not set to be
12.53 + continuous. */
12.54 +- (void) gotResponse: (int32_t/*DNSServiceErrorType*/)errorCode;
12.55
12.56 @property (readonly) struct _DNSServiceRef_t* serviceRef;
12.57
12.58 @@ -58,4 +79,23 @@
12.59 @return YES if a message is received, NO on error (or if the service isn't started) */
12.60 - (BOOL) waitForReply;
12.61
12.62 +
12.63 @end
12.64 +
12.65 +
12.66 +
12.67 +
12.68 +@interface MYDNSConnection : NSObject
12.69 +{
12.70 + struct _DNSServiceRef_t* _connectionRef;
12.71 + CFSocketRef _socket;
12.72 + CFRunLoopSourceRef _runLoopSource;
12.73 +}
12.74 +
12.75 ++ (MYDNSConnection*) sharedConnection;
12.76 +- (id) initWithServiceRef: (struct _DNSServiceRef_t *)serviceRef;
12.77 +@property (readonly) struct _DNSServiceRef_t* connectionRef;
12.78 +- (BOOL) processResult;
12.79 +- (void) close;
12.80 +
12.81 +@end
13.1 --- a/PortMapper/MYDNSService.m Tue Apr 28 10:36:28 2009 -0700
13.2 +++ b/PortMapper/MYDNSService.m Wed Apr 29 13:29:31 2009 -0700
13.3 @@ -20,7 +20,7 @@
13.4 CFDataRef address,
13.5 const void *data,
13.6 void *clientCallBackInfo);
13.7 -
13.8 +
13.9
13.10 @implementation MYDNSService
13.11
13.12 @@ -52,14 +52,25 @@
13.13 }
13.14
13.15
13.16 -@synthesize continuous=_continuous, serviceRef=_serviceRef;
13.17 +@synthesize continuous=_continuous, serviceRef=_serviceRef, usePrivateConnection=_usePrivateConnection;
13.18
13.19
13.20 -- (DNSServiceRef) createServiceRef {
13.21 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
13.22 AssertAbstractMethod();
13.23 }
13.24
13.25
13.26 +- (void) gotResponse: (DNSServiceErrorType)errorCode {
13.27 + _gotResponse = YES;
13.28 + if (!_continuous)
13.29 + [self cancel];
13.30 + if (errorCode && errorCode != _error) {
13.31 + Log(@"%@ got error %i", self,errorCode);
13.32 + self.error = errorCode;
13.33 + }
13.34 +}
13.35 +
13.36 +
13.37 - (BOOL) start
13.38 {
13.39 if (_serviceRef)
13.40 @@ -67,55 +78,60 @@
13.41
13.42 if (_error)
13.43 self.error = 0;
13.44 + _gotResponse = NO;
13.45
13.46 + if (!_usePrivateConnection) {
13.47 + _connection = [[MYDNSConnection sharedConnection] retain];
13.48 + if (!_connection) {
13.49 + self.error = kDNSServiceErr_Unknown;
13.50 + return NO;
13.51 + }
13.52 + _serviceRef = _connection.connectionRef;
13.53 + }
13.54 +
13.55 // Ask the subclass to create a DNSServiceRef:
13.56 - _serviceRef = [self createServiceRef];
13.57 + _error = [self createServiceRef: &_serviceRef];
13.58 + if (_error) {
13.59 + _serviceRef = NULL;
13.60 + setObj(&_connection,nil);
13.61 + if (!_error)
13.62 + self.error = kDNSServiceErr_Unknown;
13.63 + LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
13.64 + return NO;
13.65 + }
13.66
13.67 - if (_serviceRef) {
13.68 - // Wrap a CFSocket around the service's socket:
13.69 - CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
13.70 - _socket = CFSocketCreateWithNative(NULL,
13.71 - DNSServiceRefSockFD(_serviceRef),
13.72 - kCFSocketReadCallBack,
13.73 - &serviceCallback, &ctxt);
13.74 - if( _socket ) {
13.75 - CFSocketSetSocketFlags(_socket, CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
13.76 - // Attach the socket to the runloop so the serviceCallback will be invoked:
13.77 - _socketSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
13.78 - if( _socketSource ) {
13.79 - CFRunLoopAddSource(CFRunLoopGetCurrent(), _socketSource, kCFRunLoopCommonModes);
13.80 - LogTo(DNS,@"Opening %@ -- service=%p",self,_serviceRef);
13.81 - return YES; // success
13.82 - }
13.83 - }
13.84 - }
13.85 - if (!_error)
13.86 - self.error = kDNSServiceErr_Unknown;
13.87 - LogTo(DNS,@"Failed to open %@ -- err=%i",self,_error);
13.88 - [self cancel];
13.89 - return NO;
13.90 + if (!_connection)
13.91 + _connection = [[MYDNSConnection alloc] initWithServiceRef: _serviceRef];
13.92 +
13.93 + LogTo(DNS,@"Started %@",self);
13.94 + return YES; // Succeeded
13.95 +}
13.96 +
13.97 +
13.98 +- (BOOL) waitForReply {
13.99 + if( ! _serviceRef )
13.100 + if( ! [self start] )
13.101 + return NO;
13.102 + // Run the runloop until there's either an error or a result:
13.103 + _gotResponse = NO;
13.104 + LogTo(DNS,@"Waiting for reply to %@...", self);
13.105 + while( !_gotResponse )
13.106 + if( ! [_connection processResult] )
13.107 + break;
13.108 + LogTo(DNS,@" ...got reply");
13.109 + return (self.error==0);
13.110 }
13.111
13.112
13.113 - (void) cancel
13.114 {
13.115 - [self retain]; // Prevents _socket's dealloc from releasing & deallocing me!
13.116 - if( _socketSource ) {
13.117 - CFRunLoopSourceInvalidate(_socketSource);
13.118 - CFRelease(_socketSource);
13.119 - _socketSource = NULL;
13.120 - }
13.121 - if( _socket ) {
13.122 - CFSocketInvalidate(_socket);
13.123 - CFRelease(_socket);
13.124 - _socket = NULL;
13.125 - }
13.126 if( _serviceRef ) {
13.127 LogTo(DNS,@"Stopped %@",self);
13.128 DNSServiceRefDeallocate(_serviceRef);
13.129 _serviceRef = NULL;
13.130 +
13.131 + setObj(&_connection,nil);
13.132 }
13.133 - [self release];
13.134 }
13.135
13.136
13.137 @@ -127,31 +143,159 @@
13.138 }
13.139
13.140
13.141 -- (BOOL) priv_processResult
13.142 ++ (NSString*) fullNameOfService: (NSString*)serviceName
13.143 + ofType: (NSString*)type
13.144 + inDomain: (NSString*)domain
13.145 {
13.146 - Assert(_serviceRef);
13.147 - DNSServiceErrorType err = DNSServiceProcessResult(_serviceRef);
13.148 - if (err) {
13.149 - // An error here means the socket has failed and should be closed.
13.150 - self.error = err;
13.151 - [self cancel];
13.152 - return NO;
13.153 - } else {
13.154 - if (!_continuous)
13.155 - [self cancel];
13.156 - return YES;
13.157 + //FIX: Do I need to un-escape the serviceName?
13.158 + Assert(type);
13.159 + Assert(domain);
13.160 + char fullName[kDNSServiceMaxDomainName];
13.161 + if (DNSServiceConstructFullName(fullName, serviceName.UTF8String, type.UTF8String, domain.UTF8String) == 0)
13.162 + return [NSString stringWithUTF8String: fullName];
13.163 + else
13.164 + return nil;
13.165 +}
13.166 +
13.167 +
13.168 +@end
13.169 +
13.170 +
13.171 +#pragma mark -
13.172 +#pragma mark SHARED CONNECTION:
13.173 +
13.174 +
13.175 +@interface MYDNSConnection ()
13.176 +- (BOOL) open;
13.177 +@end
13.178 +
13.179 +
13.180 +@implementation MYDNSConnection
13.181 +
13.182 +
13.183 +MYDNSConnection *sSharedConnection;
13.184 +
13.185 +
13.186 +- (id) init
13.187 +{
13.188 + DNSServiceRef connectionRef = NULL;
13.189 + DNSServiceErrorType err = DNSServiceCreateConnection(&connectionRef);
13.190 + if (err || !connectionRef) {
13.191 + Warn(@"MYDNSConnection: DNSServiceCreateConnection failed, err=%i", err);
13.192 + [self release];
13.193 + return nil;
13.194 + }
13.195 + return [self initWithServiceRef: connectionRef];
13.196 +}
13.197 +
13.198 +
13.199 +- (id) initWithServiceRef: (DNSServiceRef)serviceRef
13.200 +{
13.201 + Assert(serviceRef);
13.202 + self = [super init];
13.203 + if (self != nil) {
13.204 + _connectionRef = serviceRef;
13.205 + LogTo(DNS,@"INIT %@", self);
13.206 + if (![self open]) {
13.207 + [self release];
13.208 + return nil;
13.209 + }
13.210 + }
13.211 + return self;
13.212 +}
13.213 +
13.214 +
13.215 ++ (MYDNSConnection*) sharedConnection {
13.216 + @synchronized(self) {
13.217 + if (!sSharedConnection)
13.218 + sSharedConnection = [[[self alloc] init] autorelease];
13.219 + }
13.220 + return sSharedConnection;
13.221 +}
13.222 +
13.223 +
13.224 +- (void) dealloc
13.225 +{
13.226 + LogTo(DNS,@"DEALLOC %@", self);
13.227 + [self close];
13.228 + [super dealloc];
13.229 +}
13.230 +
13.231 +- (void) finalize {
13.232 + [self close];
13.233 + [super finalize];
13.234 +}
13.235 +
13.236 +
13.237 +@synthesize connectionRef=_connectionRef;
13.238 +
13.239 +- (NSString*) description {
13.240 + return $sprintf(@"%@[conn=%p]", self.class,_connectionRef);
13.241 +}
13.242 +
13.243 +- (BOOL) open {
13.244 + if (_runLoopSource)
13.245 + return YES; // Already opened
13.246 +
13.247 + // Wrap a CFSocket around the service's socket:
13.248 + CFSocketContext ctxt = { 0, self, CFRetain, CFRelease, NULL };
13.249 + _socket = CFSocketCreateWithNative(NULL,
13.250 + DNSServiceRefSockFD(_connectionRef),
13.251 + kCFSocketReadCallBack,
13.252 + &serviceCallback, &ctxt);
13.253 + if( _socket ) {
13.254 + CFSocketSetSocketFlags(_socket,
13.255 + CFSocketGetSocketFlags(_socket) & ~kCFSocketCloseOnInvalidate);
13.256 + // Attach the socket to the runloop so the serviceCallback will be invoked:
13.257 + _runLoopSource = CFSocketCreateRunLoopSource(NULL, _socket, 0);
13.258 + if( _runLoopSource ) {
13.259 + CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
13.260 + // Success!
13.261 + LogTo(DNS,@"Successfully opened %@", self);
13.262 + return YES;
13.263 + }
13.264 + }
13.265 +
13.266 + // Failure:
13.267 + Warn(@"Failed to connect %@ to runloop", self);
13.268 + [self close];
13.269 + return NO;
13.270 +}
13.271 +
13.272 +
13.273 +- (void) close {
13.274 + @synchronized(self) {
13.275 + if( _runLoopSource ) {
13.276 + CFRunLoopSourceInvalidate(_runLoopSource);
13.277 + CFRelease(_runLoopSource);
13.278 + _runLoopSource = NULL;
13.279 + }
13.280 + if( _socket ) {
13.281 + CFSocketInvalidate(_socket);
13.282 + CFRelease(_socket);
13.283 + _socket = NULL;
13.284 + }
13.285 + if( _connectionRef ) {
13.286 + LogTo(DNS,@"Closed %@",self);
13.287 + DNSServiceRefDeallocate(_connectionRef);
13.288 + _connectionRef = NULL;
13.289 + }
13.290 +
13.291 + if (self==sSharedConnection)
13.292 + sSharedConnection = nil;
13.293 }
13.294 }
13.295
13.296 -- (BOOL) waitForReply
13.297 -{
13.298 - if (!_serviceRef)
13.299 - return NO;
13.300 - LogTo(DNS,@"Waiting for %@ ...", self);
13.301 - BOOL ok = [self priv_processResult];
13.302 - LogTo(DNS,@" ...done waiting");
13.303 - return ok;
13.304 -}
13.305 +
13.306 +- (BOOL) processResult {
13.307 + NSAutoreleasePool *pool = [NSAutoreleasePool new];
13.308 + LogTo(DNS,@"---serviceCallback----");
13.309 + DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef);
13.310 + if (err)
13.311 + Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err);
13.312 + [pool drain];
13.313 + return !err;
13.314 +}
13.315
13.316
13.317 /** CFSocket callback, informing us that _socket has data available, which means
13.318 @@ -161,11 +305,8 @@
13.319 CFSocketCallBackType type,
13.320 CFDataRef address, const void *data, void *clientCallBackInfo)
13.321 {
13.322 - NSAutoreleasePool *pool = [NSAutoreleasePool new];
13.323 - @try{
13.324 - [(MYDNSService*)clientCallBackInfo priv_processResult];
13.325 - }catchAndReport(@"PortMapper serviceCallback");
13.326 - [pool drain];
13.327 + MYDNSConnection *connection = clientCallBackInfo;
13.328 + [connection processResult];
13.329 }
13.330
13.331
14.1 --- a/PortMapper/MYPortMapper.m Tue Apr 28 10:36:28 2009 -0700
14.2 +++ b/PortMapper/MYPortMapper.m Wed Apr 29 13:29:31 2009 -0700
14.3 @@ -94,8 +94,6 @@
14.4 errorCode = kDNSServiceErr_NATPortMappingUnsupported;
14.5 }
14.6 }
14.7 - if( errorCode != self.error )
14.8 - self.error = errorCode;
14.9
14.10 [self priv_updateLocalAddress];
14.11 IPAddress *publicAddress = makeIPAddr(rawPublicAddress,publicPort);
14.12 @@ -106,6 +104,8 @@
14.13 LogTo(PortMapper,@"%@: Public addr is %@ (mapped=%i)",
14.14 self, self.publicAddress, self.isMapped);
14.15 }
14.16 +
14.17 + [self gotResponse: errorCode];
14.18 [[NSNotificationCenter defaultCenter] postNotificationName: MYPortMapperChangedNotification
14.19 object: self];
14.20 }
14.21 @@ -135,22 +135,19 @@
14.22 }
14.23
14.24
14.25 -- (DNSServiceRef) createServiceRef
14.26 -{
14.27 +- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
14.28 DNSServiceProtocol protocols = 0;
14.29 if( _mapTCP ) protocols |= kDNSServiceProtocol_TCP;
14.30 if( _mapUDP ) protocols |= kDNSServiceProtocol_UDP;
14.31 - DNSServiceRef serviceRef = NULL;
14.32 - self.error = DNSServiceNATPortMappingCreate(&serviceRef,
14.33 - 0 /*flags*/,
14.34 - 0 /*interfaceIndex*/,
14.35 - protocols,
14.36 - htons(_localPort),
14.37 - htons(_desiredPublicPort),
14.38 - 0 /*ttl*/,
14.39 - &portMapCallback,
14.40 - self);
14.41 - return serviceRef;
14.42 + return DNSServiceNATPortMappingCreate(sdRefPtr,
14.43 + kDNSServiceFlagsShareConnection,
14.44 + 0 /*interfaceIndex*/,
14.45 + protocols,
14.46 + htons(_localPort),
14.47 + htons(_desiredPublicPort),
14.48 + 0 /*ttl*/,
14.49 + &portMapCallback,
14.50 + self);
14.51 }
14.52
14.53