IPAddress.m
author Jens Alfke <jens@mooseyard.com>
Mon Jul 20 13:26:29 2009 -0700 (2009-07-20)
changeset 59 46c7844cb592
parent 49 20cccc7c26ee
child 60 dd637bdd214e
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.
jens@0
     1
//
jens@0
     2
//  IPAddress.m
jens@0
     3
//  MYNetwork
jens@0
     4
//
jens@0
     5
//  Created by Jens Alfke on 1/4/08.
jens@0
     6
//  Copyright 2008 Jens Alfke. All rights reserved.
jens@0
     7
//
jens@0
     8
jens@0
     9
#import "IPAddress.h"
jens@1
    10
jens@1
    11
#import "Logging.h"
jens@1
    12
#import "Test.h"
jens@1
    13
jens@0
    14
#import <sys/types.h>
jens@0
    15
#import <sys/socket.h>
jens@0
    16
#import <net/if.h>
jens@0
    17
#import <netinet/in.h>
jens@0
    18
#import <ifaddrs.h>
jens@1
    19
#import <netdb.h>
jens@0
    20
jens@0
    21
jens@0
    22
@implementation IPAddress
jens@0
    23
jens@0
    24
jens@0
    25
+ (UInt32) IPv4FromDottedQuadString: (NSString*)str
jens@0
    26
{
jens@0
    27
    Assert(str);
jens@0
    28
    UInt32 ipv4 = 0;
jens@0
    29
    NSScanner *scanner = [NSScanner scannerWithString: str];
jens@0
    30
    for( int i=0; i<4; i++ ) {
jens@0
    31
        if( i>0 && ! [scanner scanString: @"." intoString: nil] )
jens@0
    32
            return 0;
jens@0
    33
        NSInteger octet;
jens@0
    34
        if( ! [scanner scanInteger: &octet] || octet<0 || octet>255 )
jens@0
    35
            return 0;
jens@0
    36
        ipv4 = (ipv4<<8) | octet;
jens@0
    37
    }
jens@0
    38
    if( ! [scanner isAtEnd] )
jens@0
    39
        return 0;
jens@0
    40
    return htonl(ipv4);
jens@0
    41
}
jens@0
    42
         
jens@0
    43
         
jens@0
    44
- (id) initWithHostname: (NSString*)hostname port: (UInt16)port
jens@0
    45
{
jens@0
    46
    Assert(hostname);
jens@0
    47
    self = [super init];
jens@0
    48
    if (self != nil) {
jens@0
    49
        _ipv4 = [[self class] IPv4FromDottedQuadString: hostname];
jens@0
    50
        if( ! _ipv4 ) {
jens@0
    51
            [self release];
jens@0
    52
            return [[HostAddress alloc] initWithHostname: hostname port: port];
jens@0
    53
        }
jens@0
    54
        _port = port;
jens@0
    55
    }
jens@0
    56
    return self;
jens@0
    57
}
jens@0
    58
jens@49
    59
+ (IPAddress*) addressWithHostname: (NSString*)hostname port: (UInt16)port
jens@49
    60
{
jens@49
    61
    return [[[self alloc] initWithHostname: hostname port: port] autorelease];
jens@49
    62
}
jens@49
    63
jens@0
    64
jens@0
    65
- (id) initWithIPv4: (UInt32)ipv4 port: (UInt16)port
jens@0
    66
{
jens@0
    67
    self = [super init];
jens@0
    68
    if (self != nil) {
jens@0
    69
        _ipv4 = ipv4;
jens@0
    70
        _port = port;
jens@0
    71
    }
jens@0
    72
    return self;
jens@0
    73
}
jens@0
    74
jens@0
    75
- (id) initWithIPv4: (UInt32)ipv4
jens@0
    76
{
jens@0
    77
    return [self initWithIPv4: ipv4 port: 0];
jens@0
    78
}
jens@0
    79
jens@0
    80
