More work on Bonjour classes. They now support registering services.
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*) localAddressWithPort: (UInt16)port
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 port: port] autorelease];
185 + (IPAddress*) localAddress
187 return [self localAddressWithPort: 0];
191 // Private IP address ranges. See RFC 3330.
192 static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
193 {0xFF000000, 0x00000000}, // 0.x.x.x (hosts on "this" network)
194 {0xFF000000, 0x0A000000}, // 10.x.x.x (private address range)
195 {0xFF000000, 0x7F000000}, // 127.x.x.x (loopback)
196 {0xFFFF0000, 0xA9FE0000}, // 169.254.x.x (link-local self-configured addresses)
197 {0xFFF00000, 0xAC100000}, // 172.(16-31).x.x (private address range)
198 {0xFFFF0000, 0xC0A80000}, // 192.168.x.x (private address range)
205 UInt32 address = ntohl(self.ipv4);
207 for( i=0; kPrivateRanges[i].mask; i++ )
208 if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
220 @implementation HostAddress
223 - (id) initWithHostname: (NSString*)hostname port: (UInt16)port
225 self = [super initWithIPv4: 0 port: port];
227 if( [hostname length]==0 ) {
231 _hostname = [hostname copy];
236 - (id) initWithHostname: (NSString*)hostname
237 sockaddr: (const struct sockaddr*)sockaddr
240 if( [hostname length]==0 ) {
244 self = [super initWithSockAddr: sockaddr];
246 _hostname = [hostname copy];
253 - (void)encodeWithCoder:(NSCoder *)coder
255 [super encodeWithCoder: coder];
256 [coder encodeObject: _hostname forKey: @"host"];
259 - (id)initWithCoder:(NSCoder *)decoder
261 self = [super initWithCoder: decoder];
263 _hostname = [[decoder decodeObjectForKey: @"host"] copy];
275 - (NSString*) description
277 NSMutableString *desc = [_hostname mutableCopy];
278 NSString *addr = self.ipv4name;
280 [desc appendFormat: @"(%@)", addr];
282 [desc appendFormat: @":%hu",_port];
289 return [_hostname hash] ^ _port;
293 - (NSString*) hostname {return _hostname;}
298 struct hostent *ent = gethostbyname(_hostname.UTF8String);
300 Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
303 return * (const in_addr_t*) ent->h_addr_list[0];
307 - (BOOL) isSameHost: (IPAddress*)addr
309 return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
318 @implementation RecentAddress
321 - (id) initWithIPAddress: (IPAddress*)addr
323 return [super initWithIPv4: addr.ipv4 port: addr.port];
327 @synthesize lastSuccess=_lastSuccess, successes=_successes;
331 if( _successes < 0xFFFF )
333 _lastSuccess = CFAbsoluteTimeGetCurrent();
339 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
340 BOOL significant = ( now-_lastSuccess >= 18*60*60 );
346 - (void)encodeWithCoder:(NSCoder *)coder
348 [super encodeWithCoder: coder];
349 [coder encodeDouble: _lastSuccess forKey: @"last"];
350 [coder encodeInt: _successes forKey: @"succ"];
353 - (id)initWithCoder:(NSCoder *)decoder
355 self = [super initWithCoder: decoder];
357 _lastSuccess = [decoder decodeDoubleForKey: @"last"];
358 _successes = [decoder decodeIntForKey: @"succ"];
372 TestCase(IPAddress) {
373 RequireTestCase(CollectionUtils);
374 IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
375 CAssertEq(addr.ipv4,(UInt32)htonl(0x0A0001FE));
376 CAssertEq(addr.port,8080);
377 CAssertEqual(addr.hostname,@"10.0.1.254");
378 CAssertEqual(addr.description,@"10.0.1.254:8080");
379 CAssert(addr.isPrivate);
381 addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
382 CAssertEq(addr.class,[IPAddress class]);
383 CAssertEq(addr.ipv4,(UInt32)htonl(0x424200FF));
384 CAssertEq(addr.port,123);
385 CAssertEqual(addr.hostname,@"66.66.0.255");
386 CAssertEqual(addr.description,@"66.66.0.255:123");
387 CAssert(!addr.isPrivate);
389 addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
390 CAssertEq(addr.class,[HostAddress class]);
391 Log(@"www.apple.com = %@ [0x%08X]", addr.ipv4name, ntohl(addr.ipv4));
392 CAssertEq(addr.ipv4,(UInt32)htonl(0x11FBC820));
393 CAssertEq(addr.port,80);
394 CAssertEqual(addr.hostname,@"www.apple.com");
395 CAssertEqual(addr.description,@"www.apple.com:80");
396 CAssert(!addr.isPrivate);
401 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
403 Redistribution and use in source and binary forms, with or without modification, are permitted
404 provided that the following conditions are met:
406 * Redistributions of source code must retain the above copyright notice, this list of conditions
407 and the following disclaimer.
408 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
409 and the following disclaimer in the documentation and/or other materials provided with the
412 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
413 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
414 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
415 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
416 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
417 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
418 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
419 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.