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