- (id) initWithSockAddr: (const struct sockaddr*)sockaddr
jens@0
    81
{
jens@0
    82
    if( sockaddr->sa_family == AF_INET ) {
jens@0
    83
        const struct sockaddr_in *addr_in = (const struct sockaddr_in*)sockaddr;
jens@0
    84
        return [self initWithIPv4: addr_in->sin_addr.s_addr port: ntohs(addr_in->sin_port)];
jens@0
    85
    } else {
jens@0
    86
        [self release];
jens@0
    87
        return nil;
jens@0
    88
    }
jens@0
    89
}
jens@0
    90
jens@32
    91
- (id) initWithSockAddr: (const struct sockaddr*)sockaddr
jens@32
    92
                   port: (UInt16)port
jens@32
    93
{
jens@32
    94
    self = [self initWithSockAddr: sockaddr];
jens@32
    95
    if (self)
jens@32
    96
        _port = port;
jens@32
    97
    return self;
jens@32
    98
}
jens@32
    99
jens@59
   100
- (id) initWithData: (NSData*)data
jens@59
   101
{
jens@59
   102
    const struct sockaddr* addr = data.bytes;
jens@59
   103
    if (data.length < sizeof(struct sockaddr_in))
jens@59
   104
        addr = nil;
jens@59
   105
    return [self initWithSockAddr: addr];
jens@59
   106
}
jens@59
   107
jens@59
   108
jens@0
   109
