Fixed a serious bug - a race condition where a data buffer in the writer's queue could be dealloced (not the NSData, but its bytes themselves) before the writer sent it, resulting in an EFAULT error.
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];
249 return [_hostname hash] ^ _port;
253 - (NSString*) hostname {return _hostname;}
258 struct hostent *ent = gethostbyname(_hostname.UTF8String);
260 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
263 return * (const in_addr_t*) ent->h_addr_list[0];
267 - (BOOL) isSameHost: (IPAddress*)addr
269 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
278 @implementation RecentAddress
281 - (id) initWithIPAddress: (IPAddress*)addr
283 return [super initWithIPv4: addr.ipv4 port: addr.port];
287 @synthesize lastSuccess=_lastSuccess, successes=_successes;
291 if( _successes < 0xFFFF )
293 _lastSuccess = CFAbsoluteTimeGetCurrent();
299 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
300 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
306 - (void)encodeWithCoder:(NSCoder *)coder
308 [super encodeWithCoder: coder];
309 [coder encodeDouble: _lastSuccess forKey: @"last"];
310 [coder encodeInt: _successes forKey: @"succ"];
313 - (id)initWithCoder:(NSCoder *)decoder
315 self = [super initWithCoder: decoder];
317 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
318 _successes = [decoder decodeIntForKey: @"succ"];
332 TestCase(IPAddress) {
333 RequireTestCase(CollectionUtils);
334 IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
335 CAssertEq(addr.ipv4,htonl(0x0A0001FE));
336 CAssertEq(addr.port,8080);
337 CAssertEqual(addr.hostname,@"10.0.1.254");
338 CAssertEqual(addr.description,@"10.0.1.254:8080");
339 CAssert(addr.isPrivate);
341 addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
342 CAssertEq(addr.class,[IPAddress class]);
343 CAssertEq(addr.ipv4,htonl(0x424200FF));
344 CAssertEq(addr.port,123);
345 CAssertEqual(addr.hostname,@"66.66.0.255");
346 CAssertEqual(addr.description,@"66.66.0.255:123");
347 CAssert(!addr.isPrivate);
349 addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
350 CAssertEq(addr.class,[HostAddress class]);
351 Log(@"www.apple.com = 0x%08X", addr.ipv4);
352 CAssertEq(addr.ipv4,htonl(0x1195A00A));
353 CAssertEq(addr.port,80);
354 CAssertEqual(addr.hostname,@"www.apple.com");
355 CAssertEqual(addr.description,@"www.apple.com:80");
356 CAssert(!addr.isPrivate);
361 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
363 Redistribution and use in source and binary forms, with or without modification, are permitted
364 provided that the following conditions are met:
366 * Redistributions of source code must retain the above copyright notice, this list of conditions
367 and the following disclaimer.
368 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
369 and the following disclaimer in the documentation and/or other materials provided with the
372 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
373 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
374 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
375 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
376 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
377 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
378 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
379 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.