Fixed a small leak of an NSString object.
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 - (id) initWithSockAddr: (const struct sockaddr*)sockaddr
89 self = [self initWithSockAddr: sockaddr];
95 + (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
97 uint8_t name[SOCK_MAXADDRLEN];
98 socklen_t namelen = sizeof(name);
99 struct sockaddr *addr = (struct sockaddr*)name;
100 if (0 == getpeername(socket, addr, &namelen))
101 return [[[self alloc] initWithSockAddr: addr] autorelease];
106 - (id) copyWithZone: (NSZone*)zone
108 return [self retain];
111 - (void)encodeWithCoder:(NSCoder *)coder
114 [coder encodeInt32: _ipv4 forKey: @"ipv4"];
116 [coder encodeInt: _port forKey: @"port"];
119 - (id)initWithCoder:(NSCoder *)decoder
123 _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
124 _port = [decoder decodeIntForKey: @"port"];
130 @synthesize ipv4=_ipv4, port=_port;
132 - (BOOL) isEqual: (IPAddress*)addr
134 return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
137 - (BOOL) isSameHost: (IPAddress*)addr
139 return addr && _ipv4==addr->_ipv4;
144 return _ipv4 ^ _port;
147 - (NSString*) ipv4name
149 UInt32 ipv4 = self.ipv4;
151 const UInt8* b = (const UInt8*)&ipv4;
152 return [NSString stringWithFormat: @"%u.%u.%u.%u",
153 (unsigned)b[0],(unsigned)b[1],(unsigned)b[2],(unsigned)b[3]];
158 - (NSString*) hostname
160 return [self ipv4name];
163 - (NSString*) description
165 NSString *name = self.hostname ?: @"0.0.0.0";
167 name = [name stringByAppendingFormat: @":%hu",_port];
172 + (IPAddress*) localAddressWithPort: (UInt16)port
174 // getifaddrs returns a linked list of interface entries;
175 // find the first active non-loopback interface with IPv4:
177 struct ifaddrs *interfaces;
178 if( getifaddrs(&interfaces) == 0 ) {
179 struct ifaddrs *interface;
180 for( interface=interfaces; interface; interface=interface->ifa_next ) {
181 if( (interface->ifa_flags & IFF_UP) && ! (interface->ifa_flags & IFF_LOOPBACK) ) {
182 const struct sockaddr_in *addr = (const struct sockaddr_in*) interface->ifa_addr;
183 if( addr && addr->sin_family==AF_INET ) {
184 address = addr->sin_addr.s_addr;
189 freeifaddrs(interfaces);
191 return [[[self alloc] initWithIPv4: address port: port] autorelease];
194 + (IPAddress*) localAddress
196 return [self localAddressWithPort: 0];
200 // Private IP address ranges. See RFC 3330.
201 static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
202 {0xFF000000, 0x00000000}, // 0.x.x.x (hosts on "this" network)
203 {0xFF000000, 0x0A000000}, // 10.x.x.x (private address range)
204 {0xFF000000, 0x7F000000}, // 127.x.x.x (loopback)
205 {0xFFFF0000, 0xA9FE0000}, // 169.254.x.x (link-local self-configured addresses)
206 {0xFFF00000, 0xAC100000}, // 172.(16-31).x.x (private address range)
207 {0xFFFF0000, 0xC0A80000}, // 192.168.x.x (private address range)
214 UInt32 address = ntohl(self.ipv4);
216 for( i=0; kPrivateRanges[i].mask; i++ )
217 if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
229 @implementation HostAddress
232 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
234 self = [super initWithIPv4: 0 port: port];
236 if( [hostname length]==0 ) {
240 _hostname = [hostname copy];
245 - (id) initWithHostname: (NSString*)hostname
246 sockaddr: (const struct sockaddr*)sockaddr
249 if( [hostname length]==0 ) {
253 self = [super initWithSockAddr: sockaddr port: port];
255 _hostname = [hostname copy];
261 - (void)encodeWithCoder:(NSCoder *)coder
263 [super encodeWithCoder: coder];
264 [coder encodeObject: _hostname forKey: @"host"];
267 - (id)initWithCoder:(NSCoder *)decoder
269 self = [super initWithCoder: decoder];
271 _hostname = [[decoder decodeObjectForKey: @"host"] copy];
283 - (NSString*) description
285 NSMutableString *desc = [[_hostname mutableCopy] autorelease];
286 NSString *addr = self.ipv4name;
288 [desc appendFormat: @"(%@)", addr];
290 [desc appendFormat: @":%hu",self.port];
297 return [_hostname hash] ^ self.port;
301 - (NSString*) hostname {return _hostname;}
306 struct hostent *ent = gethostbyname(_hostname.UTF8String);
308 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
311 return * (const in_addr_t*) ent->h_addr_list[0];
315 - (BOOL) isSameHost: (IPAddress*)addr
317 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
326 @implementation RecentAddress
329 - (id) initWithIPAddress: (IPAddress*)addr
331 return [super initWithIPv4: addr.ipv4 port: addr.port];
335 @synthesize lastSuccess=_lastSuccess, successes=_successes;
339 if( _successes < 0xFFFF )
341 _lastSuccess = CFAbsoluteTimeGetCurrent();
347 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
348 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
354 - (void)encodeWithCoder:(NSCoder *)coder
356 [super encodeWithCoder: coder];
357 [coder encodeDouble: _lastSuccess forKey: @"last"];
358 [coder encodeInt: _successes forKey: @"succ"];
361 - (id)initWithCoder:(NSCoder *)decoder
363 self = [super initWithCoder: decoder];
365 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
366 _successes = [decoder decodeIntForKey: @"succ"];
380 TestCase(IPAddress) {
381 RequireTestCase(CollectionUtils);
382 IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
383 CAssertEq(addr.ipv4,(UInt32)htonl(0x0A0001FE));
384 CAssertEq(addr.port,8080);
385 CAssertEqual(addr.hostname,@"10.0.1.254");
386 CAssertEqual(addr.description,@"10.0.1.254:8080");
387 CAssert(addr.isPrivate);
390 addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
391 CAssertEq(addr.class,[IPAddress class]);
392 CAssertEq(addr.ipv4,(UInt32)htonl(0x424200FF));
393 CAssertEq(addr.port,123);
394 CAssertEqual(addr.hostname,@"66.66.0.255");
395 CAssertEqual(addr.description,@"66.66.0.255:123");
396 CAssert(!addr.isPrivate);
399 addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
400 CAssertEq(addr.class,[HostAddress class]);
401 Log(@"www.apple.com = %@ [0x%08X]", addr.ipv4name, ntohl(addr.ipv4));
402 CAssertEq(addr.ipv4,(UInt32)htonl(0x11FBC820));
403 CAssertEq(addr.port,80);
404 CAssertEqual(addr.hostname,@"www.apple.com");
405 CAssertEqual(addr.description,@"www.apple.com:80");
406 CAssert(!addr.isPrivate);
412 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
414 Redistribution and use in source and binary forms, with or without modification, are permitted
415 provided that the following conditions are met:
417 * Redistributions of source code must retain the above copyright notice, this list of conditions
418 and the following disclaimer.
419 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
420 and the following disclaimer in the documentation and/or other materials provided with the
423 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
424 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
425 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
426 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
427 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
428 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
429 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
430 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.