+ (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
jens@0
   110
{
jens@0
   111
    uint8_t name[SOCK_MAXADDRLEN];
jens@0
   112
    socklen_t namelen = sizeof(name);
jens@0
   113
    struct sockaddr *addr = (struct sockaddr*)name;
jens@0
   114
    if (0 == getpeername(socket, addr, &namelen))
jens@0
   115
        return [[[self alloc] initWithSockAddr: addr] autorelease];
jens@0
   116
    else
jens@0
   117
        return nil;
jens@0
   118
}    
jens@0
   119
jens@0
   120
- (id) copyWithZone: (NSZone*)zone
jens@0
   121
{
jens@0
   122
    return [self retain];
jens@0
   123
}
jens@0
   124
jens@0
   125
- (void)encodeWithCoder:(NSCoder *)coder
jens@0
   126
{
jens@0
   127
    if( _ipv4 )
jens@0
   128
        [coder encodeInt32: _ipv4 forKey: @"ipv4"];
jens@0
   129
    if( _port )
jens@0
   130
        [coder encodeInt: _port forKey: @"port"];
jens@0
   131
}
jens@0
   132
jens@0
   133
- (id)initWithCoder:(NSCoder *)decoder
jens@0
   134
{
jens@0
   135
    self = [super init];
jens@0
   136
    if( self ) {
jens@0
   137
        _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
jens@0
   138
        _port = [decoder decodeIntForKey: @"port"];
jens@0
   139
    }
jens@0
   140
    return self;
jens@0
   141
}
jens@0
   142
jens@0
   143
jens@0
   144
@synthesize ipv4=_ipv4, port=_port;
jens@0
   145
jens@0
   146
- (BOOL) isEqual: (IPAddress*)addr
jens@0
   147
{
jens@0
   148
    return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
jens@0
   149
}
jens@0
   150
jens@0
   151
- (BOOL) isSameHost: (IPAddress*)addr
jens@0
   152
{
jens@0
   153
    return addr && _ipv4==addr->_ipv4;
jens@0
   154
}
jens@0
   155
jens@0
   156
- (NSUInteger) hash
jens@0
   157
{
jens@0
   158
    return _ipv4 ^ _port;
jens@0
   159
}
jens@0
   160
jens@0
   161
- (NSString*) ipv4name
jens@0
   162
{
jens@0
   163
    UInt32 ipv4 = self.ipv4;
jens@0
   164
    if( ipv4 != 0 ) {
jens@0
   165
        const UInt8* b = (const UInt8*)&ipv4;
jens@0
   166
        return [NSString stringWithFormat: @"%u.%u.%u.%u",
jens@0
   167
                (unsigned)b[0],(unsigned)b[1],(unsigned)b[2],(unsigned)b[3]];
jens@0
   168
    } else
jens@0
   169
        return nil;
jens@0
   170
}
jens@0
   171
jens@0
   172
- (NSString*) hostname
jens@0
   173
{
jens@0
   174
    return [self ipv4name];
jens@0
   175
}
jens@0
   176
jens@59
   177
- (NSData*) asData
jens@59
   178
{
jens@59
   179
    struct sockaddr_in addr = {
jens@59
   180
        .sin_len    = sizeof(struct sockaddr_in),
jens@59
   181
        .sin_family = AF_INET,
jens@59
   182
        .sin_port   = htons(_port),
jens@59
   183
        .sin_addr   = {htonl(_ipv4)} };
jens@59
   184
    return [NSData dataWithBytes: &addr length: sizeof(addr)];
jens@59
   185
}
jens@59
   186
jens@0
   187
- (NSString*) description
jens@0
   188
{
jens@0
   189
    NSString *name = self.hostname ?: @"0.0.0.0";
jens@0
   190
    if( _port )
jens@0
   191
        name = [name stringByAppendingFormat: @":%hu",_port];
jens@0
   192
    return name;
jens@0
   193
}
jens@0
   194
jens@0
   195
jens@26
   196
+ (IPAddress*) localAddressWithPort: (UInt16)port
jens@0
   197
{
jens@0
   198
    // getifaddrs returns a linked list of interface entries;
jens@0
   199
    // find the first active non-loopback interface with IPv4:
jens@0
   200
    UInt32 address = 0;
jens@0
   201
    struct ifaddrs *interfaces;
jens@0
   202
    if( getifaddrs(&interfaces) == 0 ) {
jens@0
   203
        struct ifaddrs *interface;
jens@0
   204
        for( interface=interfaces; interface; interface=interface->ifa_next ) {
jens@0
   205
            if( (interface->ifa_flags & IFF_UP) && ! (interface->ifa_flags & IFF_LOOPBACK) ) {
jens@0
   206
                const struct sockaddr_in *addr = (const struct sockaddr_in*) interface->ifa_addr;
jens@0
   207
                if( addr && addr->sin_family==AF_INET ) {
jens@0
   208
                    address = addr->sin_addr.s_addr;
jens@0
   209
                    break;
jens@0
   210
                }
jens@0
   211
            }
jens@0
   212
        }
jens@0
   213
        freeifaddrs(interfaces);
jens@0
   214
    }
jens@26
   215
    return [[[self alloc] initWithIPv4: address port: port] autorelease];
jens@26
   216
}
jens@26
   217
jens@26
   218
+ (IPAddress*) localAddress
jens@26
   219
{
jens@26
   220
    return [self localAddressWithPort: 0];
jens@0
   221
}
jens@0
   222
jens@0
   223
jens@0
   224
// Private IP address ranges. See RFC 3330.
jens@0
   225
static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
jens@0
   226
    {0xFF000000, 0x00000000},       //   0.x.x.x (hosts on "this" network)
jens@0
   227
    {0xFF000000, 0x0A000000},       //  10.x.x.x (private address range)
jens@0
   228
    {0xFF000000, 0x7F000000},       // 127.x.x.x (loopback)
jens@0
   229
    {0xFFFF0000, 0xA9FE0000},       // 169.254.x.x (link-local self-configured addresses)
jens@0
   230
    {0xFFF00000, 0xAC100000},       // 172.(16-31).x.x (private address range)
jens@0
   231
    {0xFFFF0000, 0xC0A80000},       // 192.168.x.x (private address range)
jens@0
   232
    {0,0}
jens@0
   233
};
jens@0
   234
jens@0
   235
jens@0
   236
- (BOOL) isPrivate
jens@0
   237
{
jens@0
   238
    UInt32 address = ntohl(self.ipv4);
jens@0
   239
    int i;
jens@0
   240
    for( i=0; kPrivateRanges[i].mask; i++ )
jens@0
   241
        if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
jens@0
   242
            return YES;
jens@0
   243
    return NO;
jens@0
   244
}
jens@0
   245
jens@0
   246
jens@0
   247
@end
jens@0
   248
jens@0
   249
jens@0
   250
jens@0
   251
jens@0
   252
jens@0
   253
@implementation HostAddress
jens@0
   254
jens@0
   255
jens@0
   256
