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