IPAddress.m
author Jens Alfke <jens@mooseyard.com>
Tue Apr 28 10:36:28 2009 -0700 (2009-04-28)
changeset 29 59689fbdcf77
parent 26 cb9cdf247239
child 32 b3254a2f6d6c
permissions -rw-r--r--
Fixed two CF memory leaks. (Fixes issue #5)
     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 - (id) initWithHostname: (NSString*)hostname
   237                sockaddr: (const struct sockaddr*)sockaddr
   238                    port: (UInt16)port;
   239 {
   240     if( [hostname length]==0 ) {
   241         [self release];
   242         return nil;
   243     }
   244     self = [super initWithSockAddr: sockaddr];
   245     if( self ) {
   246         _hostname = [hostname copy];
   247         _port = port;
   248     }
   249     return self;
   250 }    
   251 
   252 
   253 - (void)encodeWithCoder:(NSCoder *)coder
   254 {
   255     [super encodeWithCoder: coder];
   256     [coder encodeObject: _hostname forKey: @"host"];
   257 }
   258 
   259 - (id)initWithCoder:(NSCoder *)decoder
   260 {
   261     self = [super initWithCoder: decoder];
   262     if( self ) {
   263         _hostname = [[decoder decodeObjectForKey: @"host"] copy];
   264     }
   265     return self;
   266 }
   267 
   268 - (void)dealloc 
   269 {
   270     [_hostname release];
   271     [super dealloc];
   272 }
   273 
   274 
   275 - (NSString*) description
   276 {
   277     NSMutableString *desc = [_hostname mutableCopy];
   278     NSString *addr = self.ipv4name;
   279     if (addr)
   280         [desc appendFormat: @"(%@)", addr];
   281     if( _port )
   282         [desc appendFormat: @":%hu",_port];
   283     return desc;
   284 }
   285 
   286 
   287 - (NSUInteger) hash
   288 {
   289     return [_hostname hash] ^ _port;
   290 }
   291 
   292 
   293 - (NSString*) hostname  {return _hostname;}
   294 
   295 
   296 - (UInt32) ipv4
   297 {
   298     struct hostent *ent = gethostbyname(_hostname.UTF8String);
   299     if( ! ent ) {
   300         Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
   301         return 0;
   302     }
   303     return * (const in_addr_t*) ent->h_addr_list[0];
   304 }
   305 
   306 
   307 - (BOOL) isSameHost: (IPAddress*)addr
   308 {
   309     return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
   310 }
   311 
   312 
   313 @end
   314 
   315 
   316 
   317 
   318 @implementation RecentAddress
   319 
   320 
   321 - (id) initWithIPAddress: (IPAddress*)addr
   322 {
   323     return [super initWithIPv4: addr.ipv4 port: addr.port];
   324 }
   325 
   326 
   327 @synthesize lastSuccess=_lastSuccess, successes=_successes;
   328 
   329 - (BOOL) noteSuccess
   330 {
   331     if( _successes < 0xFFFF )
   332         _successes++;
   333     _lastSuccess = CFAbsoluteTimeGetCurrent();
   334     return YES;
   335 }
   336 
   337 - (BOOL) noteSeen
   338 {
   339     CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
   340     BOOL significant = ( now-_lastSuccess >= 18*60*60 );
   341     _lastSuccess = now;
   342     return significant;
   343 }
   344 
   345 
   346 - (void)encodeWithCoder:(NSCoder *)coder
   347 {
   348     [super encodeWithCoder: coder];
   349     [coder encodeDouble: _lastSuccess forKey: @"last"];
   350     [coder encodeInt: _successes forKey: @"succ"];
   351 }
   352 
   353 - (id)initWithCoder:(NSCoder *)decoder
   354 {
   355     self = [super initWithCoder: decoder];
   356     if( self ) {
   357         _lastSuccess = [decoder decodeDoubleForKey: @"last"];
   358         _successes = [decoder decodeIntForKey: @"succ"];
   359     }
   360     return self;
   361 }
   362 
   363 
   364 @end
   365 
   366 
   367 
   368 
   369 
   370 #import "Test.h"
   371 
   372 TestCase(IPAddress) {
   373     RequireTestCase(CollectionUtils);
   374     IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
   375     CAssertEq(addr.ipv4,(UInt32)htonl(0x0A0001FE));
   376     CAssertEq(addr.port,8080);
   377     CAssertEqual(addr.hostname,@"10.0.1.254");
   378     CAssertEqual(addr.description,@"10.0.1.254:8080");
   379     CAssert(addr.isPrivate);
   380     
   381     addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
   382     CAssertEq(addr.class,[IPAddress class]);
   383     CAssertEq(addr.ipv4,(UInt32)htonl(0x424200FF));
   384     CAssertEq(addr.port,123);
   385     CAssertEqual(addr.hostname,@"66.66.0.255");
   386     CAssertEqual(addr.description,@"66.66.0.255:123");
   387     CAssert(!addr.isPrivate);
   388     
   389     addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
   390     CAssertEq(addr.class,[HostAddress class]);
   391     Log(@"www.apple.com = %@ [0x%08X]", addr.ipv4name, ntohl(addr.ipv4));
   392     CAssertEq(addr.ipv4,(UInt32)htonl(0x11FBC820));
   393     CAssertEq(addr.port,80);
   394     CAssertEqual(addr.hostname,@"www.apple.com");
   395     CAssertEqual(addr.description,@"www.apple.com:80");
   396     CAssert(!addr.isPrivate);
   397 }
   398 
   399 
   400 /*
   401  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   402  
   403  Redistribution and use in source and binary forms, with or without modification, are permitted
   404  provided that the following conditions are met:
   405  
   406  * Redistributions of source code must retain the above copyright notice, this list of conditions
   407  and the following disclaimer.
   408  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   409  and the following disclaimer in the documentation and/or other materials provided with the
   410  distribution.
   411  
   412  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   413  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   414  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   415  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   416  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   417   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   418  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   419  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   420  */