Bonjour/MYAddressLookup.m
author Jens Alfke <jens@mooseyard.com>
Mon Jul 20 13:26:29 2009 -0700 (2009-07-20)
changeset 59 46c7844cb592
parent 46 50dc5502ef46
permissions -rw-r--r--
* 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.
     1 //
     2 //  MYAddressLookup.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 4/24/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYAddressLookup.h"
    10 #import "MYBonjourService.h"
    11 #import "IPAddress.h"
    12 #import "ExceptionUtils.h"
    13 #import "Test.h"
    14 #import "Logging.h"
    15 #import <dns_sd.h>
    16 
    17 
    18 @interface MYAddressLookup ()
    19 @property (copy) NSString *hostname;
    20 @end
    21 
    22 
    23 @implementation MYAddressLookup
    24 
    25 - (id) initWithHostname: (NSString*)hostname
    26 {
    27     self = [super init];
    28     if (self != nil) {
    29         if (!hostname) {
    30             [self release];
    31             return nil;
    32         }
    33         _hostname = [hostname copy];
    34         _addresses = [[NSMutableSet alloc] init];
    35     }
    36     return self;
    37 }
    38 
    39 
    40 - (id) _initWithBonjourService: (MYBonjourService*)service {
    41     self = [super init];
    42     if (self) {
    43         _service = [service retain];
    44         _addresses = [[NSMutableSet alloc] init];
    45     }
    46     return self;
    47 }
    48 
    49 - (void) dealloc
    50 {
    51     [_hostname release];
    52     [_addresses release];
    53     [_service release];
    54     [super dealloc];
    55 }
    56 
    57 
    58 - (NSString*) description
    59 {
    60     return $sprintf(@"%@[%@]", self.class,_hostname);
    61 }
    62 
    63 
    64 @synthesize hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex, addresses=_addresses;
    65 
    66 
    67 - (NSTimeInterval) timeToLive {
    68     return MAX(0.0, _expires - CFAbsoluteTimeGetCurrent());
    69 }
    70 
    71 
    72 - (BOOL) start {
    73     if (_hostname) {
    74         return [super start];
    75     } else {
    76         // Service doesn't know its hostname yet; wait till it does:
    77         LogTo(DNS,@"MYAddressLookup requesting hostname of %@ ...", _service);
    78         Assert(_service);
    79         return [_service start];
    80     }
    81 }
    82 
    83 
    84 // Called by my _service's gotResponse method:
    85 - (void) _serviceGotResponse {
    86     Assert(_service);
    87     DNSServiceErrorType err = _service.error;
    88     if (err) {
    89         [self cancel];
    90         self.error = err;
    91     } else {
    92         NSString *hostname = _service.hostname;
    93         UInt16 port = _service.port;
    94         if (port!=_port || !$equal(hostname,_hostname)) {
    95             self.hostname = hostname;
    96             self.port = port;
    97             if (_hostname)
    98                 [self start];
    99         }
   100     }
   101 }
   102 
   103 
   104 - (void) priv_resolvedAddress: (const struct sockaddr*)sockaddr
   105                           ttl: (uint32_t)ttl
   106                         flags: (DNSServiceFlags)flags
   107 {
   108     HostAddress *address = [[HostAddress alloc] initWithHostname: _hostname 
   109                                                         sockaddr: sockaddr
   110                                                             port: _port];
   111     if (address) {
   112         if (flags & kDNSServiceFlagsAdd) {
   113             LogTo(DNS,@"%@ got %@ [TTL = %u]", self, address, ttl);
   114             kvAddToSet(self, @"addresses", _addresses, address);
   115         } else {
   116             LogTo(DNS,@"%@ lost %@ [TTL = %u]", self, address, ttl);
   117             kvRemoveFromSet(self, @"addresses", _addresses, address);
   118         }
   119         [address release];
   120     }
   121     
   122     _expires = CFAbsoluteTimeGetCurrent() + ttl;
   123 }
   124 
   125 
   126 static void lookupCallback(DNSServiceRef                    sdRef,
   127                            DNSServiceFlags                  flags,
   128                            uint32_t                         interfaceIndex,
   129                            DNSServiceErrorType              errorCode,
   130                            const char                       *hostname,
   131                            const struct sockaddr            *address,
   132                            uint32_t                         ttl,
   133                            void                             *context)
   134 {
   135     MYAddressLookup *lookup = context;
   136     @try{
   137         LogTo(DNS, @"lookupCallback for %s (err=%i)", hostname,errorCode);
   138         if (errorCode)
   139             [lookup setError: errorCode];
   140         else
   141             [lookup priv_resolvedAddress: address ttl: ttl flags: flags];
   142     }catchAndReport(@"MYDNSLookup query callback");
   143     [lookup gotResponse: errorCode];
   144 }
   145 
   146 
   147 - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
   148     Assert(_hostname);
   149     kvSetSet(self, @"addresses", _addresses, nil);
   150     return DNSServiceGetAddrInfo(sdRefPtr,
   151                                  kDNSServiceFlagsShareConnection,
   152                                  _interfaceIndex, 
   153                                  kDNSServiceProtocol_IPv4,
   154                                  _hostname.UTF8String,
   155                                  &lookupCallback, self);
   156 }
   157 
   158 
   159 @end
   160 
   161 
   162 
   163 TestCase(MYDNSLookup) {
   164     EnableLogTo(Bonjour,YES);
   165     EnableLogTo(DNS,YES);
   166     [NSRunLoop currentRunLoop]; // create runloop
   167 
   168     MYAddressLookup *lookup = [[MYAddressLookup alloc] initWithHostname: @"www.apple.com" port: 80];
   169     [lookup start];
   170     
   171     [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 10]];
   172     [lookup release];
   173 }    
   174 
   175 
   176 /*
   177  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   178  
   179  Redistribution and use in source and binary forms, with or without modification, are permitted
   180  provided that the following conditions are met:
   181  
   182  * Redistributions of source code must retain the above copyright notice, this list of conditions
   183  and the following disclaimer.
   184  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   185  and the following disclaimer in the documentation and/or other materials provided with the
   186  distribution.
   187  
   188  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   189  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   190  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   191  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   192  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   193   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   194  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   195  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   196  */