- (id) initWithHostname: (NSString*)hostname port: (UInt16)port
jens@0
   257
{
jens@0
   258
    self = [super initWithIPv4: 0 port: port];
jens@0
   259
    if( self ) {
jens@0
   260
        if( [hostname length]==0 ) {
jens@0
   261
            [self release];
jens@0
   262
            return nil;
jens@0
   263
        }
jens@0
   264
        _hostname = [hostname copy];
jens@0
   265
    }
jens@0
   266
    return self;
jens@0
   267
}
jens@0
   268
jens@28
   269
- (id) initWithHostname: (NSString*)hostname
jens@28
   270
               sockaddr: (const struct sockaddr*)sockaddr
jens@28
   271
                   port: (UInt16)port;
jens@28
   272
{
jens@28
   273
    if( [hostname length]==0 ) {
jens@28
   274
        [self release];
jens@28
   275
        return nil;
jens@28
   276
    }
jens@32
   277
    self = [super initWithSockAddr: sockaddr port: port];
jens@28
   278
    if( self ) {
jens@28
   279
        _hostname = [hostname copy];
jens@28
   280
    }
jens@28
   281
    return self;
jens@28
   282
}    
jens@28
   283
jens@0
   284
jens@0
   285
- (void)encodeWithCoder:(NSCoder *)coder
jens@0
   286
{
jens@0
   287
    [super encodeWithCoder: coder];
jens@0
   288
    [coder encodeObject: _hostname forKey: @"host"];
jens@0
   289
}
jens@0
   290
jens@0
   291
- (id)initWithCoder:(NSCoder *)decoder
jens@0
   292
{
jens@0
   293
    self = [super initWithCoder: decoder];
jens@0
   294
    if( self ) {
jens@0
   295
        _hostname = [[decoder decodeObjectForKey: @"host"] copy];
jens@0
   296
    }
jens@0
   297
    return self;
jens@0
   298
}
jens@0
   299
jens@24
   300
- (void)dealloc 
jens@24
   301
{
jens@24
   302
    [_hostname release];
jens@24
   303
    [super dealloc];
jens@24
   304
}
jens@24
   305
jens@24
   306
jens@28
   307
- (NSString*) description
jens@28
   308
{
danpreston@35
   309
    NSMutableString *desc = [[_hostname mutableCopy] autorelease];
jens@28
   310
    NSString *addr = self.ipv4name;
jens@28
   311
    if (addr)
jens@28
   312
        [desc appendFormat: @"(%@)", addr];
jens@32
   313
    if( self.port )
jens@32
   314
        [desc appendFormat: @":%hu",self.port];
jens@28
   315
    return desc;
jens@28
   316
}
jens@28
   317
jens@28
   318
jens@0
   319
- (NSUInteger) hash
jens@0
   320
{
jens@32
   321
    return [_hostname hash] ^ self.port;
jens@0
   322
}
jens@0
   323
jens@0
   324
jens@0
   325
- (NSString*) hostname  {return _hostname;}
jens@0
   326
jens@0
   327
jens@0
   328
- (UInt32) ipv4
jens@0
   329
{
jens@0
   330
    struct hostent *ent = gethostbyname(_hostname.UTF8String);
jens@0
   331
    if( ! ent ) {
jens@0
   332
        Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
jens@0
   333
        return 0;
jens@0
   334
    }
jens@0
   335
    return * (const in_addr_t*) ent->h_addr_list[0];
jens@0
   336
}
jens@0
   337
jens@0
   338
jens@0
   339
- (BOOL) isSameHost: (IPAddress*)addr
jens@0
   340
{
jens@0
   341
    return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
jens@0
   342
}
jens@0
   343
jens@0
   344
jens@0
   345
@end
jens@0
   346
jens@0
   347
jens@0
   348
jens@0
   349
jens@0
   350
@implementation RecentAddress
jens@0
   351
jens@0
   352
jens@0
   353
- (id) initWithIPAddress: (IPAddress*)addr
jens@0
   354
{
jens@0
   355
    return [super initWithIPv4: addr.ipv4 port: addr.port];
jens@0
   356
}
jens@0
   357
jens@0
   358
jens@0
   359
@synthesize lastSuccess=_lastSuccess, successes=_successes;
jens@0
   360
jens@0
   361
