BROKEN COMMIT. Majority of code to handle closing has been added. Listeners do not close correctly.
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 + (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
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];
111 - (id) copyWithZone: (NSZone*)zone
113 return [self retain];
116 - (void)encodeWithCoder:(NSCoder *)coder
119 [coder encodeInt32: _ipv4 forKey: @"ipv4"];
121 [coder encodeInt: _port forKey: @"port"];
124 - (id)initWithCoder:(NSCoder *)decoder
128 _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
129 _port = [decoder decodeIntForKey: @"port"];
135 @synthesize ipv4=_ipv4, port=_port;
137 - (BOOL) isEqual: (IPAddress*)addr
139 return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
142 - (BOOL) isSameHost: (IPAddress*)addr
144 return addr && _ipv4==addr->_ipv4;
149 return _ipv4 ^ _port;
152 - (NSString*) ipv4name
154 UInt32 ipv4 = self.ipv4;
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]];
163 - (NSString*) hostname
165 return [self ipv4name];
168 - (NSString*) description
170 NSString *name = self.hostname ?: @"0.0.0.0";
172 name = [name stringByAppendingFormat: @":%hu",_port];
177 + (IPAddress*) localAddressWithPort: (UInt16)port
179 // getifaddrs returns a linked list of interface entries;
180 // find the first active non-loopback interface with IPv4:
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;
194 freeifaddrs(interfaces);
196 return [[[self alloc] initWithIPv4: address port: port] autorelease];
199 + (IPAddress*) localAddress
201 return [self localAddressWithPort: 0];
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)
219 UInt32 address = ntohl(self.ipv4);
221 for( i=0; kPrivateRanges[i].mask; i++ )
222 if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
234 @implementation HostAddress
237 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
239 self = [super initWithIPv4: 0 port: port];
241 if( [hostname length]==0 ) {
245 _hostname = [hostname copy];
250 - (id) initWithHostname: (NSString*)hostname
251 sockaddr: (const struct sockaddr*)sockaddr
254 if( [hostname length]==0 ) {
258 self = [super initWithSockAddr: sockaddr port: port];
260 _hostname = [hostname copy];
266 - (void)encodeWithCoder:(NSCoder *)coder
268 [super encodeWithCoder: coder];
269 [coder encodeObject: _hostname forKey: @"host"];
272 - (id)initWithCoder:(NSCoder *)decoder
274 self = [super initWithCoder: decoder];
276 _hostname = [[decoder decodeObjectForKey: @"host"] copy];
288 - (NSString*) description
290 NSMutableString *desc = [[_hostname mutableCopy] autorelease];
291 NSString *addr = self.ipv4name;
293 [desc appendFormat: @"(%@)", addr];
295 [desc appendFormat: @":%hu",self.port];
302 return [_hostname hash] ^ self.port;
306 - (NSString*) hostname {return _hostname;}
311 struct hostent *ent = gethostbyname(_hostname.UTF8String);
313 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
316 return * (const in_addr_t*) ent->h_addr_list[0];
320 - (BOOL) isSameHost: (IPAddress*)addr
322 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
331 @implementation RecentAddress
334 - (id) initWithIPAddress: (IPAddress*)addr
336 return [super initWithIPv4: addr.ipv4 port: addr.port];
340 @synthesize lastSuccess=_lastSuccess, successes=_successes;
344 if( _successes < 0xFFFF )
346 _lastSuccess = CFAbsoluteTimeGetCurrent();
352 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
353 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
359 - (void)encodeWithCoder:(NSCoder *)coder
361 [super encodeWithCoder: coder];
362 [coder encodeDouble: _lastSuccess forKey: @"last"];
363 [coder encodeInt: _successes forKey: @"succ"];
366 - (id)initWithCoder:(NSCoder *)decoder
368 self = [super initWithCoder: decoder];
370 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
371 _successes = [decoder decodeIntForKey: @"succ"];
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);
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);
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);
417 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
419 Redistribution and use in source and binary forms, with or without modification, are permitted
420 provided that the following conditions are met:
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
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.