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