2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 #if HAVE_SYS_TYPES_H || defined(__APPLE__)
26 # include <sys/types.h>
29 # include <netinet/in.h>
31 #if HAVE_UNISTD_H || defined(__APPLE__)
34 #if defined(__APPLE__)
38 #if !defined(__CYGWIN32__)
47 #include "NGSocketExceptions.h"
48 #include "NGInternetSocketAddress.h"
49 #include "NGInternetSocketDomain.h"
52 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
53 #define USE_GETHOSTBYNAME_R 1
56 @implementation NGInternetSocketAddress
58 #if LIB_FOUNDATION_LIBRARY
59 extern NSRecursiveLock *libFoundationLock;
60 #define systemLock libFoundationLock
62 static NSRecursiveLock *systemLock = nil;
65 static NSMapTable *nameCache = NULL;
68 [NGSocket initialize];
70 if (nameCache == NULL) {
71 nameCache = NSCreateMapTable(NSIntMapKeyCallBacks,
72 NSObjectMapValueCallBacks,
76 #if !LIB_FOUNDATION_LIBRARY
77 [[NSNotificationCenter defaultCenter]
78 addObserver:self selector:@selector(taskNowMultiThreaded:)
79 name:NSWillBecomeMultiThreadedNotification
84 + (void)taskNowMultiThreaded:(NSNotification *)_notification {
85 if (systemLock == nil) systemLock = [[NSRecursiveLock alloc] init];
88 static inline NSString *_nameOfLocalhost(void) {
90 return [[NSHost currentHost] name];
92 NSString *hostName = nil;
97 gethostname(buffer, sizeof(buffer));
98 hostName = [[NSString alloc] initWithCString:buffer];
102 return [hostName autorelease];
108 Fill up the host and port ivars based on the INET address.
110 TODO: cache some information, takes quite some time (11% of execution
111 time on MacOSX proftest) to get the hostname of an address.
113 struct hostent *hostEntity = NULL; // only valid during lock
114 NSString *newHost = nil;
116 struct sockaddr_in *sockAddr = self->address;
118 if (self->isHostFilled)
119 /* host is already filled .. */
123 NSAssert(self->isAddressFilled, @"either host or address must be filled ...");
126 if (sockAddr->sin_addr.s_addr != 0) { // not a wildcard address
127 #if !defined(HAVE_GETHOSTBYADDR_R)
129 newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
132 newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
135 if (newHost == nil) {
139 #if USE_GETHOSTBYNAME_R
140 struct hostent hostEntityBuffer;
143 hostEntity = gethostbyaddr_r((char *)&(sockAddr->sin_addr.s_addr),
145 [[self domain] socketDomain],
151 # warning "doesn't resolve host name on mingw32 !"
155 hostEntity = gethostbyaddr((char *)&(sockAddr->sin_addr.s_addr),
157 [[self domain] socketDomain]);
158 # if defined(WIN32) && !defined(__CYGWIN32__)
159 errorCode = WSAGetLastError();
165 if (hostEntity == NULL) {
174 NSLog(@"%s: host not found ..", __PRETTY_FUNCTION__);
179 NSLog(@"%s:\n couldn't lookup host, retry ..",
180 __PRETTY_FUNCTION__);
183 NSLog(@"%s: couldn't lookup host ..", __PRETTY_FUNCTION__);
188 NSLog(@"%s: no recovery", __PRETTY_FUNCTION__);
192 NSLog(@"%s: no data", __PRETTY_FUNCTION__);
196 NSLog(@"%s: unknown error: h_errno=%i errno=%s",
198 errorCode, strerror(errno));
202 newHost = [NSString stringWithCString:inet_ntoa(sockAddr->sin_addr)];
205 newHost = [NSString stringWithCString:hostEntity->h_name];
210 if (hostEntity == NULL) {
211 // throw could not get address ..
212 NSLog(@"could not get DNS name of address %@ in domain %@: %i",
213 newHost, [self domain], errorCode);
217 NSMapInsert(nameCache, (void *)sockAddr->sin_addr.s_addr, newHost);
219 /* TODO: should also cache unknown IPs ! */
222 //else printf("%s: CACHE HIT !\n", __PRETTY_FUNCTION__);
224 #if !defined(HAVE_GETHOSTBYADDR_R)
229 /* wildcard address */
233 ASSIGNCOPY(self->hostName, newHost);
234 self->isHostFilled = YES;
237 - (void)_fillAddress {
239 Fill up the INET address based on the host and port ivars.
242 // NGCouldNotResolveHostNameException when a DNS lookup fails
244 #if defined(WIN32) && !defined(__CYGWIN32__)
245 u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
247 unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
250 if (self->isAddressFilled)
251 /* address is already filled .. */
255 NSAssert(self->isHostFilled, @"either host or address must be filled ...");
258 if (self->hostName == nil) {
259 // if ([self isWildcardAddress])
260 *ia = htonl(INADDR_ANY); // wildcard (0)
261 self->isAddressFilled = YES;
264 const unsigned char *chost;
266 chost = [[self hostName] cString];
268 // try to interpret hostname as INET dotted address (eg 122.133.44.87)
269 *ia = inet_addr(chost);
271 if ((int)*ia != -1) { // succeeded
272 self->isAddressFilled = YES;
274 else { // failed, try to interpret hostname as DNS hostname
277 int addrType = AF_INET;
278 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
280 struct hostent hostEntity;
282 struct hostent *hostEntity; // only valid during lock
285 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
286 if (gethostbyname_r(chost, &hostEntity,
287 buffer, sizeof(buffer), &errorCode) == NULL) {
291 addrType = hostEntity.h_addrtype;
293 if (addrType == AF_INET)
294 *ia = ((struct in_addr *)(hostEntity.h_addr_list[0]))->s_addr;
296 didFail = YES; // invalid domain (eg AF_INET6)
301 if ((hostEntity = gethostbyname(chost)) == NULL) {
303 #if defined(WIN32) && !defined(__CYGWIN32__)
304 errorCode = WSAGetLastError();
310 addrType = hostEntity->h_addrtype;
312 if (addrType == AF_INET)
313 *ia = ((struct in_addr *)(hostEntity->h_addr_list[0]))->s_addr;
315 didFail = YES; // invalid domain (eg AF_INET6)
321 if (didFail) { // could not resolve hostname
323 NSString *reason = nil;
325 if (addrType != AF_INET) {
326 // invalid domain (eg AF_INET6)
327 reason = @"resolved address is in invalid domain";
331 case HOST_NOT_FOUND: reason = @"host not found"; break;
332 case TRY_AGAIN: reason = @"try again"; break;
333 case NO_RECOVERY: reason = @"no recovery"; break;
334 case NO_DATA: reason = @"no address available"; break;
336 reason = [NSString stringWithFormat:@"error code %i", errorCode];
340 [[[NGCouldNotResolveHostNameException alloc]
341 initWithHostName:[self hostName] reason:reason] raise];
344 self->isAddressFilled = YES;
351 + (id)addressWithPort:(int)_port onHost:(id)_host {
352 return [[[self alloc] initWithPort:_port onHost:_host] autorelease];
354 + (id)addressWithPort:(int)_port {
355 return [[[self alloc] initWithPort:_port] autorelease];
358 + (id)addressWithService:(NSString *)_sname onHost:(id)_host
359 protocol:(NSString *)_protocol
361 return [[[self alloc] initWithService:_sname
366 + (id)addressWithService:(NSString *)_sname protocol:(NSString *)_protocol {
367 return [[[self alloc] initWithService:_sname protocol:_protocol] autorelease];
370 + (id)wildcardAddress {
371 return [[[self alloc] initWithPort:0 onHost:@"*"] autorelease];
373 + (id)wildcardAddressWithPort:(int)_port {
374 return [[[self alloc] initWithPort:_port onHost:@"*"] autorelease];
378 if ((self = [super init])) {
379 self->address = malloc(sizeof(struct sockaddr_in));
384 - (id)initWithPort:(int)_port onHost:(id)_host { /* designated initializer */
385 if ((self = [self init])) {
386 self->isAddressFilled = NO;
387 self->isHostFilled = YES;
390 if ([_host isKindOfClass:[NSHost class]])
391 _host = [(NSHost *)_host address];
393 if ([_host isEqualToString:@"*"]) {
394 self->hostName = nil; /* wildcard host */
397 self->hostName = [_host copy];
398 self->isWildcardHost = NO;
403 self->isWildcardHost = YES;
406 ((struct sockaddr_in *)self->address)->sin_family =
407 [[self domain] socketDomain];
408 ((struct sockaddr_in *)self->address)->sin_port =
409 htons((short)(_port & 0xffff));
414 - (id)initWithService:(NSString *)_serviceName onHost:(id)_host
415 protocol:(NSString *)_protocol
417 NSException *exc = nil;
419 #if defined(HAVE_GETSERVBYNAME_R)
421 struct servent entry;
423 struct servent *entry;
426 #if defined(HAVE_GETSERVBYNAME_R)
427 if (getservbyname_r((char *)[_serviceName cString], [_protocol cString],
428 &entry, buffer, sizeof(buffer)) == NULL) {
429 exc = [[NGDidNotFindServiceException alloc] initWithServiceName:_serviceName];
436 entry = getservbyname((char *)[_serviceName cString], [_protocol cString]);
438 exc = [[NGDidNotFindServiceException alloc] initWithServiceName:_serviceName];
440 port = entry->s_port;
446 self = [self autorelease];
450 return [self initWithPort:port onHost:_host];
453 - (id)initWithPort:(int)_port {
454 return [self initWithPort:_port onHost:_nameOfLocalhost()];
457 - (id)initWithService:(NSString *)_serviceName protocol:(NSString *)_protocol {
458 return [self initWithService:_serviceName
459 onHost:_nameOfLocalhost()
463 - (id)initWithDomain:(id)_domain
464 internalRepresentation:(void *)_representation
467 struct sockaddr_in *sockAddr = _representation;
469 NSAssert(_length == sizeof(struct sockaddr_in),
470 @"invalid socket address length");
472 if (_length != sizeof(struct sockaddr_in)) {
473 NSLog(@"%s: got invalid sockaddr_in size ...", __PRETTY_FUNCTION__);
479 if ((self = [self init]) == nil)
482 self->isHostFilled = NO; /* need to lookup DNS */
486 self->isAddressFilled = YES;
487 memcpy(self->address, _representation, sizeof(struct sockaddr_in));
489 if (sockAddr->sin_addr.s_addr != 0) {
490 /* not a wildcard address */
491 self->isWildcardHost = NO;
494 /* wildcard address */
495 self->hostName = nil;
496 self->isWildcardHost = YES;
497 self->isHostFilled = YES; /* wildcard host, no DNS lookup ... */
504 [self->hostName release];
505 if (self->address) free(self->address);
511 - (NSString *)hostName {
512 if (!self->isHostFilled) [self _fillHost];
513 return [[self->hostName copy] autorelease];
515 - (NSString *)address {
516 #if defined(WIN32) && !defined(__CYGWIN32__)
518 ia = (u_long *)&(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
521 ia = (unsigned int)&(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
524 if (self->hostName == nil) /* wildcard */
527 if (!self->isAddressFilled)
536 ptr = inet_ntoa(*((struct in_addr *)ia));
537 str = [NSString stringWithCString:ptr];
547 if (!self->isAddressFilled)
549 return ntohs(((struct sockaddr_in *)self->address)->sin_port);
552 - (BOOL)isWildcardAddress {
553 if (self->isWildcardHost) return YES;
554 return ([self hostName] == nil) || ([self port] == 0);
557 /* NGSocketAddress protocol */
559 - (void *)internalAddressRepresentation {
561 // NGCouldNotResolveHostNameException when a DNS lookup fails
563 if (!self->isAddressFilled)
566 return self->address;
569 - (int)addressRepresentationSize {
570 return [[self domain] addressRepresentationSize];
573 static id domain = nil;
574 if (domain == nil) domain = [[NGInternetSocketDomain domain] retain];
584 - (BOOL)isEqualToAddress:(NGInternetSocketAddress *)_otherAddress {
585 if (self == _otherAddress)
587 if (![[_otherAddress hostName] isEqualToString:[self hostName]])
589 if ([_otherAddress port] != [self port])
594 - (BOOL)isEqual:(id)_object {
595 if (_object == self) return YES;
596 if ([_object class] != [self class]) return NO;
597 return [self isEqualToAddress:_object];
602 - (id)copyWithZone:(NSZone *)_zone {
603 // socket addresses are immutable, therefore just retain self
604 return [self retain];
609 - (void)encodeWithCoder:(NSCoder *)_encoder {
610 int aPort = [self port];
612 [_encoder encodeValueOfObjCType:@encode(int) at:&aPort];
613 [_encoder encodeObject:[self hostName]];
615 - (id)initWithCoder:(NSCoder *)_decoder {
619 [_decoder decodeValueOfObjCType:@encode(int) at:&aPort];
620 aHost = [_decoder decodeObject];
622 return [self initWithPort:aPort onHost:aHost];
627 - (NSString *)stringValue {
628 NSString *name = [self hostName];
629 if (name == nil) name = @"*";
630 return [NSString stringWithFormat:@"%@:%i", name, [self port]];
633 - (NSString *)description {
634 NSString *name = [self hostName];
636 if (name == nil) name = @"*";
638 return [NSString stringWithFormat:@"<InetSocketAddress: %@:%i>",
642 @end /* NGInternetSocketAddress */
644 @implementation NGActiveSocket(NGInternetActiveSocket)
646 + (id)socketConnectedToPort:(int)_port onHost:(id)_host {
647 // this method calls +socketConnectedToAddress: with an
648 // NGInternetSocketAddress
650 return [self socketConnectedToAddress:
651 [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
654 - (BOOL)connectToPort:(int)_port onHost:(id)_host {
655 // this method calls -connectToAddress: with an NGInternetSocketAddress
657 return [self connectToAddress:
658 [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
661 @end /* NGActiveSocket(NGInternetActiveSocket) */