IPAddress.m
author Jens Alfke <jens@mooseyard.com>
Sun May 25 12:32:47 2008 -0700 (2008-05-25)
changeset 5 2c4bb6968927
parent 0 9d67172bb323
child 24 ab88e3ee5726
permissions -rw-r--r--
More documentation.
     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*) localAddress
   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] autorelease];
   183 }
   184 
   185 
   186 // Private IP address ranges. See RFC 3330.
   187 static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
   188     {0xFF000000, 0x00000000},       //   0.x.x.x (hosts on "this" network)
   189     {0xFF000000, 0x0A000000},       //  10.x.x.x (private address range)
   190     {0xFF000000, 0x7F000000},       // 127.x.x.x (loopback)
   191     {0xFFFF0000, 0xA9FE0000},       // 169.254.x.x (link-local self-configured addresses)
   192     {0xFFF00000, 0xAC100000},       // 172.(16-31).x.x (private address range)
   193     {0xFFFF0000, 0xC0A80000},       // 192.168.x.x (private address range)
   194     {0,0}
   195 };
   196 
   197 
   198 - (BOOL) isPrivate
   199 {
   200     UInt32 address = ntohl(self.ipv4);
   201     int i;
   202     for( i=0; kPrivateRanges[i].mask; i++ )
   203         if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
   204             return YES;
   205     return NO;
   206 }
   207 
   208 
   209 @end
   210 
   211 
   212 
   213 
   214 
   215 @implementation HostAddress
   216 
   217 
   218 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
   219 {
   220     self = [super initWithIPv4: 0 port: port];
   221     if( self ) {
   222         if( [hostname length]==0 ) {
   223             [self release];
   224             return nil;
   225         }
   226         _hostname = [hostname copy];
   227     }
   228     return self;
   229 }
   230 
   231 
   232 - (void)encodeWithCoder:(NSCoder *)coder
   233 {
   234     [super encodeWithCoder: coder];
   235     [coder encodeObject: _hostname forKey: @"host"];
   236 }
   237 
   238 - (id)initWithCoder:(NSCoder *)decoder
   239 {
   240     self = [super initWithCoder: decoder];
   241     if( self ) {
   242         _hostname = [[decoder decodeObjectForKey: @"host"] copy];
   243     }
   244     return self;
   245 }
   246 
   247 - (NSUInteger) hash
   248 {
   249     return [_hostname hash] ^ _port;
   250 }
   251 
   252 
   253 - (NSString*) hostname  {return _hostname;}
   254 
   255 
   256 - (UInt32) ipv4
   257 {
   258     struct hostent *ent = gethostbyname(_hostname.UTF8String);
   259     if( ! ent ) {
   260         Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
   261         return 0;
   262     }
   263     return * (const in_addr_t*) ent->h_addr_list[0];
   264 }
   265 
   266 
   267 - (BOOL) isSameHost: (IPAddress*)addr
   268 {
   269     return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
   270 }
   271 
   272 
   273 @end
   274 
   275 
   276 
   277 
   278 @implementation RecentAddress
   279 
   280 
   281 - (id) initWithIPAddress: (IPAddress*)addr
   282 {
   283     return [super initWithIPv4: addr.ipv4 port: addr.port];
   284 }
   285 
   286 
   287 @synthesize lastSuccess=_lastSuccess, successes=_successes;
   288 
   289 - (BOOL) noteSuccess
   290 {
   291     if( _successes < 0xFFFF )
   292         _successes++;
   293     _lastSuccess = CFAbsoluteTimeGetCurrent();
   294     return YES;
   295 }
   296 
   297 - (BOOL) noteSeen
   298 {
   299     CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
   300     BOOL significant = ( now-_lastSuccess >= 18*60*60 );
   301     _lastSuccess = now;
   302     return significant;
   303 }
   304 
   305 
   306 - (void)encodeWithCoder:(NSCoder *)coder
   307 {
   308     [super encodeWithCoder: coder];
   309     [coder encodeDouble: _lastSuccess forKey: @"last"];
   310     [coder encodeInt: _successes forKey: @"succ"];
   311 }
   312 
   313 - (id)initWithCoder:(NSCoder *)decoder
   314 {
   315     self = [super initWithCoder: decoder];
   316     if( self ) {
   317         _lastSuccess = [decoder decodeDoubleForKey: @"last"];
   318         _successes = [decoder decodeIntForKey: @"succ"];
   319     }
   320     return self;
   321 }
   322 
   323 
   324 @end
   325 
   326 
   327 
   328 
   329 
   330 #import "Test.h"
   331 
   332 TestCase(IPAddress) {
   333     RequireTestCase(CollectionUtils);
   334     IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
   335     CAssertEq(addr.ipv4,htonl(0x0A0001FE));
   336     CAssertEq(addr.port,8080);
   337     CAssertEqual(addr.hostname,@"10.0.1.254");
   338     CAssertEqual(addr.description,@"10.0.1.254:8080");
   339     CAssert(addr.isPrivate);
   340     
   341     addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
   342     CAssertEq(addr.class,[IPAddress class]);
   343     CAssertEq(addr.ipv4,htonl(0x424200FF));
   344     CAssertEq(addr.port,123);
   345     CAssertEqual(addr.hostname,@"66.66.0.255");
   346     CAssertEqual(addr.description,@"66.66.0.255:123");
   347     CAssert(!addr.isPrivate);
   348     
   349     addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
   350     CAssertEq(addr.class,[HostAddress class]);
   351     Log(@"www.apple.com = 0x%08X", addr.ipv4);
   352     CAssertEq(addr.ipv4,htonl(0x1195A00A));
   353     CAssertEq(addr.port,80);
   354     CAssertEqual(addr.hostname,@"www.apple.com");
   355     CAssertEqual(addr.description,@"www.apple.com:80");
   356     CAssert(!addr.isPrivate);
   357 }
   358 
   359 
   360 /*
   361  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   362  
   363  Redistribution and use in source and binary forms, with or without modification, are permitted
   364  provided that the following conditions are met:
   365  
   366  * Redistributions of source code must retain the above copyright notice, this list of conditions
   367  and the following disclaimer.
   368  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   369  and the following disclaimer in the documentation and/or other materials provided with the
   370  distribution.
   371  
   372  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   373  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   374  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   375  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   376  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   377   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   378  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   379  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   380  */