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];
59 + (IPAddress*) addressWithHostname: (NSString*)hostname port: (UInt16)port
61 return [[[self alloc] initWithHostname: hostname port: port] autorelease];
65 - (id) initWithIPv4: (UInt32)ipv4 port: (UInt16)port
75 - (id) initWithIPv4: (UInt32)ipv4
77 return [self initWithIPv4: ipv4 port: 0];
80 - (id) initWithSockAddr: (const struct sockaddr*)sockaddr
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)];
91 - (id) initWithSockAddr: (const struct sockaddr*)sockaddr
94 self = [self initWithSockAddr: sockaddr];
100 - (id) initWithData: (NSData*)data
106 const struct sockaddr* addr = data.bytes;
107 if (data.length < sizeof(struct sockaddr_in))
109 return [self initWithSockAddr: addr];
113 + (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
115 uint8_t name[SOCK_MAXADDRLEN];
116 socklen_t namelen = sizeof(name);
117 struct sockaddr *addr = (struct sockaddr*)name;
118 if (0 == getpeername(socket, addr, &namelen))
119 return [[[self alloc] initWithSockAddr: addr] autorelease];
124 - (id) copyWithZone: (NSZone*)zone
126 return [self retain];
129 - (void)encodeWithCoder:(NSCoder *)coder
132 [coder encodeInt32: _ipv4 forKey: @"ipv4"];
134 [coder encodeInt: _port forKey: @"port"];
137 - (id)initWithCoder:(NSCoder *)decoder
141 _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
142 _port = [decoder decodeIntForKey: @"port"];
148 @synthesize ipv4=_ipv4, port=_port;
150 - (BOOL) isEqual: (IPAddress*)addr
152 return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
155 - (BOOL) isSameHost: (IPAddress*)addr
157 return addr && _ipv4==addr->_ipv4;
162 return _ipv4 ^ _port;
165 - (NSString*) ipv4name
167 UInt32 ipv4 = self.ipv4;
169 const UInt8* b = (const UInt8*)&ipv4;
170 return [NSString stringWithFormat: @"%u.%u.%u.%u",
171 (unsigned)b[0],(unsigned)b[1],(unsigned)b[2],(unsigned)b[3]];
176 - (NSString*) hostname
178 return [self ipv4name];
183 struct sockaddr_in addr = {
184 .sin_len = sizeof(struct sockaddr_in),
185 .sin_family = AF_INET,
186 .sin_port = htons(_port),
187 .sin_addr = {htonl(_ipv4)} };
188 return [NSData dataWithBytes: &addr length: sizeof(addr)];
191 - (NSString*) description
193 NSString *name = self.hostname ?: @"0.0.0.0";
195 name = [name stringByAppendingFormat: @":%hu",_port];
200 + (IPAddress*) localAddressWithPort: (UInt16)port
202 // getifaddrs returns a linked list of interface entries;
203 // find the first active non-loopback interface with IPv4:
205 struct ifaddrs *interfaces;
206 if( getifaddrs(&interfaces) == 0 ) {
207 struct ifaddrs *interface;
208 for( interface=interfaces; interface; interface=interface->ifa_next ) {
209 if( (interface->ifa_flags & IFF_UP) && ! (interface->ifa_flags & IFF_LOOPBACK) ) {
210 const struct sockaddr_in *addr = (const struct sockaddr_in*) interface->ifa_addr;
211 if( addr && addr->sin_family==AF_INET ) {
212 address = addr->sin_addr.s_addr;
217 freeifaddrs(interfaces);
219 return [[[self alloc] initWithIPv4: address port: port] autorelease];
222 + (IPAddress*) localAddress
224 return [self localAddressWithPort: 0];
228 // Private IP address ranges. See RFC 3330.
229 static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
230 {0xFF000000, 0x00000000}, // 0.x.x.x (hosts on "this" network)
231 {0xFF000000, 0x0A000000}, // 10.x.x.x (private address range)
232 {0xFF000000, 0x7F000000}, // 127.x.x.x (loopback)
233 {0xFFFF0000, 0xA9FE0000}, // 169.254.x.x (link-local self-configured addresses)
234 {0xFFF00000, 0xAC100000}, // 172.(16-31).x.x (private address range)
235 {0xFFFF0000, 0xC0A80000}, // 192.168.x.x (private address range)
242 UInt32 address = ntohl(self.ipv4);
244 for( i=0; kPrivateRanges[i].mask; i++ )
245 if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
257 @implementation HostAddress
260 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
262 self = [super initWithIPv4: 0 port: port];
264 if( [hostname length]==0 ) {
268 _hostname = [hostname copy];
273 - (id) initWithHostname: (NSString*)hostname
274 sockaddr: (const struct sockaddr*)sockaddr
277 if( [hostname length]==0 ) {
281 self = [super initWithSockAddr: sockaddr port: port];
283 _hostname = [hostname copy];
289 - (void)encodeWithCoder:(NSCoder *)coder
291 [super encodeWithCoder: coder];
292 [coder encodeObject: _hostname forKey: @"host"];
295 - (id)initWithCoder:(NSCoder *)decoder
297 self = [super initWithCoder: decoder];
299 _hostname = [[decoder decodeObjectForKey: @"host"] copy];
311 - (NSString*) description
313 NSMutableString *desc = [[_hostname mutableCopy] autorelease];
314 NSString *addr = self.ipv4name;
316 [desc appendFormat: @"(%@)", addr];
318 [desc appendFormat: @":%hu",self.port];
325 return [_hostname hash] ^ self.port;
329 - (NSString*) hostname {return _hostname;}
334 struct hostent *ent = gethostbyname(_hostname.UTF8String);
336 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
339 return * (const in_addr_t*) ent->h_addr_list[0];
343 - (BOOL) isSameHost: (IPAddress*)addr
345 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
354 @implementation RecentAddress
357 - (id) initWithIPAddress: (IPAddress*)addr
359 return [super initWithIPv4: addr.ipv4 port: addr.port];
363 @synthesize lastSuccess=_lastSuccess, successes=_successes;
367 if( _successes < 0xFFFF )
369 _lastSuccess = CFAbsoluteTimeGetCurrent();
375 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
376 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
382 - (void)encodeWithCoder:(NSCoder *)coder
384 [super encodeWithCoder: coder];
385 [coder encodeDouble: _lastSuccess forKey: @"last"];
386 [coder encodeInt: _successes forKey: @"succ"];
389 - (id)initWithCoder:(NSCoder *)decoder
391 self = [super initWithCoder: decoder];
393 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
394 _successes = [decoder decodeIntForKey: @"succ"];
408 TestCase(IPAddress) {
409 RequireTestCase(CollectionUtils);
410 IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
411 CAssertEq(addr.ipv4,(UInt32)htonl(0x0A0001FE));
412 CAssertEq(addr.port,8080);
413 CAssertEqual(addr.hostname,@"10.0.1.254");
414 CAssertEqual(addr.description,@"10.0.1.254:8080");
415 CAssert(addr.isPrivate);
418 addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
419 CAssertEq(addr.class,[IPAddress class]);
420 CAssertEq(addr.ipv4,(UInt32)htonl(0x424200FF));
421 CAssertEq(addr.port,123);
422 CAssertEqual(addr.hostname,@"66.66.0.255");
423 CAssertEqual(addr.description,@"66.66.0.255:123");
424 CAssert(!addr.isPrivate);
427 addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
428 CAssertEq(addr.class,[HostAddress class]);
429 Log(@"www.apple.com = %@ [0x%08X]", addr.ipv4name, ntohl(addr.ipv4));
430 CAssertEq(addr.ipv4,(UInt32)htonl(0x11FBC820));
431 CAssertEq(addr.port,80);
432 CAssertEqual(addr.hostname,@"www.apple.com");
433 CAssertEqual(addr.description,@"www.apple.com:80");
434 CAssert(!addr.isPrivate);
440 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
442 Redistribution and use in source and binary forms, with or without modification, are permitted
443 provided that the following conditions are met:
445 * Redistributions of source code must retain the above copyright notice, this list of conditions
446 and the following disclaimer.
447 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
448 and the following disclaimer in the documentation and/or other materials provided with the
451 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
452 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
453 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
454 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
455 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
456 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
457 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
458 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.