2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
24 #if HAVE_SYS_TYPES_H || defined(__APPLE__)
25 # include <sys/types.h>
28 # include <netinet/in.h>
30 #if HAVE_UNISTD_H || defined(__APPLE__)
33 #if defined(__APPLE__)
37 #if !defined(__CYGWIN32__)
46 #include "NGSocketExceptions.h"
47 #include "NGInternetSocketAddress.h"
48 #include "NGInternetSocketDomain.h"
51 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
52 #define USE_GETHOSTBYNAME_R 1
55 @implementation NGInternetSocketAddress
57 #if LIB_FOUNDATION_LIBRARY
58 extern NSRecursiveLock *libFoundationLock;
59 #define systemLock libFoundationLock
61 static NSRecursiveLock *systemLock = nil;
64 static NSMapTable *nameCache = NULL;
67 [NGSocket initialize];
69 if (nameCache == NULL) {
70 nameCache = NSCreateMapTable(NSIntMapKeyCallBacks,
71 NSObjectMapValueCallBacks,
75 #if !LIB_FOUNDATION_LIBRARY
76 [[NSNotificationCenter defaultCenter]
77 addObserver:self selector:@selector(taskNowMultiThreaded:)
78 name:NSWillBecomeMultiThreadedNotification
83 + (void)taskNowMultiThreaded:(NSNotification *)_notification {
84 if (systemLock == nil) systemLock = [[NSRecursiveLock alloc] init];
87 static inline NSString *_nameOfLocalhost(void) {
89 return [[NSHost currentHost] name];
91 NSString *hostName = nil;
96 gethostname(buffer, sizeof(buffer));
97 hostName = [[NSString alloc] initWithCString:buffer];
101 return [hostName autorelease];
107 Fill up the host and port ivars based on the INET address.
109 TODO: cache some information, takes quite some time (11% of execution
110 time on MacOSX proftest) to get the hostname of an address.
112 struct hostent *hostEntity = NULL; // only valid during lock
113 NSString *newHost = nil;
115 struct sockaddr_in *sockAddr = self->address;
117 if (self->isHostFilled)
118 /* host is already filled .. */
122 NSAssert(self->isAddressFilled, @"either host or address must be filled ...");
125 if (sockAddr->sin_addr.s_addr != 0) { // not a wildcard address
126 #if !defined(HAVE_GETHOSTBYADDR_R)
128 newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
131 newHost = NSMapGet(nameCache, (void *)sockAddr->sin_addr.s_addr);
134 if (newHost == nil) {
138 #if USE_GETHOSTBYNAME_R
139 struct hostent hostEntityBuffer;
142 hostEntity = gethostbyaddr_r((char *)&(sockAddr->sin_addr.s_addr),
144 [[self domain] socketDomain],
150 # warning "doesn't resolve host name on mingw32 !"
154 hostEntity = gethostbyaddr((char *)&(sockAddr->sin_addr.s_addr),
156 [[self domain] socketDomain]);
157 # if defined(WIN32) && !defined(__CYGWIN32__)
158 errorCode = WSAGetLastError();
164 if (hostEntity == NULL) {
173 NSLog(@"%s: host not found ..", __PRETTY_FUNCTION__);
178 NSLog(@"%s:\n couldn't lookup host, retry ..",
179 __PRETTY_FUNCTION__);
182 NSLog(@"%s: couldn't lookup host ..", __PRETTY_FUNCTION__);
187 NSLog(@"%s: no recovery", __PRETTY_FUNCTION__);
191 NSLog(@"%s: no data", __PRETTY_FUNCTION__);
195 NSLog(@"%s: unknown error: h_errno=%i errno=%s",
197 errorCode, strerror(errno));
201 newHost = [NSString stringWithCString:inet_ntoa(sockAddr->sin_addr)];
204 newHost = [NSString stringWithCString:hostEntity->h_name];
209 if (hostEntity == NULL) {
210 // throw could not get address ..
211 NSLog(@"could not get DNS name of address %@ in domain %@: %i",
212 newHost, [self domain], errorCode);
216 NSMapInsert(nameCache, (void *)sockAddr->sin_addr.s_addr, newHost);
218 /* TODO: should also cache unknown IPs ! */
221 //else printf("%s: CACHE HIT !\n", __PRETTY_FUNCTION__);
223 #if !defined(HAVE_GETHOSTBYADDR_R)
228 /* wildcard address */
232 ASSIGNCOPY(self->hostName, newHost);
233 self->isHostFilled = YES;
236 - (NSException *)_fillAddress {
238 Fill up the INET address based on the host and port ivars.
241 // NGCouldNotResolveHostNameException when a DNS lookup fails
243 #if defined(WIN32) && !defined(__CYGWIN32__)
244 u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
246 unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr);
249 if (self->isAddressFilled)
250 /* address is already filled .. */
254 NSAssert(self->isHostFilled, @"either host or address must be filled ...");
257 if (self->hostName == nil) {
258 // if ([self isWildcardAddress])
259 *ia = htonl(INADDR_ANY); // wildcard (0)
260 self->isAddressFilled = YES;
263 const unsigned char *chost;
265 chost = [[self hostName] cString];
267 // try to interpret hostname as INET dotted address (eg 122.133.44.87)
268 *ia = inet_addr(chost);
270 if ((int)*ia != -1) { // succeeded
271 self->isAddressFilled = YES;
273 else { // failed, try to interpret hostname as DNS hostname
276 int addrType = AF_INET;
277 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
279 struct hostent hostEntity;
281 struct hostent *hostEntity; // only valid during lock
284 #if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux)
285 if (gethostbyname_r(chost, &hostEntity,
286 buffer, sizeof(buffer), &errorCode) == NULL) {
290 addrType = hostEntity.h_addrtype;
292 if (addrType == AF_INET)
293 *ia = ((struct in_addr *)(hostEntity.h_addr_list[0]))->s_addr;
295 didFail = YES; // invalid domain (eg AF_INET6)
300 if ((hostEntity = gethostbyname(chost)) == NULL) {
302 #if defined(WIN32) && !defined(__CYGWIN32__)
303 errorCode = WSAGetLastError();
309 addrType = hostEntity->h_addrtype;
311 if (addrType == AF_INET)
312 *ia = ((struct in_addr *)(hostEntity->h_addr_list[0]))->s_addr;
314 didFail = YES; // invalid domain (eg AF_INET6)
320 if (didFail) { // could not resolve hostname
322 NSString *reason = nil;
324 if (addrType != AF_INET) {
325 // invalid domain (eg AF_INET6)
326 reason = @"resolved address is in invalid domain";
330 case HOST_NOT_FOUND: reason = @"host not found"; break;
331 case TRY_AGAIN: reason = @"try again"; break;
332 case NO_RECOVERY: reason = @"no recovery"; break;
333 case NO_DATA: reason = @"no address available"; break;
335 reason = [NSString stringWithFormat:@"error code %i", errorCode];
339 return [[[NGCouldNotResolveHostNameException alloc]
340 initWithHostName:[self hostName] reason:reason] autorelease];
343 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)
528 [[self _fillAddress] raise];
536 ptr = inet_ntoa(*((struct in_addr *)ia));
537 str = [NSString stringWithCString:ptr];
547 if (!self->isAddressFilled)
548 [[self _fillAddress] raise];
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)
564 [[self _fillAddress] raise];
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 {
630 if ((name = [self hostName]) == nil)
633 return [NSString stringWithFormat:@"%@:%i", name, [self port]];
636 - (NSString *)description {
640 ms = [NSMutableString stringWithCapacity:128];
641 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
643 if ((tmp = [self hostName]) != nil)
644 [ms appendFormat:@" host=%@", tmp];
646 [ms appendString:@" *host"];
648 if (!self->isAddressFilled)
649 [ms appendString:@" not-filled"];
651 [ms appendFormat:@" port=%d", [self port]];
653 [ms appendString:@">"];
657 @end /* NGInternetSocketAddress */
659 @implementation NGActiveSocket(NGInternetActiveSocket)
661 + (id)socketConnectedToPort:(int)_port onHost:(id)_host {
662 // this method calls +socketConnectedToAddress: with an
663 // NGInternetSocketAddress
665 return [self socketConnectedToAddress:
666 [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
669 - (BOOL)connectToPort:(int)_port onHost:(id)_host {
670 // this method calls -connectToAddress: with an NGInternetSocketAddress
672 return [self connectToAddress:
673 [NGInternetSocketAddress addressWithPort:_port onHost:_host]];
676 @end /* NGActiveSocket(NGInternetActiveSocket) */