- (BOOL) noteSuccess
jens@0
   362
{
jens@0
   363
    if( _successes < 0xFFFF )
jens@0
   364
        _successes++;
jens@0
   365
    _lastSuccess = CFAbsoluteTimeGetCurrent();
jens@0
   366
    return YES;
jens@0
   367
}
jens@0
   368
jens@0
   369
- (BOOL) noteSeen
jens@0
   370
{
jens@0
   371
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
jens@0
   372
    BOOL significant = ( now-_lastSuccess >= 18*60*60 );
jens@0
   373
    _lastSuccess = now;
jens@0
   374
    return significant;
jens@0
   375
}
jens@0
   376
jens@0
   377
jens@0
   378
- (void)encodeWithCoder:(NSCoder *)coder
jens@0
   379
{
jens@0
   380
    [super encodeWithCoder: coder];
jens@0
   381
    [coder encodeDouble: _lastSuccess forKey: @"last"];
jens@0
   382
    [coder encodeInt: _successes forKey: @"succ"];
jens@0
   383
}
jens@0
   384
jens@0
   385
- (id)initWithCoder:(NSCoder *)decoder
jens@0
   386
{
jens@0
   387
    self = [super initWithCoder: decoder];
jens@0
   388
    if( self ) {
jens@0
   389
        _lastSuccess = [decoder decodeDoubleForKey: @"last"];
jens@0
   390
        _successes = [decoder decodeIntForKey: @"succ"];
jens@0
   391
    }
jens@0
   392
    return self;
jens@0
   393
}
jens@0
   394
jens@0
   395
jens@0
   396
@end
jens@0
   397
jens@0
   398
jens@0
   399
jens@0
   400
jens@0
   401
jens@0
   402
#import "Test.h"
jens@0
   403
jens@0
   404
TestCase(IPAddress) {
jens@0
   405
    RequireTestCase(CollectionUtils);
jens@0
   406
    IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
jens@26
   407
    CAssertEq(addr.ipv4,(UInt32)htonl(0x0A0001FE));
jens@0
   408
    CAssertEq(addr.port,8080);
jens@0
   409
    CAssertEqual(addr.hostname,@"10.0.1.254");
jens@0
   410
    CAssertEqual(addr.description,@"10.0.1.254:8080");
jens@0
   411
    CAssert(addr.isPrivate);
danpreston@35
   412
	[addr release];
jens@0
   413
    
jens@0
   414
    addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
jens@0
   415
    CAssertEq(addr.class,[IPAddress class]);
jens@26
   416
    CAssertEq(addr.ipv4,(UInt32)htonl(0x424200FF));
jens@0
   417
    CAssertEq(addr.port,123);
jens@0
   418
    CAssertEqual(addr.hostname,@"66.66.0.255");
jens@0
   419
    CAssertEqual(addr.description,@"66.66.0.255:123");
jens@0
   420
    CAssert(!addr.isPrivate);
danpreston@35
   421
 	[addr release];
danpreston@35
   422
   
jens@0
   423
    addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
jens@0
   424
    CAssertEq(addr.class,[HostAddress class]);
jens@26
   425
    Log(@"www.apple.com = %@ [0x%08X]", addr.ipv4name, ntohl(addr.ipv4));
jens@26
   426
    CAssertEq(addr.ipv4,(UInt32)htonl(0x11FBC820));
jens@0
   427
    CAssertEq(addr.port,80);
jens@0
   428
    CAssertEqual(addr.hostname,@"www.apple.com");
jens@0
   429
    CAssertEqual(addr.description,@"www.apple.com:80");
jens@0
   430
    CAssert(!addr.isPrivate);
danpreston@35
   431
	[addr release];
jens@0
   432
}
jens@0
   433
jens@0
   434
jens@0
   435
/*
jens@0
   436
 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@0
   437
 
jens@0
   438
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
   439
 provided that the following conditions are met:
jens@0
   440
 
jens@0
   441
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
   442
 and the following disclaimer.
jens@0
   443
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@0
   444
 and the following disclaimer in the documentation and/or other materials provided with the
jens@0
   445
 distribution.
jens@0
   446
 
jens@0
   447
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
   448
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
   449
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
   450
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
   451
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
   452
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
   453
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
   454
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
   455
 */