1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE 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 9 later version. 10 11 SOPE 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. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with SOPE; see the file COPYING. If not, write to the 18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 19 02111-1307, USA. 20*/ 21 22#include "common.h" 23 24#if HAVE_SYS_TYPES_H || defined(__APPLE__) 25# include <sys/types.h> 26#endif 27#if HAVE_NETINET_IN_H 28# include <netinet/in.h> 29#endif 30#if HAVE_UNISTD_H || defined(__APPLE__) 31# include <unistd.h> 32#endif 33#if defined(__APPLE__) 34# include <netdb.h> 35#endif 36 37#if !defined(__CYGWIN32__) 38# if HAVE_WINDOWS_H 39# include <windows.h> 40# endif 41# if HAVE_WINSOCK_H 42# include <winsock.h> 43# endif 44#endif 45 46#include "NGSocketExceptions.h" 47#include "NGInternetSocketAddress.h" 48#include "NGInternetSocketDomain.h" 49#include "common.h" 50 51#if defined(HAVE_GETHOSTBYNAME_R) && !defined(linux) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__GLIBC__) 52#define USE_GETHOSTBYNAME_R 1 53#endif 54 55@implementation NGInternetSocketAddress 56 57#if LIB_FOUNDATION_LIBRARY 58extern NSRecursiveLock *libFoundationLock; 59#define systemLock libFoundationLock 60#else 61static NSRecursiveLock *systemLock = nil; 62#endif 63 64static NSMapTable *nameCache = NULL; 65 66+ (void)initialize { 67 [NGSocket initialize]; 68 69 if (nameCache == NULL) { 70 nameCache = NSCreateMapTable(NSIntMapKeyCallBacks, 71 NSObjectMapValueCallBacks, 72 128); 73 } 74 75#if !LIB_FOUNDATION_LIBRARY 76 [[NSNotificationCenter defaultCenter] 77 addObserver:self selector:@selector(taskNowMultiThreaded:) 78 name:NSWillBecomeMultiThreadedNotification 79 object:nil]; 80#endif 81} 82 83+ (void)taskNowMultiThreaded:(NSNotification *)_notification { 84 if (systemLock == nil) systemLock = [[NSRecursiveLock alloc] init]; 85} 86 87static inline NSString *_nameOfLocalhost(void) { 88#if 1 89 return [[NSHost currentHost] name]; 90#else 91 NSString *hostName = nil; 92 93 [systemLock lock]; 94 { 95 char buffer[1024]; 96 gethostname(buffer, sizeof(buffer)); 97 hostName = [[NSString alloc] initWithCString:buffer]; 98 } 99 [systemLock unlock]; 100 101 return [hostName autorelease]; 102#endif 103} 104 105- (void)_fillHost { 106 /* 107 Fill up the host and port ivars based on the INET address. 108 109 TODO: cache some information, takes quite some time (11% of execution 110 time on MacOSX proftest) to get the hostname of an address. 111 */ 112 struct hostent *hostEntity = NULL; // only valid during lock 113 NSString *newHost = nil; 114 int errorCode = 0; 115 struct sockaddr_in *sockAddr = self->address; 116 117 if (self->isHostFilled) 118 /* host is already filled .. */ 119 return; 120 121#if DEBUG 122 NSAssert(self->isAddressFilled, @"either host or address must be filled ..."); 123#endif 124 125 if (sockAddr->sin_addr.s_addr != 0) { // not a wildcard address 126#if !defined(HAVE_GETHOSTBYADDR_R) 127 [systemLock lock]; 128 newHost = NSMapGet(nameCache, 129 (void *)(unsigned long)sockAddr->sin_addr.s_addr); 130#else 131 [systemLock lock]; 132 newHost = NSMapGet(nameCache, 133 (void *)(unsigned long)sockAddr->sin_addr.s_addr); 134 [systemLock unlock]; 135#endif 136 if (newHost == nil) { 137 BOOL done = NO; 138 139 while (!done) { 140#if USE_GETHOSTBYNAME_R 141 struct hostent hostEntityBuffer; 142 char buffer[8200]; 143 144 hostEntity = gethostbyaddr_r((char *)&(sockAddr->sin_addr.s_addr), 145 4, 146 [[self domain] socketDomain], 147 &hostEntityBuffer, 148 buffer, 8200, 149 &errorCode); 150#else 151# ifdef __MINGW32__ 152# warning "doesn't resolve host name on mingw32 !" 153 hostEntity = NULL; 154 errorCode = -1; 155# else 156 hostEntity = gethostbyaddr((char *)&(sockAddr->sin_addr.s_addr), 157 4, 158 [[self domain] socketDomain]); 159# if defined(WIN32) && !defined(__CYGWIN32__) 160 errorCode = WSAGetLastError(); 161# else 162 errorCode = h_errno; 163# endif 164# endif 165#endif 166 if (hostEntity == NULL) { 167 done = YES; 168 169 switch (errorCode) { 170#ifdef __MINGW32__ 171 case -1: 172 break; 173#endif 174 case HOST_NOT_FOUND: 175 NSLog(@"%s: host not found ..", __PRETTY_FUNCTION__); 176 break; 177 178 case TRY_AGAIN: 179#ifndef __linux 180 NSLog(@"%s:\n couldn't lookup host, retry ..", 181 __PRETTY_FUNCTION__); 182 done = NO; 183#else 184 NSLog(@"%s: couldn't lookup host ..", __PRETTY_FUNCTION__); 185#endif 186 break; 187 188 case NO_RECOVERY: 189 NSLog(@"%s: no recovery", __PRETTY_FUNCTION__); 190 break; 191 192 case NO_DATA: 193 NSLog(@"%s: no data", __PRETTY_FUNCTION__); 194 break; 195 196 default: 197 NSLog(@"%s: unknown error: h_errno=%i errno=%s", 198 __PRETTY_FUNCTION__, 199 errorCode, strerror(errno)); 200 break; 201 } 202 203 newHost = [NSString stringWithCString:inet_ntoa(sockAddr->sin_addr)]; 204 } 205 else { 206 newHost = [NSString stringWithCString:hostEntity->h_name]; 207 done = YES; 208 } 209 } 210 211 if (hostEntity == NULL) { 212 // throw could not get address .. 213 NSLog(@"could not get DNS name of address %@ in domain %@: %i", 214 newHost, [self domain], errorCode); 215 } 216 else if (newHost) { 217 /* add to cache */ 218 NSMapInsert(nameCache, 219 (void *)(unsigned long)sockAddr->sin_addr.s_addr, newHost); 220 } 221 /* TODO: should also cache unknown IPs ! */ 222 } 223 224 //else printf("%s: CACHE HIT !\n", __PRETTY_FUNCTION__); 225 226#if !defined(HAVE_GETHOSTBYADDR_R) 227 [systemLock unlock]; 228#endif 229 } 230 else { 231 /* wildcard address */ 232 newHost = nil; 233 } 234 235 ASSIGNCOPY(self->hostName, newHost); 236 self->isHostFilled = YES; 237} 238 239- (NSException *)_fillAddress { 240 /* 241 Fill up the INET address based on the host and port ivars. 242 */ 243 // throws 244 // NGCouldNotResolveHostNameException when a DNS lookup fails 245 246#if defined(WIN32) && !defined(__CYGWIN32__) 247 u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 248#else 249 unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 250#endif 251 252 if (self->isAddressFilled) 253 /* address is already filled .. */ 254 return nil; 255 256#if DEBUG 257 NSAssert(self->isHostFilled, @"either host or address must be filled ..."); 258#endif 259 260 if (self->hostName == nil) { 261 // if ([self isWildcardAddress]) 262 *ia = htonl(INADDR_ANY); // wildcard (0) 263 self->isAddressFilled = YES; 264 } 265 else { 266 const unsigned char *chost; 267 268 chost = (unsigned char *)[[self hostName] cString]; 269 270 // try to interpret hostname as INET dotted address (eg 122.133.44.87) 271 *ia = inet_addr((char *)chost); 272 273 if ((int)*ia != -1) { // succeeded 274 self->isAddressFilled = YES; 275 } 276 else { // failed, try to interpret hostname as DNS hostname 277 BOOL didFail = NO; 278 int errorCode = 0; 279 int addrType = AF_INET; 280#if defined(USE_GETHOSTBYNAME_R) 281 char buffer[4096]; 282 struct hostent hostEntity; 283#else 284 struct hostent *hostEntity; // only valid during lock 285#endif 286 287#if defined(USE_GETHOSTBYNAME_R) 288 if (gethostbyname_r(chost, &hostEntity, 289 buffer, sizeof(buffer), &errorCode) == NULL) { 290 didFail = YES; 291 } 292 else { 293 addrType = hostEntity.h_addrtype; 294 295 if (addrType == AF_INET) 296 *ia = ((struct in_addr *)(hostEntity.h_addr_list[0]))->s_addr; 297 else 298 didFail = YES; // invalid domain (eg AF_INET6) 299 } 300#else 301 [systemLock lock]; 302 { 303 if ((hostEntity = gethostbyname((char *)chost)) == NULL) { 304 didFail = YES; 305#if defined(WIN32) && !defined(__CYGWIN32__) 306 errorCode = WSAGetLastError(); 307#else 308 errorCode = h_errno; 309#endif 310 } 311 else { 312 addrType = hostEntity->h_addrtype; 313 314 if (addrType == AF_INET) 315 *ia = ((struct in_addr *)(hostEntity->h_addr_list[0]))->s_addr; 316 else 317 didFail = YES; // invalid domain (eg AF_INET6) 318 } 319 } 320 [systemLock unlock]; 321#endif 322 323 if (didFail) { // could not resolve hostname 324 // did not find host 325 NSString *reason = nil; 326 327 if (addrType != AF_INET) { 328 // invalid domain (eg AF_INET6) 329 reason = @"resolved address is in invalid domain"; 330 } 331 else { 332 switch (errorCode) { 333 case HOST_NOT_FOUND: reason = @"host not found"; break; 334 case TRY_AGAIN: reason = @"try again"; break; 335 case NO_RECOVERY: reason = @"no recovery"; break; 336 case NO_DATA: reason = @"no address available"; break; 337 default: 338 reason = [NSString stringWithFormat:@"error code %i", errorCode]; 339 break; 340 } 341 } 342 return [[[NGCouldNotResolveHostNameException alloc] 343 initWithHostName:[self hostName] reason:reason] autorelease]; 344 } 345 346 self->isAddressFilled = YES; 347 } 348 } 349 return nil; 350} 351 352/* constructors */ 353 354+ (id)addressWithPort:(int)_port onHost:(id)_host { 355 return [[[self alloc] initWithPort:_port onHost:_host] autorelease]; 356} 357+ (id)addressWithPort:(int)_port { 358 return [[[self alloc] initWithPort:_port] autorelease]; 359} 360 361+ (id)addressWithService:(NSString *)_sname onHost:(id)_host 362 protocol:(NSString *)_protocol 363{ 364 return [[[self alloc] initWithService:_sname 365 onHost:_host 366 protocol:_protocol] 367 autorelease]; 368} 369+ (id)addressWithService:(NSString *)_sname protocol:(NSString *)_protocol { 370 return [[[self alloc] initWithService:_sname protocol:_protocol] autorelease]; 371} 372 373+ (id)wildcardAddress { 374 return [[[self alloc] initWithPort:0 onHost:@"*"] autorelease]; 375} 376+ (id)wildcardAddressWithPort:(int)_port { 377 return [[[self alloc] initWithPort:_port onHost:@"*"] autorelease]; 378} 379 380- (id)init { 381 if ((self = [super init])) { 382 self->address = malloc(sizeof(struct sockaddr_in)); 383 } 384 return self; 385} 386 387- (id)initWithPort:(int)_port onHost:(id)_host { /* designated initializer */ 388 if ((self = [self init])) { 389 self->isAddressFilled = NO; 390 self->isHostFilled = YES; 391 392 if (_host != nil) { 393 if ([_host isKindOfClass:[NSHost class]]) 394 _host = [(NSHost *)_host address]; 395 396 if ([_host isEqualToString:@"*"]) { 397 self->hostName = nil; /* wildcard host */ 398 } 399 else { 400 self->hostName = [_host copy]; 401 self->isWildcardHost = NO; 402 } 403 } 404 else { 405 /* wildcard host */ 406 self->isWildcardHost = YES; 407 } 408 409 ((struct sockaddr_in *)self->address)->sin_family = 410 [[self domain] socketDomain]; 411 ((struct sockaddr_in *)self->address)->sin_port = 412 htons((short)(_port & 0xffff)); 413 } 414 return self; 415} 416 417- (id)initWithService:(NSString *)_serviceName onHost:(id)_host 418 protocol:(NSString *)_protocol 419{ 420 /* careful: the port in servent is in network byteorder! */ 421 NSException *exc = nil; 422 int port = -1; 423#if defined(HAVE_GETSERVBYNAME_R) 424 char buffer[2048]; 425 struct servent entry; 426#else 427 struct servent *entry; 428#endif 429 430#if defined(HAVE_GETSERVBYNAME_R) 431 if (getservbyname_r((char *)[_serviceName cString], [_protocol cString], 432 &entry, buffer, sizeof(buffer)) == NULL) { 433 exc = [[NGDidNotFindServiceException alloc] initWithServiceName:_serviceName]; 434 } 435 else 436 port = entry.s_port; 437#else 438 [systemLock lock]; 439 { 440 entry = getservbyname((char *)[_serviceName cString], [_protocol cString]); 441 if (entry == NULL) { 442 exc = [[NGDidNotFindServiceException alloc] 443 initWithServiceName:_serviceName]; 444 } 445 else 446 port = entry->s_port; 447 } 448 [systemLock unlock]; 449#endif 450 451 if (exc != nil) { 452 self = [self autorelease]; 453 [exc raise]; 454 return nil; 455 } 456 return [self initWithPort:ntohs(port) onHost:_host]; 457} 458 459- (id)initWithPort:(int)_port { 460 return [self initWithPort:_port onHost:_nameOfLocalhost()]; 461} 462 463- (id)initWithService:(NSString *)_serviceName protocol:(NSString *)_protocol { 464 return [self initWithService:_serviceName 465 onHost:_nameOfLocalhost() 466 protocol:_protocol]; 467} 468 469- (id)initWithDomain:(id)_domain 470 internalRepresentation:(void *)_representation 471 size:(int)_length 472{ 473 struct sockaddr_in *sockAddr = _representation; 474#if DEBUG 475 NSAssert(_length == sizeof(struct sockaddr_in), 476 @"invalid socket address length"); 477#else 478 if (_length != sizeof(struct sockaddr_in)) { 479 NSLog(@"%s: got invalid sockaddr_in size ...", __PRETTY_FUNCTION__); 480 [self release]; 481 return nil; 482 } 483#endif 484 485 if ((self = [self init]) == nil) 486 return nil; 487 488 self->isHostFilled = NO; /* need to lookup DNS */ 489 490 /* fill address */ 491 492 self->isAddressFilled = YES; 493 memcpy(self->address, _representation, sizeof(struct sockaddr_in)); 494 495 if (sockAddr->sin_addr.s_addr != 0) { 496 /* not a wildcard address */ 497 self->isWildcardHost = NO; 498 } 499 else { 500 /* wildcard address */ 501 self->hostName = nil; 502 self->isWildcardHost = YES; 503 self->isHostFilled = YES; /* wildcard host, no DNS lookup ... */ 504 } 505 506 return self; 507} 508 509- (void)dealloc { 510 [self->hostName release]; 511 if (self->address) free(self->address); 512 [super dealloc]; 513} 514 515/* accessors */ 516 517- (NSString *)hostName { 518 if (!self->isHostFilled) [self _fillHost]; 519 return [[self->hostName copy] autorelease]; 520} 521 522- (BOOL) _isLoopback { 523 if (!self->isAddressFilled) 524 [[self _fillAddress] raise]; 525#if defined(WIN32) && !defined(__CYGWIN32__) 526 u_long *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 527#else 528 unsigned int *ia = &(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 529#endif 530 return ((((long int) (ntohl(*ia))) & 0xff000000) == 0x7f000000); 531} 532 533- (NSString *)address { 534#if defined(WIN32) && !defined(__CYGWIN32__) 535 u_long *ia; 536 ia = (u_long *)&(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 537#else 538 unsigned long ia; 539 ia = (unsigned long) 540 &(((struct sockaddr_in *)self->address)->sin_addr.s_addr); 541#endif 542 543 if (self->hostName == nil) /* wildcard */ 544 return nil; 545 546 if (!self->isAddressFilled) 547 [[self _fillAddress] raise]; 548 549 { 550 char *ptr = NULL; 551 NSString *str = nil; 552 553 [systemLock lock]; 554 { 555 ptr = inet_ntoa(*((struct in_addr *)ia)); 556 str = [NSString stringWithCString:ptr]; 557 } 558 [systemLock unlock]; 559 560 return str; 561 } 562} 563 564- (int)port { 565 /* how to do ? */ 566 if (!self->isAddressFilled) 567 [[self _fillAddress] raise]; 568 return ntohs(((struct sockaddr_in *)self->address)->sin_port); 569} 570 571- (BOOL)isWildcardAddress { 572 if (self->isWildcardHost) return YES; 573 return ([self hostName] == nil) || ([self port] == 0); 574} 575 576/* NGSocketAddress protocol */ 577 578- (void *)internalAddressRepresentation { 579 // throws 580 // NGCouldNotResolveHostNameException when a DNS lookup fails 581 582 if (!self->isAddressFilled) 583 [[self _fillAddress] raise]; 584 585 return self->address; 586} 587 588- (int)addressRepresentationSize { 589 return [[self domain] addressRepresentationSize]; 590} 591- (id)domain { 592 static id domain = nil; 593 if (domain == nil) domain = [[NGInternetSocketDomain domain] retain]; 594 return domain; 595} 596 597- (BOOL) isLocalhost { 598 NSString *normalized_hostname; 599 600 if (![self hostName]) 601 return NO; 602 603 if ([self _isLoopback]) 604 return YES; 605 // normalize the string 606 normalized_hostname = [[self hostName] lowercaseString]; 607 608 if ([normalized_hostname hasSuffix: @"."]) { 609 normalized_hostname = [normalized_hostname substringToIndex: [normalized_hostname length] - 1]; 610 } 611 612 if ([normalized_hostname isEqualToString: @"localhost6"] || 613 [normalized_hostname isEqualToString: @"localhost6.localdomain6"]) { 614 return YES; 615 } 616 if ([normalized_hostname isEqualToString: @"localhost"] || 617 [normalized_hostname isEqualToString: @"localhost.localdomain"] || 618 [normalized_hostname hasSuffix: @".localhost"]) { 619 return YES; 620 } 621 return NO; 622} 623 624/* comparing */ 625 626- (NSUInteger)hash { 627 return [self port]; 628} 629 630- (BOOL)isEqualToAddress:(NGInternetSocketAddress *)_otherAddress { 631 if (self == _otherAddress) 632 return YES; 633 if (![[_otherAddress hostName] isEqualToString:[self hostName]]) 634 return NO; 635 if ([_otherAddress port] != [self port]) 636 return NO; 637 return YES; 638} 639 640- (BOOL)isEqual:(id)_object { 641 if (_object == self) return YES; 642 if ([_object class] != [self class]) return NO; 643 return [self isEqualToAddress:_object]; 644} 645 646/* NSCopying */ 647 648- (id)copyWithZone:(NSZone *)_zone { 649 // socket addresses are immutable, therefore just retain self 650 return [self retain]; 651} 652 653/* NSCoding */ 654 655- (void)encodeWithCoder:(NSCoder *)_encoder { 656 int aPort = [self port]; 657 658 [_encoder encodeValueOfObjCType:@encode(int) at:&aPort]; 659 [_encoder encodeObject:[self hostName]]; 660} 661- (id)initWithCoder:(NSCoder *)_decoder { 662 int aPort; 663 id aHost; 664 665 [_decoder decodeValueOfObjCType:@encode(int) at:&aPort]; 666 aHost = [_decoder decodeObject]; 667 668 return [self initWithPort:aPort onHost:aHost]; 669} 670 671/* description */ 672 673- (NSString *)stringValue { 674 NSString *name; 675 676 if ((name = [self hostName]) == nil) 677 name = @"*"; 678 679 return [NSString stringWithFormat:@"%@:%i", name, [self port]]; 680} 681 682- (NSString *)description { 683 NSMutableString *ms; 684 id tmp; 685 686 ms = [NSMutableString stringWithCapacity:128]; 687 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])]; 688 689 if ((tmp = [self hostName]) != nil) 690 [ms appendFormat:@" host=%@", tmp]; 691 else 692 [ms appendString:@" *host"]; 693 694 if (!self->isAddressFilled) 695 [ms appendString:@" not-filled"]; 696 else 697 [ms appendFormat:@" port=%d", [self port]]; 698 699 [ms appendString:@">"]; 700 return ms; 701} 702 703@end /* NGInternetSocketAddress */ 704 705@implementation NGActiveSocket(NGInternetActiveSocket) 706 707+ (id)socketConnectedToPort:(int)_port onHost:(id)_host { 708 // this method calls +socketConnectedToAddress: with an 709 // NGInternetSocketAddress 710 711 return [self socketConnectedToAddress: 712 [NGInternetSocketAddress addressWithPort:_port onHost:_host]]; 713} 714 715- (BOOL)connectToPort:(int)_port onHost:(id)_host { 716 // this method calls -connectToAddress: with an NGInternetSocketAddress 717 718 return [self connectToAddress: 719 [NGInternetSocketAddress addressWithPort:_port onHost:_host]]; 720} 721 722@end /* NGActiveSocket(NGInternetActiveSocket) */ 723