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