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 <sys/types.h> 23#include <netinet/in.h> 24#include <netinet/tcp.h> 25 26#include "config.h" 27 28#if defined(HAVE_UNISTD_H) || defined(__APPLE__) 29# include <unistd.h> 30#endif 31 32#ifdef HAVE_SYS_SELECT_H 33# include <sys/select.h> 34#endif 35#ifdef HAVE_SYS_FILIO_H 36# include <sys/filio.h> 37#endif 38#if defined(HAVE_SYS_IOCTL_H) 39# include <sys/ioctl.h> 40#endif 41#if defined(HAVE_TIME_H) || defined(__APPLE__) 42# include <time.h> 43#endif 44#if defined(HAVE_SYS_TIME_H) || defined(__APPLE__) 45# include <sys/time.h> 46#endif 47#if defined(HAVE_FCNTL_H) || defined(__APPLE__) 48# include <fcntl.h> 49#endif 50 51#if defined(__APPLE__) 52# include <sys/types.h> 53# include <sys/socket.h> 54# include <sys/ioctl.h> 55#endif 56 57#if HAVE_WINDOWS_H && !defined(__CYGWIN32__) 58# include <windows.h> 59#endif 60 61#if defined(WIN32) && !defined(__CYGWIN32__) 62# include <winsock.h> 63# define ioctl ioctlsocket 64#endif 65 66#include "common.h" 67 68#include <NGStreams/NGDescriptorFunctions.h> 69#include <NGStreams/NGLocalSocketAddress.h> 70#include <NGStreams/NGLocalSocketDomain.h> 71#include "NGActiveSocket.h" 72#include "NGSocketExceptions.h" 73#include "NGSocket+private.h" 74#include "common.h" 75 76#if !defined(POLLRDNORM) 77# define POLLRDNORM POLLIN 78#endif 79 80@interface NGActiveSocket(PrivateMethods) 81 82- (id)_initWithDescriptor:(int)_fd 83 localAddress:(id<NGSocketAddress>)_local 84 remoteAddress:(id<NGSocketAddress>)_remote; 85 86@end 87 88@implementation NGActiveSocket 89 90#if !defined(WIN32) || defined(__CYGWIN32__) 91 92+ (BOOL)socketPair:(id<NGSocket>[2])_pair { 93 int fds[2]; 94 NGLocalSocketDomain *domain; 95 96 _pair[0] = nil; 97 _pair[1] = nil; 98 99 domain = [NGLocalSocketDomain domain]; 100 if (socketpair([domain socketDomain], SOCK_STREAM, [domain protocol], 101 fds) == 0) { 102 NGActiveSocket *s1 = nil; 103 NGActiveSocket *s2 = nil; 104 NGLocalSocketAddress *address; 105 106 s1 = [[self alloc] _initWithDomain:domain descriptor:fds[0]]; 107 s2 = [[self alloc] _initWithDomain:domain descriptor:fds[1]]; 108 s1 = [s1 autorelease]; 109 s2 = [s2 autorelease]; 110 111 address = [NGLocalSocketAddress address]; 112 if ((s1 != nil) && (s2 != nil)) { 113 s1->mode = NGStreamMode_readWrite; 114 s1->receiveTimeout = 0.0; 115 s1->sendTimeout = 0.0; 116 ASSIGN(s1->remoteAddress, address); 117 s2->mode = NGStreamMode_readWrite; 118 s2->receiveTimeout = 0.0; 119 s2->sendTimeout = 0.0; 120 ASSIGN(s2->remoteAddress, address); 121 122 _pair[0] = s1; 123 _pair[1] = s2; 124 125 return YES; 126 } 127 else 128 return NO; 129 } 130 else { 131 int e = errno; 132 NSString *reason = nil; 133 134 switch (e) { 135 case EACCES: 136 reason = @"Not allowed to create socket of this type"; 137 break; 138 case ENOMEM: 139 reason = @"Could not create socket: Insufficient user memory available"; 140 break; 141 case EPROTONOSUPPORT: 142 reason = @"The protocol is not supported by the address family or " 143 @"implementation"; 144 break; 145 case EPROTOTYPE: 146 reason = @"The socket type is not supported by the protocol"; 147 break; 148 case EMFILE: 149 reason = @"Could not create socket: descriptor table is full"; 150 break; 151 case EOPNOTSUPP: 152 reason = @"The specified protocol does not permit creation of socket " 153 @"pairs"; 154 break; 155 156#if DEBUG 157 case 0: 158 NSLog(@"WARNING(%s): socketpair() call failed, but errno=0", 159 __PRETTY_FUNCTION__); 160#endif 161 default: 162 reason = [NSString stringWithFormat:@"Could not create socketpair: %s", 163 strerror(e)]; 164 break; 165 } 166 [[[NGCouldNotCreateSocketException alloc] 167 initWithReason:reason domain:domain] raise]; 168 return NO; 169 } 170} 171 172#endif 173 174+ (id)socketConnectedToAddress:(id<NGSocketAddress>)_address { 175 volatile id sock = [[self alloc] initWithDomain:[_address domain]]; 176 177 if (sock != nil) { 178 if (![sock connectToAddress:_address]) { 179 NSException *e; 180 /* 181 this method needs to raise the exception, since no object is returned 182 in which we could check the -lastException ... 183 */ 184 e = [[sock lastException] retain]; 185 [sock autorelease]; 186 [self release]; 187 e = [e autorelease]; 188 [e raise]; 189 return nil; 190 } 191 sock = [sock autorelease]; 192 } 193 return sock; 194} 195 196- (id)initWithDomain:(id<NGSocketDomain>)_domain { 197 // designated initializer 198 if ((self = [super initWithDomain:_domain])) { 199 self->mode = NGStreamMode_readWrite; 200 self->receiveTimeout = 0.0; 201 self->sendTimeout = 0.0; 202 } 203 return self; 204} 205 206- (id)_initWithDescriptor:(int)_fd 207 localAddress:(id<NGSocketAddress>)_local 208 remoteAddress:(id<NGSocketAddress>)_remote 209{ 210 if ((self = [self _initWithDomain:[_local domain] descriptor:_fd])) { 211 ASSIGN(self->localAddress, _local); 212 ASSIGN(self->remoteAddress, _remote); 213 self->mode = NGStreamMode_readWrite; 214 215#if !defined(WIN32) || defined(__CYGWIN32__) 216 NGAddDescriptorFlag(self->fd, O_NONBLOCK); 217#endif 218 } 219 return self; 220} 221 222- (void)dealloc { 223 [self->remoteAddress release]; 224 [super dealloc]; 225} 226 227/* operations */ 228 229- (NSException *)lastException { 230 return [super lastException]; 231} 232 233- (void)raise:(NSString *)_name reason:(NSString *)_reason { 234 Class clazz; 235 NSException *e; 236 237 clazz = NSClassFromString(_name); 238 NSAssert1(clazz, @"did not find exception class %@", _name); 239 240 e = [clazz alloc]; 241 if (_reason) { 242 if ([clazz instancesRespondToSelector:@selector(initWithReason:socket:)]) 243 e = [(id)e initWithReason:_reason socket:self]; 244 else if ([clazz instancesRespondToSelector:@selector(initWithStream:reason:)]) 245 e = [(id)e initWithStream:self reason:_reason]; 246 else if ([clazz instancesRespondToSelector:@selector(initWithSocket:)]) 247 e = [(id)e initWithSocket:self]; 248 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)]) 249 e = [(id)e initWithStream:self]; 250 else 251 e = [e initWithReason:_reason]; 252 } 253 else { 254 if ([clazz instancesRespondToSelector:@selector(initWithSocket:)]) 255 e = [(id)e initWithSocket:self]; 256 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)]) 257 e = [(id)e initWithStream:self]; 258 else 259 e = [e init]; 260 } 261 [self setLastException:e]; 262 [e release]; 263} 264- (void)raise:(NSString *)_name { 265 [self raise:_name reason:nil]; 266} 267 268- (BOOL)markNonblockingAfterConnect { 269#if !defined(WIN32) || defined(__CYGWIN32__) 270 // mark socket as non-blocking 271 return YES; 272#else 273 // on Win we only support blocking sockets right now ... 274 return NO; 275#endif 276} 277 278- (BOOL)primaryConnectToAddress:(id<NGSocketAddress>)_address { 279 // throws 280 // NGCouldNotConnectException if the the connect() call fails 281 282 [self resetLastException]; 283 284 if (connect(fd, 285 (struct sockaddr *)[_address internalAddressRepresentation], 286 [_address addressRepresentationSize]) != 0) { 287 NSString *reason = nil; 288 int errorCode = errno; 289 NSException *e; 290 291 switch (errorCode) { 292 case EACCES: 293 reason = @"search permission denied for element in path"; 294 break; 295#if defined(WIN32) && !defined(__CYGWIN32__) 296 case WSAEADDRINUSE: 297 reason = @"address already in use"; 298 break; 299 case WSAEADDRNOTAVAIL: 300 reason = @"address is not available on remote machine"; 301 break; 302 case WSAEAFNOSUPPORT: 303 reason = @"addresses in the specified family cannot be used with the socket"; 304 break; 305 case WSAEALREADY: 306 reason = @"a previous non-blocking attempt has not yet been completed"; 307 break; 308 case WSAEBADF: 309 reason = @"descriptor is invalid"; 310 break; 311 case WSAECONNREFUSED: 312 reason = @"connection refused"; 313 break; 314 case WSAEINTR: 315 reason = @"connect was interrupted"; 316 break; 317 case WSAEINVAL: 318 reason = @"the address length is invalid"; 319 break; 320 case WSAEISCONN: 321 reason = @"socket is already connected"; 322 break; 323 case WSAENETUNREACH: 324 reason = @"network is unreachable"; 325 break; 326 case WSAETIMEDOUT: 327 reason = @"timeout occured"; 328 break; 329#else 330 case EADDRINUSE: 331 reason = @"address already in use"; 332 break; 333 case EADDRNOTAVAIL: 334 reason = @"address is not available on remote machine"; 335 break; 336 case EAFNOSUPPORT: 337 reason = @"addresses in the specified family cannot be used with the socket"; 338 break; 339 case EALREADY: 340 reason = @"a previous non-blocking attempt has not yet been completed"; 341 break; 342 case EBADF: 343 reason = @"descriptor is invalid"; 344 break; 345 case ECONNREFUSED: 346 reason = @"connection refused"; 347 break; 348 case EINTR: 349 reason = @"connect was interrupted"; 350 break; 351 case EINVAL: 352 reason = @"the address length is invalid"; 353 break; 354 case EIO: 355 reason = @"an IO error occured"; 356 break; 357 case EISCONN: 358 reason = @"socket is already connected"; 359 break; 360 case ENETUNREACH: 361 reason = @"network is unreachable"; 362 break; 363 case ETIMEDOUT: 364 reason = @"timeout occured"; 365 break; 366#endif 367 368#if DEBUG 369 case 0: 370 NSLog(@"WARNING(%s): connect() call failed, but errno=0", 371 __PRETTY_FUNCTION__); 372#endif 373 374 default: 375 reason = [NSString stringWithCString:strerror(errorCode)]; 376 break; 377 } 378 379 reason = [NSString stringWithFormat:@"Could not connect to address %@: %@", 380 _address, reason]; 381 382 e = [[NGCouldNotConnectException alloc] 383 initWithReason:reason socket:self address:_address]; 384 [self setLastException:e]; 385 [e release]; 386 return NO; 387 } 388 389 /* connect was successful */ 390 391 ASSIGN(self->remoteAddress, _address); 392 393 if ([self markNonblockingAfterConnect]) { 394 /* mark socket as non-blocking */ 395 NGAddDescriptorFlag(self->fd, O_NONBLOCK); 396 NSAssert((NGGetDescriptorFlags(self->fd) & O_NONBLOCK), 397 @"could not enable non-blocking mode .."); 398 } 399 return YES; 400} 401 402- (BOOL)connectToAddress:(id<NGSocketAddress>)_address { 403 // throws 404 // NGSocketAlreadyConnectedException if the socket is already connected 405 // NGInvalidSocketDomainException if the remote domain != local domain 406 // NGCouldNotCreateSocketException if the socket creation failed 407 408 if ([self isConnected]) { 409 [[[NGSocketAlreadyConnectedException alloc] 410 initWithReason:@"Could not connected: socket is already connected" 411 socket:self] raise]; 412 return NO; 413 } 414 415 // check whether the remote address is in the same domain like the bound one 416 if (flags.isBound) { 417 if (![[localAddress domain] isEqual:[_address domain]]) { 418 [[[NGInvalidSocketDomainException alloc] 419 initWithReason:@"local and remote socket domains are different" 420 socket:self domain:[_address domain]] raise]; 421 return NO; 422 } 423 } 424 425 // connect, remote-address is non-nil if this returns 426 if (![self primaryConnectToAddress:_address]) 427 return NO; 428 429 // if the socket wasn't bound before (normal case), bind it now 430 if (!flags.isBound) 431 if (![self kernelBoundAddress]) return NO; 432 return YES; 433} 434 435- (void)_shutdownDuringOperation { 436 [self shutdown]; 437} 438 439- (BOOL)shutdown { 440 if (self->fd != NGInvalidSocketDescriptor) { 441 if (self->mode != NGStreamMode_undefined) { 442 if (shutdown(self->fd, SHUT_RDWR) == 0) 443 self->mode = NGStreamMode_undefined; 444 } 445 446 if (self->flags.closeOnFree) { 447#if defined(WIN32) && !defined(__CYGWIN32__) 448 if (closesocket(self->fd) == 0) { 449#else 450 if (close(self->fd) == 0) { 451#endif 452 self->fd = NGInvalidSocketDescriptor; 453 } 454 else { 455 NSLog(@"ERROR(%s): close of socket %@ (fd=%i) alive=%s failed: %s", 456 __PRETTY_FUNCTION__, 457 self, self->fd, [self isAlive] ? "YES" : "NO", strerror(errno)); 458 } 459 } else { 460 self->fd = NGInvalidSocketDescriptor; 461 } 462 ASSIGN(self->remoteAddress, (id)nil); 463 } 464 return YES; 465} 466 467- (BOOL)shutdownSendChannel { 468 if (NGCanWriteInStreamMode(self->mode)) { 469 shutdown(self->fd, SHUT_WR); 470 471 if (self->mode == NGStreamMode_readWrite) 472 self->mode = NGStreamMode_readOnly; 473 else { 474 self->mode = NGStreamMode_undefined; 475 if (self->flags.closeOnFree) { 476#if defined(WIN32) && !defined(__CYGWIN32__) 477 closesocket(self->fd); 478#else 479 close(self->fd); 480#endif 481 } 482 self->fd = NGInvalidSocketDescriptor; 483 } 484 } 485 return YES; 486} 487- (BOOL)shutdownReceiveChannel { 488 if (NGCanReadInStreamMode(self->mode)) { 489 shutdown(self->fd, SHUT_RD); 490 491 if (self->mode == NGStreamMode_readWrite) 492 self->mode = NGStreamMode_writeOnly; 493 else { 494 self->mode = NGStreamMode_undefined; 495 if (self->flags.closeOnFree) { 496#if defined(WIN32) && !defined(__CYGWIN32__) 497 closesocket(self->fd); 498#else 499 close(self->fd); 500#endif 501 } 502 self->fd = NGInvalidSocketDescriptor; 503 } 504 } 505 return YES; 506} 507 508// ******************** accessors ****************** 509 510- (id<NGSocketAddress>)remoteAddress { 511 return self->remoteAddress; 512} 513 514- (BOOL)isConnected { 515 return (self->remoteAddress != nil); 516} 517- (BOOL)isOpen { 518 return [self isConnected]; 519} 520 521- (int)socketType { 522 return SOCK_STREAM; 523} 524 525- (void)disableNagle:(BOOL)_disable { 526 int on; 527 528 if ([self isConnected]) { 529 on = _disable ? 1 : 0; 530 setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); 531 } 532} 533 534- (void)setSendTimeout:(NSTimeInterval)_timeout { 535 struct timeval tv; 536 537 if ([self isConnected]) { 538 tv.tv_sec = (int) _timeout; 539 tv.tv_usec = 0; 540 setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (struct timeval)); 541 } 542 self->sendTimeout = _timeout; 543} 544- (NSTimeInterval)sendTimeout { 545 return self->sendTimeout; 546} 547 548- (void)setReceiveTimeout:(NSTimeInterval)_timeout { 549 struct timeval tv; 550 551 if ([self isConnected]) { 552 tv.tv_sec = (int) _timeout; 553 tv.tv_usec = 0; 554 setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (struct timeval)); 555 } 556 self->receiveTimeout = _timeout; 557} 558- (NSTimeInterval)receiveTimeout { 559 return self->receiveTimeout; 560} 561 562- (BOOL)wouldBlockInMode:(NGStreamMode)_mode { 563 short events = 0; 564 565 if ((![self isConnected]) || (fd == NGInvalidSocketDescriptor)) 566 return NO; 567 568 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM; 569 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM; 570 571 // timeout of 0 means return immediatly 572 return (NGPollDescriptor([self fileDescriptor], events, 0) == 1 ? NO : YES); 573} 574 575- (int)waitForMode:(NGStreamMode)_mode timeout:(NSTimeInterval)_timeout { 576 short events = 0; 577 578 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM; 579 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM; 580 581 // timeout of 0 means return immediatly 582 return NGPollDescriptor([self fileDescriptor], events, 583 (int)(_timeout * 1000.0)); 584} 585 586- (unsigned)numberOfAvailableBytesForReading { 587 int len; 588 589 // need to check whether socket is connected 590 if (self->remoteAddress == nil) { 591 [self raise:@"NGSocketNotConnectedException" 592 reason:@"socket is not connected"]; 593 return NGStreamError; 594 } 595 596 if (!NGCanReadInStreamMode(self->mode)) { 597 [self raise:@"NGWriteOnlyStreamException"]; 598 return NGStreamError; 599 } 600 601#if !defined(WIN32) && !defined(__CYGWIN32__) 602 while (ioctl(self->fd, FIONREAD, &len) == -1) { 603 if (errno == EINTR) continue; 604 605 [self raise:@"NGSocketException" 606 reason:@"could not get number of available bytes"]; 607 return NGStreamError; 608 } 609#else 610 // PeekNamedPipe() on Win ... 611 len = 0; 612#endif 613 return len; 614} 615 616- (BOOL)isAlive { 617 if (self->fd == NGInvalidSocketDescriptor) 618 return NO; 619 620 /* poll socket for input */ 621#if 1 622 { 623 struct pollfd pfd; 624 int ret, timeout = 5; 625 pfd.fd = self->fd; 626 pfd.events = POLLIN; 627 628 while (YES) { 629 ret = poll(&pfd, 1, timeout); 630 if (ret >= 0) 631 break; 632 633 switch (errno) { 634 case EINTR: 635 continue; 636 default: 637 NSLog(@"socket poll() failed: %s", strerror(errno)); 638 goto notAlive; 639 } 640 } 641 642 /* no input is pending, connection is alive */ 643 if (!(pfd.revents & POLLIN)) { 644 return YES; 645 } 646 } 647#else 648 { 649 struct timeval to; 650 fd_set readMask; 651 652 while (YES) { 653 FD_ZERO(&readMask); 654 FD_SET(self->fd, &readMask); 655 to.tv_sec = to.tv_usec = 0; 656 657 if (select(self->fd + 1, &readMask, NULL, NULL, &to) >= 0) 658 break; 659 660 switch (errno) { 661 case EINTR: 662 continue; 663 default: 664 NSLog(@"socket select() failed: %s", strerror(errno)); 665 goto notAlive; 666 } 667 } 668 669 /* no input is pending, connection is alive */ 670 if (!FD_ISSET(self->fd, &readMask)) 671 return YES; 672 } 673#endif 674 675 /* 676 input is pending: If select() indicates pending input, but ioctl() 677 indicates zero bytes of pending input, the connection is broken 678 */ 679 { 680#if defined(WIN32) && !defined(__CYGWIN32__) 681 u_long len; 682#else 683 int len; 684#endif 685 while (ioctl(self->fd, FIONREAD, &len) == -1) { 686 if (errno == EINTR) continue; 687 goto notAlive; 688 } 689 if (len > 0) return YES; 690 } 691 692 notAlive: 693 /* valid descriptor, but not alive .. so we close the socket */ 694#if defined(WIN32) && !defined(__CYGWIN32__) 695 closesocket(self->fd); 696#else 697 close(self->fd); 698#endif 699 self->fd = NGInvalidSocketDescriptor; 700 DESTROY(self->remoteAddress); 701 return NO; 702} 703 704// ******************** NGStream ******************** 705 706- (unsigned)readBytes:(void *)_buf count:(unsigned)_len { 707 // throws 708 // NGStreamReadErrorException when the read call failed 709 // NGSocketNotConnectedException when the socket is not connected 710 // NGEndOfStreamException when the end of the stream is reached 711 // NGWriteOnlyStreamException when the receive channel was shutdown 712 NSException *e = nil; 713 714 if (self->fd == NGInvalidSocketDescriptor) { 715 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"]; 716 return NGStreamError; 717 } 718 719 // need to check whether socket is connected 720 if (self->remoteAddress == nil) { 721 [self raise:@"NGSocketNotConnectedException" 722 reason:@"socket is not connected"]; 723 return NGStreamError; 724 } 725 726 if (!NGCanReadInStreamMode(self->mode)) { 727 [self raise:@"NGWriteOnlyStreamException"]; 728 return NGStreamError; 729 } 730 731 if (_len == 0) return 0; 732 733 { 734#if defined(WIN32) && !defined(__CYGWIN32__) 735 int readResult; 736 737 readResult = recv(self->fd, _buf, _len, 0); 738 if (readResult == 0) { 739 [self _shutdownDuringOperation]; 740 [self raise:@"NGSocketShutdownDuringReadException"]; 741 return NGStreamError; 742 } 743 else if (readResult < 0) { 744 int errorCode = WSAGetLastError(); 745 746 switch (errorCode) { 747 case WSAECONNRESET: 748 e = [[NGSocketConnectionResetException alloc] initWithStream:self]; 749 break; 750 case WSAETIMEDOUT: 751 e = [[NGSocketTimedOutException alloc] initWithStream:self]; 752 break; 753 754 case WSAEWOULDBLOCK: 755 NSLog(@"WARNING: descriptor would block .."); 756 757 default: 758 e = [[NGStreamReadErrorException alloc] 759 initWithStream:self errorCode:errorCode]; 760 break; 761 } 762 if (e) { 763 [self setLastException:e]; 764 [e release]; 765 return NGStreamError; 766 } 767 } 768#else /* !WIN32 */ 769 int readResult; 770 771 NSAssert(_buf, @"invalid buffer"); 772 NSAssert1(_len > 0, @"invalid length: %i", _len); 773 retry: 774 readResult = NGDescriptorRecv(self->fd, _buf, _len, 0, 775 (self->receiveTimeout == 0.0) 776 ? -1 // block until data 777 : (int)(self->receiveTimeout * 1000.0)); 778#if DEBUG 779 if ((readResult < 0) && (errno == EINVAL)) { 780 NSLog(@"%s: invalid argument in NGDescriptorRecv(%i, 0x%p, %i, %i)", 781 __PRETTY_FUNCTION__, 782 self->fd, _buf, _len, 0); 783 } 784#endif 785 786 if (readResult == 0) { 787 [self _shutdownDuringOperation]; 788 [self raise:@"NGSocketShutdownDuringReadException"]; 789 return NGStreamError; 790 } 791 else if (readResult == -2) { 792 [self raise:@"NGSocketTimedOutException"]; 793 return NGStreamError; 794 } 795 else if (readResult < 0) { 796 int errorCode = errno; 797 798 e = nil; 799 switch (errorCode) { 800 case 0: 801#if DEBUG 802 /* this happens with the Oracle7 adaptor !!! */ 803 NSLog(@"WARNING(%s): readResult<0 (%i), but errno=0 - retry", 804 __PRETTY_FUNCTION__, readResult); 805#endif 806 goto retry; 807 break; 808 809 case ECONNRESET: 810 e = [[NGSocketConnectionResetException alloc] initWithStream:self]; 811 break; 812 case ETIMEDOUT: 813 e = [[NGSocketTimedOutException alloc] initWithStream:self]; 814 break; 815 816 case EWOULDBLOCK: 817 NSLog(@"WARNING: descriptor would block .."); 818 819 default: 820 e = [[NGStreamReadErrorException alloc] 821 initWithStream:self errorCode:errorCode]; 822 break; 823 } 824 if (e) { 825 [self setLastException:e]; 826 [e release]; 827 return NGStreamError; 828 } 829 } 830#endif /* !WIN32 */ 831 return readResult; 832 } 833} 834 835#if defined(WIN32) && !defined(__CYGWIN32__) 836#warning fix exception handling 837 838- (unsigned)_winWriteBytes:(const void *)_buf count:(unsigned)_len { 839 NSException *e = nil; 840 int writeResult; 841 842 writeResult = send(self->fd, _buf, _len, 0); 843 844 if (writeResult == 0) { 845 [self _shutdownDuringOperation]; 846 [self raise:@"NGSocketShutdownDuringWriteException"]; 847 return NGStreamError; 848 } 849 else if (writeResult < 0) { 850 int errorCode = WSAGetLastError(); 851 852 switch (errorCode) { 853 case WSAECONNRESET: 854 e = [[NGSocketConnectionResetException alloc] initWithStream:self]; 855 break; 856 case WSAETIMEDOUT: 857 e = [[NGSocketTimedOutException alloc] initWithStream:self]; 858 break; 859 860 case WSAEWOULDBLOCK: 861 NSLog(@"WARNING: descriptor would block .."); 862 863 default: 864 e = [[NGStreamWriteErrorException alloc] 865 initWithStream:self errorCode:errno]; 866 break; 867 } 868 if (e) { 869 [self setLastException:e]; 870 [e release]; 871 return NGStreamError; 872 } 873 } 874 return writeResult; 875} 876 877#else 878 879- (unsigned)_unixWriteBytes:(const void *)_buf count:(unsigned)_len { 880 int writeResult; 881 int timeOut; 882 int retryCount; 883 884 retryCount = 0; 885 timeOut = (self->sendTimeout == 0.0) 886 ? -1 // block until data 887 : (int)(self->sendTimeout * 1000.0); 888 889 wretry: 890 writeResult = NGDescriptorSend(self->fd, _buf, _len, MSG_NOSIGNAL, timeOut); 891 892 if (writeResult == 0) { 893 [self _shutdownDuringOperation]; 894 [self raise:@"NGSocketShutdownDuringWriteException"]; 895 return NGStreamError; 896 } 897 else if (writeResult == -2) { 898 [self raise:@"NGSocketTimedOutException"]; 899 return NGStreamError; 900 } 901 else if (writeResult < 0) { 902 int errorCode = errno; 903 904 switch (errorCode) { 905 case 0: 906#if DEBUG 907 /* this happens with the Oracle7 (on SuSE < 7.1??) adaptor !!! */ 908 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - retry", 909 __PRETTY_FUNCTION__, writeResult); 910#endif 911 retryCount++; 912 if (retryCount > 200000) { 913 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - cancel retry " 914 @"(already tried %i times !!!)", 915 __PRETTY_FUNCTION__, writeResult, retryCount); 916 [self _shutdownDuringOperation]; 917 [self raise:@"NGSocketShutdownDuringWriteException"]; 918 return NGStreamError; 919 break; 920 } 921 sleep(retryCount); 922 goto wretry; 923 break; 924 925 case ECONNRESET: 926 [self raise:@"NGSocketConnectionResetException"]; 927 return NGStreamError; 928 case ETIMEDOUT: 929 [self raise:@"NGSocketTimedOutException"]; 930 return NGStreamError; 931 932 case EPIPE: 933 [self _shutdownDuringOperation]; 934 [self raise:@"NGSocketShutdownDuringWriteException"]; 935 return NGStreamError; 936 937 case EWOULDBLOCK: 938 NSLog(@"WARNING: descriptor would block .."); 939 940 default: { 941 NSException *e; 942 e = [[NGStreamWriteErrorException alloc] 943 initWithStream:self errorCode:errno]; 944 [self setLastException:e]; 945 [e release]; 946 return NGStreamError; 947 } 948 } 949 } 950 return writeResult; 951} 952 953#endif 954 955- (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len { 956 // throws 957 // NGStreamWriteErrorException when the write call failed 958 // NGSocketNotConnectedException when the socket is not connected 959 // NGReadOnlyStreamException when the send channel was shutdown 960 961 if (_len == NGStreamError) { 962 NSLog(@"ERROR(%s): got NGStreamError passed in as length ...", 963 __PRETTY_FUNCTION__); 964 return NGStreamError; 965 } 966#if DEBUG 967 if (_len > (1024 * 1024 * 100 /* 100MB */)) { 968 NSLog(@"WARNING(%s): got passed in length %uMB (%u bytes, errcode=%u) ...", 969 __PRETTY_FUNCTION__, (_len / 1024 / 1024), _len, NGStreamError); 970 } 971#endif 972 973 if (self->fd == NGInvalidSocketDescriptor) { 974 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"]; 975 return NGStreamError; 976 } 977 978 // need to check whether socket is connected 979 if (self->remoteAddress == nil) { 980 [self raise:@"NGSocketNotConnectedException" 981 reason:@"socket is not connected"]; 982 return NGStreamError; 983 } 984 985 if (!NGCanWriteInStreamMode(self->mode)) { 986 [self raise:@"NGReadOnlyStreamException"]; 987 return NGStreamError; 988 } 989 990 //NSLog(@"writeBytes: count:%u", _len); 991 992#if defined(WIN32) && !defined(__CYGWIN32__) 993 return [self _winWriteBytes:_buf count:_len]; 994#else 995 return [self _unixWriteBytes:_buf count:_len]; 996#endif 997} 998 999- (BOOL)flush { 1000 return YES; 1001} 1002#if 0 1003- (BOOL)close { 1004 return [self shutdown]; 1005} 1006#endif 1007 1008- (NGStreamMode)mode { 1009 return self->mode; 1010} 1011 1012/* methods method which write exactly _len bytes or fail */ 1013 1014- (BOOL)safeReadBytes:(void *)_buf count:(unsigned)_len { 1015 volatile int toBeRead; 1016 int readResult; 1017 void *pos; 1018 unsigned (*readBytes)(id, SEL, void *, unsigned); 1019 1020 *(&readBytes) = (void *)[self methodForSelector:@selector(readBytes:count:)]; 1021 *(&toBeRead) = _len; 1022 *(&readResult) = 0; 1023 *(&pos) = _buf; 1024 1025 while (YES) { 1026 *(&readResult) = 1027 readBytes(self, @selector(readBytes:count:), pos, toBeRead); 1028 1029 if (readResult == NGStreamError) { 1030 NSException *localException; 1031 NSData *data; 1032 1033 data = [NSData dataWithBytes:_buf length:(_len - toBeRead)]; 1034 1035 localException = [[NGEndOfStreamException alloc] 1036 initWithStream:self 1037 readCount:(_len - toBeRead) 1038 safeCount:_len 1039 data:data]; 1040 [self setLastException:localException]; 1041 RELEASE(localException); 1042 } 1043 1044 NSAssert(readResult != 0, @"ERROR: readBytes may not return '0' .."); 1045 1046 if (readResult == toBeRead) { 1047 // all bytes were read successfully, return 1048 break; 1049 } 1050 1051 if (readResult < 1) { 1052 [NSException raise:NSInternalInconsistencyException 1053 format:@"readBytes:count: returned a value < 1"]; 1054 } 1055 1056 toBeRead -= readResult; 1057 pos += readResult; 1058 } 1059 1060 return YES; 1061} 1062 1063- (BOOL)safeWriteBytes:(const void *)_buf count:(unsigned)_len { 1064 int toBeWritten = _len; 1065 int writeResult; 1066 void *pos = (void *)_buf; 1067 1068 /* method cache (THREAD, reentrant) */ 1069 static Class lastClass = Nil; 1070 static int (*writeBytes)(id,SEL,const void*,unsigned) = NULL; 1071 1072 if (lastClass == *(Class *)self) { 1073 if (writeBytes == NULL) 1074 writeBytes = 1075 (void *)[self methodForSelector:@selector(writeBytes:count:)]; 1076 } 1077 else { 1078 lastClass = *(Class *)self; 1079 writeBytes = (void *)[self methodForSelector:@selector(writeBytes:count:)]; 1080 } 1081 1082 while (YES) { 1083 writeResult = 1084 (int)writeBytes(self, @selector(writeBytes:count:), pos, toBeWritten); 1085 1086 if (writeResult == NGStreamError) { 1087 /* remember number of written bytes ??? */ 1088 return NO; 1089 } 1090 else if (writeResult == toBeWritten) { 1091 // all bytes were written successfully, return 1092 break; 1093 } 1094 1095 if (writeResult < 1) { 1096 [NSException raise:NSInternalInconsistencyException 1097 format:@"writeBytes:count: returned a value < 1 in stream %@", 1098 self]; 1099 return NO; 1100 } 1101 1102 toBeWritten -= writeResult; 1103 pos += writeResult; 1104 } 1105 return YES; 1106} 1107 1108- (BOOL)mark { 1109 return NO; 1110} 1111- (BOOL)rewind { 1112 [self raise:@"NGStreamException" reason:@"stream doesn't support a mark"]; 1113 return NO; 1114} 1115- (BOOL)markSupported { 1116 return NO; 1117} 1118 1119// convenience methods 1120 1121- (int)readByte { // java semantics (-1 returned on EOF) 1122 int result; 1123 unsigned char c; 1124 1125 result = [self readBytes:&c count:sizeof(unsigned char)]; 1126 1127 if (result != 1) { 1128 static Class EOFExcClass = Nil; 1129 1130 if (EOFExcClass == Nil) 1131 EOFExcClass = [NGEndOfStreamException class]; 1132 1133 if ([[self lastException] isKindOfClass:EOFExcClass]) 1134 [self resetLastException]; 1135 1136 return -1; 1137 } 1138 return (int)c; 1139} 1140 1141/* description */ 1142 1143- (NSString *)modeDescription { 1144 NSString *result = @"<unknown>"; 1145 1146 switch ([self mode]) { 1147 case NGStreamMode_undefined: result = @"<closed>"; break; 1148 case NGStreamMode_readOnly: result = @"r"; break; 1149 case NGStreamMode_writeOnly: result = @"w"; break; 1150 case NGStreamMode_readWrite: result = @"rw"; break; 1151 default: 1152 [[[NGUnknownStreamModeException alloc] initWithStream:self] raise]; 1153 break; 1154 } 1155 return result; 1156} 1157 1158- (NSString *)description { 1159 NSMutableString *d = [NSMutableString stringWithCapacity:64]; 1160 1161 [d appendFormat:@"<%@[0x%p]: mode=%@ address=%@", 1162 NSStringFromClass([self class]), self, 1163 [self modeDescription], [self localAddress]]; 1164 1165 if ([self isConnected]) 1166 [d appendFormat:@" connectedTo=%@", [self remoteAddress]]; 1167 1168 if ([self sendTimeout] != 0.0) 1169 [d appendFormat:@" send-timeout=%4.3fs", [self sendTimeout]]; 1170 if ([self receiveTimeout] != 0.0) 1171 [d appendFormat:@" receive-timeout=%4.3fs", [self receiveTimeout]]; 1172 1173 [d appendString:@">"]; 1174 return d; 1175} 1176 1177@end /* NGActiveSocket */ 1178 1179@implementation NGActiveSocket(DataMethods) 1180 1181- (NSData *)readDataOfLength:(unsigned int)_length { 1182 unsigned readCount; 1183 char buf[_length]; 1184 1185 if (_length == 0) return [NSData data]; 1186 1187 readCount = [self readBytes:buf count:_length]; 1188 return [NSData dataWithBytes:buf length:readCount]; 1189} 1190 1191- (NSData *)safeReadDataOfLength:(unsigned int)_length { 1192 char buf[_length]; 1193 1194 if (_length == 0) return [NSData data]; 1195 [self safeReadBytes:buf count:_length]; 1196 return [NSData dataWithBytes:buf length:_length]; 1197} 1198 1199- (unsigned int)writeData:(NSData *)_data { 1200 return [self writeBytes:[_data bytes] count:[_data length]]; 1201} 1202- (BOOL)safeWriteData:(NSData *)_data { 1203 return [self safeWriteBytes:[_data bytes] count:[_data length]]; 1204} 1205 1206@end /* NGActiveSocket(DataMethods) */ 1207 1208#include <NGStreams/NGBufferedStream.h> 1209 1210@implementation NGBufferedStream(FastSocketForwarders) 1211 1212- (BOOL)isConnected { 1213 return [(id)self->source isConnected]; 1214} 1215- (int)fileDescriptor { 1216 return [(NSFileHandle *)self->source fileDescriptor]; 1217} 1218 1219@end /* NGBufferedStream(FastSocketForwarders) */ 1220