5 // Created by Jens Alfke on 1/4/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
15 #import <sys/socket.h>
17 #import <netinet/in.h>
22 @implementation IPAddress
25 + (UInt32) IPv4FromDottedQuadString: (NSString*)str
29 NSScanner *scanner = [NSScanner scannerWithString: str];
30 for( int i=0; i<4; i++ ) {
31 if( i>0 && ! [scanner scanString: @"." intoString: nil] )
34 if( ! [scanner scanInteger: &octet] || octet<0 || octet>255 )
36 ipv4 = (ipv4<<8) | octet;
38 if( ! [scanner isAtEnd] )
44 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
49 _ipv4 = [[self class] IPv4FromDottedQuadString: hostname];
52 return [[HostAddress alloc] initWithHostname: hostname port: port];
60 - (id) initWithIPv4: (UInt32)ipv4 port: (UInt16)port
70 - (id) initWithIPv4: (UInt32)ipv4
72 return [self initWithIPv4: ipv4 port: 0];
75 - (id) initWithSockAddr: (const struct sockaddr*)sockaddr
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)];
86 + (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
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];
97 - (id) copyWithZone: (NSZone*)zone
102 - (void)encodeWithCoder:(NSCoder *)coder
105 [coder encodeInt32: _ipv4 forKey: @"ipv4"];
107 [coder encodeInt: _port forKey: @"port"];
110 - (id)initWithCoder:(NSCoder *)decoder
114 _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
115 _port = [decoder decodeIntForKey: @"port"];
121 @synthesize ipv4=_ipv4, port=_port;
123 - (BOOL) isEqual: (IPAddress*)addr
125 return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
128 - (BOOL) isSameHost: (IPAddress*)addr
130 return addr && _ipv4==addr->_ipv4;
135 return _ipv4 ^ _port;
138 - (NSString*) ipv4name
140 UInt32 ipv4 = self.ipv4;
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]];
149 - (NSString*) hostname
151 return [self ipv4name];
154 - (NSString*) description
156 NSString *name = self.hostname ?: @"0.0.0.0";
158 name = [name stringByAppendingFormat: @":%hu",_port];
163 + (IPAddress*) localAddress
165 // getifaddrs returns a linked list of interface entries;
166 // find the first active non-loopback interface with IPv4:
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;
180 freeifaddrs(interfaces);
182 return [[[self alloc] initWithIPv4: address] autorelease];
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)
200 UInt32 address = ntohl(self.ipv4);
202 for( i=0; kPrivateRanges[i].mask; i++ )
203 if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
215 @implementation HostAddress
218 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
220 self = [super initWithIPv4: 0 port: port];
222 if( [hostname length]==0 ) {
226 _hostname = [hostname copy];
232 - (void)encodeWithCoder:(NSCoder *)coder
234 [super encodeWithCoder: coder];
235 [coder encodeObject: _hostname forKey: @"host"];
238 - (id)initWithCoder:(NSCoder *)decoder
240 self = [super initWithCoder: decoder];
242 _hostname = [[decoder decodeObjectForKey: @"host"] copy];
256 return [_hostname hash] ^ _port;
260 - (NSString*) hostname {return _hostname;}
265 struct hostent *ent = gethostbyname(_hostname.UTF8String);
267 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
270 return * (const in_addr_t*) ent->h_addr_list[0];
274 - (BOOL) isSameHost: (IPAddress*)addr
276 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
285 @implementation RecentAddress
288 - (id) initWithIPAddress: (IPAddress*)addr
290 return [super initWithIPv4: addr.ipv4 port: addr.port];
294 @synthesize lastSuccess=_lastSuccess, successes=_successes;
298 if( _successes < 0xFFFF )
300 _lastSuccess = CFAbsoluteTimeGetCurrent();
306 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
307 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
313 - (void)encodeWithCoder:(NSCoder *)coder
315 [super encodeWithCoder: coder];
316 [coder encodeDouble: _lastSuccess forKey: @"last"];
317 [coder encodeInt: _successes forKey: @"succ"];
320 - (id)initWithCoder:(NSCoder *)decoder
322 self = [super initWithCoder: decoder];
324 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
325 _successes = [decoder decodeIntForKey: @"succ"];
339 TestCase(IPAddress) {
340 RequireTestCase(CollectionUtils);
341 IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
342 CAssertEq(addr.ipv4,htonl(0x0A0001FE));
343 CAssertEq(addr.port,8080);
344 CAssertEqual(addr.hostname,@"10.0.1.254");
345 CAssertEqual(addr.description,@"10.0.1.254:8080");
346 CAssert(addr.isPrivate);
348 addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
349 CAssertEq(addr.class,[IPAddress class]);
350 CAssertEq(addr.ipv4,htonl(0x424200FF));
351 CAssertEq(addr.port,123);
352 CAssertEqual(addr.hostname,@"66.66.0.255");
353 CAssertEqual(addr.description,@"66.66.0.255:123");
354 CAssert(!addr.isPrivate);
356 addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
357 CAssertEq(addr.class,[HostAddress class]);
358 Log(@"www.apple.com = 0x%08X", addr.ipv4);
359 CAssertEq(addr.ipv4,htonl(0x1195A00A));
360 CAssertEq(addr.port,80);
361 CAssertEqual(addr.hostname,@"www.apple.com");
362 CAssertEqual(addr.description,@"www.apple.com:80");
363 CAssert(!addr.isPrivate);
368 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
370 Redistribution and use in source and binary forms, with or without modification, are permitted
371 provided that the following conditions are met:
373 * Redistributions of source code must retain the above copyright notice, this list of conditions
374 and the following disclaimer.
375 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
376 and the following disclaimer in the documentation and/or other materials provided with the
379 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
380 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
381 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
382 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
383 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
384 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
385 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
386 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.