# HG changeset patch # User Jens Alfke # Date 1248121589 25200 # Node ID 46c7844cb59233501ee42306dcd0ecf87e3f674c # Parent 6577813acf125769d98e429b3e744fd0f0a559b2 * MYBonjourBrowser: Added delegate (no methods for it yet, just for client use.) * MYBonjourRegistration: Added +canonicalFormOfTXTRecordDictionary:. * MYBonjourService: Added back-reference to browser. * IPAddress: Added conversions to/from struct sockaddr. diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourBrowser.h --- a/Bonjour/MYBonjourBrowser.h Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourBrowser.h Mon Jul 20 13:26:29 2009 -0700 @@ -20,6 +20,7 @@ NSMutableSet *_services, *_addServices, *_rmvServices; BOOL _pendingUpdate; MYBonjourRegistration *_myRegistration; + id _delegate; } /** Initializes a new MYBonjourBrowser. @@ -27,6 +28,8 @@ @param serviceType The name of the service type to look for, e.g. "_http._tcp". */ - (id) initWithServiceType: (NSString*)serviceType; +@property (assign) id delegate; + /** Is the browser currently browsing? */ @property (readonly) BOOL browsing; diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourBrowser.m --- a/Bonjour/MYBonjourBrowser.m Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourBrowser.m Mon Jul 20 13:26:29 2009 -0700 @@ -62,7 +62,7 @@ } -@synthesize browsing=_browsing, services=_services, serviceClass=_serviceClass; +@synthesize delegate=_delegate, browsing=_browsing, services=_services, serviceClass=_serviceClass; - (NSString*) description @@ -92,10 +92,11 @@ flags: (DNSServiceFlags)flags { // Create (or reuse existing) MYBonjourService object: - MYBonjourService *service = [[_serviceClass alloc] initWithName: serviceName - type: regtype - domain: domain - interface: interfaceIndex]; + MYBonjourService *service = [[_serviceClass alloc] initWithBrowser: self + name: serviceName + type: regtype + domain: domain + interface: interfaceIndex]; if ([_myRegistration isSameAsService: service]) { // This is an echo of my own registration, so ignore it LogTo(Bonjour,@"%@ ignoring echo %@", self,service); diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourRegistration.h --- a/Bonjour/MYBonjourRegistration.h Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourRegistration.h Mon Jul 20 13:26:29 2009 -0700 @@ -45,9 +45,11 @@ /** The registration's full name -- the name, type and domain concatenated together. */ @property (readonly) NSString *fullName; + /** Is the registration currently active? */ @property (readonly) BOOL registered; + /** The service's metadata dictionary, stored in its DNS TXT record */ @property (copy) NSDictionary *txtRecord; @@ -76,6 +78,10 @@ /** Is this browsed service an echo of this local registration? (Compares fullNames.) */ - (BOOL) isSameAsService: (MYBonjourService*)service; +/** Converts a TXT record dictionary to data in a consistent way. + This is used when signing (and verifying signatures of) TXT records. */ ++ (NSData*) canonicalFormOfTXTRecordDictionary: (NSDictionary*)txtDict; + //@} @end diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourRegistration.m --- a/Bonjour/MYBonjourRegistration.m Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourRegistration.m Mon Jul 20 13:26:29 2009 -0700 @@ -165,6 +165,65 @@ } +static int compareData (id data1, id data2, void *context) { + size_t length1 = [data1 length], length2 = [data2 length]; + int result = memcmp([data1 bytes], [data2 bytes], MIN(length1,length2)); + if (result==0) { + if (length1>length2) + result = 1; + else if (length1 255) { + Warn(@"TXT dictionary key too long: %@", key); + return nil; + } + id value = [txtDict objectForKey: key]; + if (![value isKindOfClass: [NSData class]]) { + value = [[value description] dataUsingEncoding: NSUTF8StringEncoding]; + } + if ([value length] > 255) { + Warn(@"TXT dictionary value too long: %@", value); + return nil; + } + [dataDict setObject: value forKey: keyData]; + } + } + + // Add key/value pairs, sorted by increasing key: + NSMutableData *canonical = [NSMutableData dataWithCapacity: 1000]; + for (NSData *key in [[dataDict allKeys] sortedArrayUsingFunction: compareData context: NULL]) { + // Append key prefixed with length: + UInt8 length = [key length]; + [canonical appendBytes: &length length: sizeof(length)]; + [canonical appendData: key]; + // Append value prefixed with length: + NSData *value = [dataDict objectForKey: key]; + length = [value length]; + [canonical appendBytes: &length length: sizeof(length)]; + [canonical appendData: value]; + } + return canonical; +} + + - (void) updateTxtRecord { [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil]; if (self.serviceRef) { @@ -179,7 +238,7 @@ if (err) Warn(@"%@ failed to update TXT (err=%i)", self,err); else - LogTo(Bonjour,@"%@ updated TXT to %@", self,data); + LogTo(Bonjour,@"%@ updated TXT to %u bytes: %@", self,data.length,data); } } diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourService.h --- a/Bonjour/MYBonjourService.h Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourService.h Mon Jul 20 13:26:29 2009 -0700 @@ -7,13 +7,14 @@ // #import "MYDNSService.h" -@class MYBonjourQuery, MYAddressLookup; +@class MYBonjourBrowser, MYBonjourQuery, MYAddressLookup; /** Represents a Bonjour service discovered by a MYBonjourBrowser. */ @interface MYBonjourService : MYDNSService { @private + MYBonjourBrowser *_bonjourBrowser; NSString *_name, *_fullName, *_type, *_domain, *_hostname; uint32_t _interfaceIndex; BOOL _startedResolve; @@ -23,6 +24,9 @@ MYAddressLookup *_addressLookup; } +/** The browser I belong to. */ +@property (readonly) MYBonjourBrowser *bonjourBrowser; + /** The service's name. */ @property (readonly) NSString *name; @@ -81,10 +85,11 @@ /** Designated initializer. You probably don't want to create MYBonjourService instances yourself, but if you subclass you might need to override this initializer. */ -- (id) initWithName: (NSString*)serviceName - type: (NSString*)type - domain: (NSString*)domain - interface: (UInt32)interfaceIndex; +- (id) initWithBrowser: (MYBonjourBrowser*)browser + name: (NSString*)serviceName + type: (NSString*)type + domain: (NSString*)domain + interface: (UInt32)interfaceIndex; /** Called when this service is officially added to its browser's service set. You can override this, but be sure to call the superclass method. */ diff -r 6577813acf12 -r 46c7844cb592 Bonjour/MYBonjourService.m --- a/Bonjour/MYBonjourService.m Fri Jul 03 17:50:28 2009 -0700 +++ b/Bonjour/MYBonjourService.m Mon Jul 20 13:26:29 2009 -0700 @@ -29,16 +29,18 @@ @implementation MYBonjourService -- (id) initWithName: (NSString*)serviceName - type: (NSString*)type - domain: (NSString*)domain - interface: (UInt32)interfaceIndex +- (id) initWithBrowser: (MYBonjourBrowser*)browser + name: (NSString*)serviceName + type: (NSString*)type + domain: (NSString*)domain + interface: (UInt32)interfaceIndex { Assert(serviceName); Assert(type); Assert(domain); self = [super init]; if (self != nil) { + _bonjourBrowser = browser; _name = [serviceName copy]; _type = [type copy]; _domain = [domain copy]; @@ -62,8 +64,8 @@ } -@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, - hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex; +@synthesize bonjourBrowser=_bonjourBrowser, name=_name, type=_type, domain=_domain, + fullName=_fullName, hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex; - (NSString*) description { diff -r 6577813acf12 -r 46c7844cb592 IPAddress.h --- a/IPAddress.h Fri Jul 03 17:50:28 2009 -0700 +++ b/IPAddress.h Mon Jul 20 13:26:29 2009 -0700 @@ -42,6 +42,9 @@ /** Initializes an IPAddress from a BSD struct sockaddr. */ - (id) initWithSockAddr: (const struct sockaddr*)sockaddr; +/** Initializes an IPAddress from NSData containing a BSD struct sockaddr. */ +- (id) initWithData: (NSData*)data; + /** Returns the IP address of this host (plus the specified port number). If multiple network interfaces are active, the main one's address is returned. */ + (IPAddress*) localAddressWithPort: (UInt16)port; @@ -71,6 +74,9 @@ /** The port number, or zero if none was specified, in native byte order. */ @property (readonly) UInt16 port; +/** The address as an NSData object containing a struct sockaddr. */ +@property (readonly) NSData* asData; + /** Is this IP address in a designated private/local address range, such as 10.0.1.X? If so, the address is not globally meaningful outside of the local subnet. */ @property (readonly) BOOL isPrivate; // In a private/local addr range like 10.0.1.X? diff -r 6577813acf12 -r 46c7844cb592 IPAddress.m --- a/IPAddress.m Fri Jul 03 17:50:28 2009 -0700 +++ b/IPAddress.m Mon Jul 20 13:26:29 2009 -0700 @@ -97,6 +97,15 @@ return self; } +- (id) initWithData: (NSData*)data +{ + const struct sockaddr* addr = data.bytes; + if (data.length < sizeof(struct sockaddr_in)) + addr = nil; + return [self initWithSockAddr: addr]; +} + + + (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket { uint8_t name[SOCK_MAXADDRLEN]; @@ -165,6 +174,16 @@ return [self ipv4name]; } +- (NSData*) asData +{ + struct sockaddr_in addr = { + .sin_len = sizeof(struct sockaddr_in), + .sin_family = AF_INET, + .sin_port = htons(_port), + .sin_addr = {htonl(_ipv4)} }; + return [NSData dataWithBytes: &addr length: sizeof(addr)]; +} + - (NSString*) description { NSString *name = self.hostname ?: @"0.0.0.0";