Bonjour/MYBonjourBrowser.m
changeset 28 732576fa8a0d
parent 26 cb9cdf247239
child 31 1d6924779df7
     1.1 --- a/Bonjour/MYBonjourBrowser.m	Wed Apr 22 16:45:39 2009 -0700
     1.2 +++ b/Bonjour/MYBonjourBrowser.m	Mon Apr 27 09:03:56 2009 -0700
     1.3 @@ -8,13 +8,24 @@
     1.4  
     1.5  #import "MYBonjourBrowser.h"
     1.6  #import "MYBonjourService.h"
     1.7 +#import "ExceptionUtils.h"
     1.8  #import "Test.h"
     1.9  #import "Logging.h"
    1.10 +#import <dns_sd.h>
    1.11  
    1.12  
    1.13 +static void browseCallback (DNSServiceRef                       sdRef,
    1.14 +                            DNSServiceFlags                     flags,
    1.15 +                            uint32_t                            interfaceIndex,
    1.16 +                            DNSServiceErrorType                 errorCode,
    1.17 +                            const char                          *serviceName,
    1.18 +                            const char                          *regtype,
    1.19 +                            const char                          *replyDomain,
    1.20 +                            void                                *context);
    1.21 +
    1.22  @interface MYBonjourBrowser ()
    1.23  @property BOOL browsing;
    1.24 -@property (retain) NSError* error;
    1.25 +- (void) _updateServiceList;
    1.26  @end
    1.27  
    1.28  
    1.29 @@ -26,9 +37,8 @@
    1.30      Assert(serviceType);
    1.31      self = [super init];
    1.32      if (self != nil) {
    1.33 +        self.continuous = YES;
    1.34          _serviceType = [serviceType copy];
    1.35 -        _browser = [[NSNetServiceBrowser alloc] init];
    1.36 -        _browser.delegate = self;
    1.37          _services = [[NSMutableSet alloc] init];
    1.38          _addServices = [[NSMutableSet alloc] init];
    1.39          _rmvServices = [[NSMutableSet alloc] init];
    1.40 @@ -41,11 +51,7 @@
    1.41  - (void) dealloc
    1.42  {
    1.43      LogTo(Bonjour,@"DEALLOC BonjourBrowser");
    1.44 -    [_browser stop];
    1.45 -    _browser.delegate = nil;
    1.46 -    [_browser release];
    1.47      [_serviceType release];
    1.48 -    [_error release];
    1.49      [_services release];
    1.50      [_addServices release];
    1.51      [_rmvServices release];
    1.52 @@ -53,40 +59,64 @@
    1.53  }
    1.54  
    1.55  
    1.56 -@synthesize browsing=_browsing, error=_error, services=_services, serviceClass=_serviceClass;
    1.57 +@synthesize browsing=_browsing, services=_services, serviceClass=_serviceClass;
    1.58  
    1.59  
    1.60 -- (void) start
    1.61 +- (NSString*) description
    1.62  {
    1.63 -    [_browser searchForServicesOfType: _serviceType inDomain: @"local."];
    1.64 +    return $sprintf(@"%@[%@]", self.class,_serviceType);
    1.65  }
    1.66  
    1.67 -- (void) stop
    1.68 -{
    1.69 -    [_browser stop];
    1.70 +
    1.71 +- (DNSServiceRef) createServiceRef {
    1.72 +    DNSServiceRef serviceRef = NULL;
    1.73 +    self.error = DNSServiceBrowse(&serviceRef, 0, 0,
    1.74 +                                  _serviceType.UTF8String, NULL,
    1.75 +                                  &browseCallback, self);
    1.76 +    return serviceRef;
    1.77  }
    1.78  
    1.79  
    1.80 -- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)netServiceBrowser
    1.81 -{
    1.82 -    LogTo(Bonjour,@"%@ started browsing",self);
    1.83 -    self.browsing = YES;
    1.84 +- (void) priv_gotError: (DNSServiceErrorType)errorCode {
    1.85 +    LogTo(Bonjour,@"%@ got error %i", self,errorCode);
    1.86 +    self.error = errorCode;
    1.87  }
    1.88  
    1.89 -- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)netServiceBrowser
    1.90 +- (void) priv_gotServiceName: (NSString*)serviceName
    1.91 +                        type: (NSString*)regtype
    1.92 +                      domain: (NSString*)domain
    1.93 +                   interface: (uint32_t)interfaceIndex
    1.94 +                       flags: (DNSServiceFlags)flags
    1.95  {
    1.96 -    LogTo(Bonjour,@"%@ stopped browsing",self);
    1.97 -    self.browsing = NO;
    1.98 -}
    1.99 -
   1.100 -- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser 
   1.101 -             didNotSearch:(NSDictionary *)errorDict
   1.102 -{
   1.103 -    NSString *domain = [errorDict objectForKey: NSNetServicesErrorDomain];
   1.104 -    int err = [[errorDict objectForKey: NSNetServicesErrorCode] intValue];
   1.105 -    self.error = [NSError errorWithDomain: domain code: err userInfo: nil];
   1.106 -    LogTo(Bonjour,@"%@ got error: ",self,self.error);
   1.107 -    self.browsing = NO;
   1.108 +    // Create (or reuse existing) MYBonjourService object:
   1.109 +    MYBonjourService *service = [[_serviceClass alloc] initWithName: serviceName
   1.110 +                                                               type: regtype
   1.111 +                                                             domain: domain
   1.112 +                                                          interface: interfaceIndex];
   1.113 +    MYBonjourService *existingService = [_services member: service];
   1.114 +    if( existingService ) {
   1.115 +        [service release];
   1.116 +        service = [existingService retain];
   1.117 +    }
   1.118 +    
   1.119 +    // Add it to the add/remove sets:
   1.120 +    NSMutableSet *addTo, *removeFrom;
   1.121 +    if (flags & kDNSServiceFlagsAdd) {
   1.122 +        addTo = _addServices;
   1.123 +        removeFrom = _rmvServices;
   1.124 +    } else {
   1.125 +        addTo = _rmvServices;
   1.126 +        removeFrom = _addServices;
   1.127 +    }
   1.128 +    if( [removeFrom containsObject: service] )
   1.129 +        [removeFrom removeObject: service];
   1.130 +    else
   1.131 +        [addTo addObject: service];
   1.132 +    [service release];
   1.133 +    
   1.134 +    // After a round of updates is done, do the update:
   1.135 +    if( ! (flags & kDNSServiceFlagsMoreComing) )
   1.136 +        [self _updateServiceList];
   1.137  }
   1.138  
   1.139  
   1.140 @@ -117,42 +147,26 @@
   1.141  }
   1.142  
   1.143  
   1.144 -- (void) _handleService: (NSNetService*)netService 
   1.145 -                  addTo: (NSMutableSet*)addTo
   1.146 -             removeFrom: (NSMutableSet*)removeFrom
   1.147 -             moreComing: (BOOL)moreComing
   1.148 +static void browseCallback (DNSServiceRef                       sdRef,
   1.149 +                            DNSServiceFlags                     flags,
   1.150 +                            uint32_t                            interfaceIndex,
   1.151 +                            DNSServiceErrorType                 errorCode,
   1.152 +                            const char                          *serviceName,
   1.153 +                            const char                          *regtype,
   1.154 +                            const char                          *replyDomain,
   1.155 +                            void                                *context)
   1.156  {
   1.157 -    // Wrap the NSNetService in a BonjourService, using an existing instance if possible:
   1.158 -    MYBonjourService *service = [[_serviceClass alloc] initWithNetService: netService];
   1.159 -    MYBonjourService *existingService = [_services member: service];
   1.160 -    if( existingService ) {
   1.161 -        [service release];
   1.162 -        service = [existingService retain];
   1.163 -    }
   1.164 -    
   1.165 -    if( [removeFrom containsObject: service] )
   1.166 -        [removeFrom removeObject: service];
   1.167 -    else
   1.168 -        [addTo addObject: service];
   1.169 -    [service release];
   1.170 -    if( ! moreComing )
   1.171 -        [self _updateServiceList];
   1.172 -}
   1.173 -
   1.174 -- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser 
   1.175 -           didFindService:(NSNetService *)netService
   1.176 -               moreComing:(BOOL)moreComing 
   1.177 -{
   1.178 -    //LogTo(Bonjour,@"Add service %@",netService);
   1.179 -    [self _handleService: netService addTo: _addServices removeFrom: _rmvServices moreComing: moreComing];
   1.180 -}
   1.181 -
   1.182 -- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser 
   1.183 -         didRemoveService:(NSNetService *)netService 
   1.184 -               moreComing:(BOOL)moreComing 
   1.185 -{
   1.186 -    //LogTo(Bonjour,@"Remove service %@",netService);
   1.187 -    [self _handleService: netService addTo: _rmvServices removeFrom: _addServices moreComing: moreComing];
   1.188 +    @try{
   1.189 +        //LogTo(Bonjour,@"browseCallback (error=%i, name='%s')", errorCode,serviceName);
   1.190 +        if (errorCode)
   1.191 +            [(MYBonjourBrowser*)context priv_gotError: errorCode];
   1.192 +        else
   1.193 +            [(MYBonjourBrowser*)context priv_gotServiceName: [NSString stringWithUTF8String: serviceName]
   1.194 +                                                       type: [NSString stringWithUTF8String: regtype]
   1.195 +                                                     domain: [NSString stringWithUTF8String: replyDomain]
   1.196 +                                                  interface: interfaceIndex
   1.197 +                                                      flags: flags];
   1.198 +    }catchAndReport(@"Bonjour");
   1.199  }
   1.200  
   1.201  
   1.202 @@ -163,6 +177,11 @@
   1.203  #pragma mark -
   1.204  #pragma mark TESTING:
   1.205  
   1.206 +#if DEBUG
   1.207 +
   1.208 +#import "MYBonjourQuery.h"
   1.209 +#import "MYAddressLookup.h"
   1.210 +
   1.211  @interface BonjourTester : NSObject
   1.212  {
   1.213      MYBonjourBrowser *_browser;
   1.214 @@ -175,7 +194,7 @@
   1.215  {
   1.216      self = [super init];
   1.217      if (self != nil) {
   1.218 -        _browser = [[MYBonjourBrowser alloc] initWithServiceType: @"_http._tcp"];
   1.219 +        _browser = [[MYBonjourBrowser alloc] initWithServiceType: @"_presence._tcp"];
   1.220          [_browser addObserver: self forKeyPath: @"services" options: NSKeyValueObservingOptionNew context: NULL];
   1.221          [_browser addObserver: self forKeyPath: @"browsing" options: NSKeyValueObservingOptionNew context: NULL];
   1.222          [_browser start];
   1.223 @@ -199,7 +218,11 @@
   1.224          if( [[change objectForKey: NSKeyValueChangeKindKey] intValue]==NSKeyValueChangeInsertion ) {
   1.225              NSSet *newServices = [change objectForKey: NSKeyValueChangeNewKey];
   1.226              for( MYBonjourService *service in newServices ) {
   1.227 -                LogTo(Bonjour,@"    --> %@ : TXT=%@", service,service.txtRecord);
   1.228 +                NSString *hostname = service.hostname;  // block till it's resolved
   1.229 +                Log(@"##### %@ : at %@:%hu, TXT=%@", 
   1.230 +                      service, hostname, service.port, service.txtRecord);
   1.231 +                service.addressLookup.continuous = YES;
   1.232 +                [service queryForRecord: kDNSServiceType_NULL];
   1.233              }
   1.234          }
   1.235      }
   1.236 @@ -208,12 +231,15 @@
   1.237  @end
   1.238  
   1.239  TestCase(Bonjour) {
   1.240 +    EnableLogTo(Bonjour,YES);
   1.241 +    EnableLogTo(DNS,YES);
   1.242      [NSRunLoop currentRunLoop]; // create runloop
   1.243      BonjourTester *tester = [[BonjourTester alloc] init];
   1.244 -    [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 15]];
   1.245 +    [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1500]];
   1.246      [tester release];
   1.247  }
   1.248  
   1.249 +#endif
   1.250  
   1.251  
   1.252  /*