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