]> err.no Git - sope/blob - sope-core/NGStreams/NGSocket.m
added a new mkdirs like method to NSFileManager
[sope] / sope-core / NGStreams / NGSocket.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 <NGStreams/NGConcreteStreamFileHandle.h>
24 #include "NGSocketExceptions.h"
25 #include "NGSocket.h"
26 #include "NGSocket+private.h"
27 #include "NGInternetSocketDomain.h"
28
29 #include "config.h"
30 #if defined(__APPLE__)
31 #  include <sys/types.h>
32 #  include <sys/socket.h>
33 #endif
34
35 #if defined(HAVE_UNISTD_H) || defined(__APPLE__)
36 #  include <unistd.h>
37 #endif
38
39 #include "common.h"
40
41 @interface _NGConcreteSocketFileHandle : NGConcreteStreamFileHandle
42 {
43 }
44
45 - (id)initWithSocket:(id<NGSocket>)_socket;
46
47 @end
48
49 @interface NSObject(WildcardAddresses)
50 - (BOOL)isWildcardAddress;
51 @end
52
53 #ifdef __s390__
54 #  define SockAddrLenType socklen_t
55 #elif __APPLE__
56 #  define SockAddrLenType int
57 #else
58 #  define SockAddrLenType size_t
59 #endif
60
61 @implementation NGSocket
62
63 #if defined(WIN32) && !defined(__CYGWIN32__)
64
65 static BOOL    isInitialized = NO;
66 static WSADATA wsaData;
67
68 + (int)version {
69   return 2;
70 }
71
72 + (void)initialize {
73   if (!isInitialized) {
74     isInitialized = YES;
75
76     if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
77       NSLog(@"WARNING: Could not start Windows sockets !");
78
79     NSLog(@"WinSock version %i.%i.",
80           LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
81   }
82 }
83
84 static void _killWinSock(void) __attribute__((destructor));
85 static void _killWinSock(void) {
86   fprintf(stderr, "killing Windows sockets ..\n");
87   if (isInitialized) {
88     WSACleanup();
89     isInitialized = NO;
90   }
91 }
92
93 #endif /* WIN32 */
94
95 + (id)socketInDomain:(id<NGSocketDomain>)_domain {
96   return [[[self alloc] initWithDomain:_domain] autorelease];
97 }
98
99 - (id)init {
100   return [self initWithDomain:[NGInternetSocketDomain domain]];
101 }
102
103 #if defined(WIN32) && !defined(__CYGWIN32__)
104 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(SOCKET)_fd {
105 #else
106 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(int)_fd {
107 #endif
108   if ((self = [super init])) {
109     self->fd                = _fd;
110     self->flags.closeOnFree = YES;
111     self->flags.isBound     = (_fd == NGInvalidSocketDescriptor) ? NO : YES;
112     self->domain            = [_domain retain];
113
114     if (_fd == NGInvalidSocketDescriptor)
115       [self primaryCreateSocket];
116   }
117   return self;
118 }
119 - (id)initWithDomain:(id<NGSocketDomain>)_domain {
120   return [self _initWithDomain:_domain descriptor:NGInvalidSocketDescriptor];
121 }
122
123 - (void)gcFinalize {
124   if (self->flags.closeOnFree)
125     [self close];
126   else
127     NSLog(@"WARNING: socket was not 'closeOnFree' !");
128 }
129
130 - (void)dealloc {
131   [self gcFinalize];
132
133   [self->lastException release];
134   [self->localAddress  release];
135   [self->domain  release];
136   self->fileHandle = nil;
137   [super dealloc];
138 }
139
140 /* creation */
141
142 - (BOOL)primaryCreateSocket {
143   // throws
144   //   NGCouldNotCreateSocketException  if the socket creation failed
145   
146   fd = socket([domain socketDomain], [self socketType], [domain protocol]);
147
148 #if defined(WIN32) && !defined(__CYGWIN32__)
149   if (fd == SOCKET_ERROR) { // error
150     int e = WSAGetLastError();
151     NSString *reason = nil;
152     
153     switch (e) {
154       case WSAEACCES:
155         reason = @"Not allowed to create socket of this type";
156         break;
157       case WSAEMFILE:
158         reason = @"Could not create socket: descriptor table is full";
159         break;
160       case WSAEPROTONOSUPPORT:
161         reason = @"Could not create socket: The protocol type or the specified "
162                  @"protocol  is  not  supported within this domain";
163         break;
164       default:
165         reason = [NSString stringWithFormat:@"Could not create socket: %s",
166                              strerror(e)];
167         break;
168     }
169 #else
170   if (fd == -1) { // error
171     int      e       = errno;
172     NSString *reason = nil;
173     
174     switch (e) {
175       case EACCES:
176         reason = @"Not allowed to create socket of this type";
177         break;
178       case EMFILE:
179         reason = @"Could not create socket: descriptor table is full";
180         break;
181       case ENOMEM:
182         reason = @"Could not create socket: Insufficient user memory available";
183         break;
184       case EPROTONOSUPPORT:
185         reason = @"Could not create socket: The protocol type or the specified "
186                  @"protocol  is  not  supported within this domain";
187         break;
188       default:
189         reason = [NSString stringWithFormat:@"Could not create socket: %s",
190                              strerror(e)];
191         break;
192     }
193 #endif
194
195     [[[NGCouldNotCreateSocketException alloc]
196               initWithReason:reason domain:domain] raise];
197     return NO;
198   }
199   return YES;
200 }
201
202 - (BOOL)close {
203   if (self->fd != NGInvalidSocketDescriptor) {
204 #if DEBUG && 0
205     NSLog(@"%@: closing socket fd %i", self, self->fd);
206 #endif
207 #if defined(WIN32) && !defined(__CYGWIN32__)
208     closesocket(self->fd);
209 #else
210     close(self->fd);
211 #endif
212     self->fd = NGInvalidSocketDescriptor;
213
214     if (self->flags.isBound) {
215       self->flags.isBound = NO;
216       [[self domain] cleanupAddress:self->localAddress
217                      afterCloseOfSocket:self];
218     }
219     else
220       self->flags.isBound = NO;
221   }
222   return YES;
223 }
224
225 /* operations */
226
227 - (void)setLastException:(NSException *)_exception {
228   /* NOTE: watch out for cycles !!! */
229   // THREAD
230   ASSIGN(self->lastException, _exception);
231 }
232 - (NSException *)lastException {
233   // THREAD
234   return self->lastException;
235 }
236 - (void)resetLastException {
237   // THREAD
238   ASSIGN(self->lastException,(id)nil);
239 }
240  
241 - (BOOL)primaryBindToAddress:(id<NGSocketAddress>)_address {
242   // throws
243   //   NGCouldNotBindSocketException    if the bind failed
244
245   [[self domain] prepareAddress:_address
246                  forBindWithSocket:self];
247
248   if (bind(fd,
249            (struct sockaddr *)[_address internalAddressRepresentation],
250            [_address addressRepresentationSize]) != 0) {
251     NSString *reason = nil;
252 #if defined(WIN32) && !defined(__CYGWIN32__)
253     int errorCode = WSAGetLastError();
254 #else    
255     int errorCode = errno;
256 #endif
257
258     switch (errorCode) {
259       default:
260         reason = [NSString stringWithCString:strerror(errorCode)];
261         break;
262     }
263
264     reason = [NSString stringWithFormat:@"Could not bind to address %@: %@",
265                          _address, reason];
266     
267     [[[NGCouldNotBindSocketException alloc]
268               initWithReason:reason socket:self address:_address] raise];
269     return NO;
270   }
271
272   /* bind was successful */
273   
274   ASSIGN(self->localAddress, _address);
275   self->flags.isBound = YES;
276   return YES;
277 }
278
279 - (BOOL)bindToAddress:(id<NGSocketAddress>)_address {
280   // throws
281   //   NGSocketAlreadyBoundException    if the socket is already bound
282   //   NGCouldNotCreateSocketException  if the socket creation failed
283   //   NGCouldNotBindSocketException    if the bind failed
284
285   // check whether socket is already bound (either manually or by the kernel)
286   if (flags.isBound) {
287     [[[NGSocketAlreadyBoundException alloc]
288               initWithReason:@"socket is already bound." socket:self] raise];
289   }
290
291   if (_address == nil) {
292     /* let kernel bind address */
293     return [self kernelBoundAddress];
294   }
295   
296   // perform bind
297   if (![self primaryBindToAddress:_address])
298     return NO;
299   
300   /* check for wildcard port */
301   
302   if ([_address respondsToSelector:@selector(isWildcardAddress)]) {
303     if ([(id)_address isWildcardAddress]) {
304       SockAddrLenType len = [[_address domain] addressRepresentationSize];
305       char data[len]; // struct sockaddr
306       
307       if (getsockname(fd, (void *)&data, &len) == 0) { // function is MT-safe
308         id<NGSocketAddress> boundAddr;
309         
310         boundAddr = [[_address domain]
311                                addressWithRepresentation:&(data[0])
312                                size:len];
313 #if 0
314         NSLog(@"got sock name (addr-len=%d, %s, %d) %@ ..",
315               len,
316               inet_ntoa( (((struct sockaddr_in *)(&data[0]))->sin_addr)),
317               ntohs(((struct sockaddr_in *)(&data[0]))->sin_port),
318               boundAddr);
319 #endif   
320         ASSIGN(self->localAddress, boundAddr);
321       }
322       else {
323         // could not get local socket name, THROW
324         NSLog(@"ERROR: couldn't resolve wildcard address %@", _address);
325       }
326     }
327   }
328   return YES;
329 }
330
331 - (BOOL)kernelBoundAddress {
332   SockAddrLenType len = [[self domain] addressRepresentationSize];
333   char   data[len];
334   
335   // check whether socket is already bound (either manually or by the kernel)
336   if (flags.isBound) {
337     [[[NGSocketAlreadyBoundException alloc]
338               initWithReason:@"socket is already bound." socket:self] raise];
339     return NO;
340   }
341   
342 #if 0
343   NSLog(@"socket: kernel bound address of %i in domain %@",
344         self->fd, [self domain]);
345 #endif
346   
347   if (getsockname(self->fd, (void *)&data, &len) != 0) { // function is MT-safe
348     // could not get local socket name, THROW
349     [[[NGSocketException alloc]
350          initWithReason:@"could not get local socket name" socket:self] raise];
351     return NO;
352   }
353
354   if (self->localAddress) { // release old address
355     [self->localAddress release];
356     self->localAddress = nil;
357   }
358   self->localAddress = [[self domain] addressWithRepresentation:(void *)data
359                                       size:len];
360   self->localAddress  = [self->localAddress retain];
361   self->flags.isBound = YES;
362   return YES;
363 }
364
365 /* accessors */
366
367 - (id<NGSocketAddress>)localAddress {
368   return self->localAddress;
369 }
370
371 - (BOOL)isBound {
372   return self->flags.isBound;
373 }
374
375 - (int)socketType {
376   [self subclassResponsibility:_cmd];
377   return -1;
378 }
379
380 - (id<NGSocketDomain>)domain {
381   return self->domain;
382 }
383
384 #if defined(WIN32) && !defined(__CYGWIN32__)
385 - (SOCKET)fileDescriptor {
386 #else 
387 - (int)fileDescriptor {
388 #endif
389   return self->fd;
390 }
391
392 - (void)resetFileHandle { // called by the NSFileHandle on dealloc
393   self->fileHandle = nil;
394 }
395 - (NSFileHandle *)fileHandle {
396   /* the filehandle will reset itself from the stream when being deallocated */
397   if (self->fileHandle == nil) {
398     self->fileHandle =
399       [(_NGConcreteSocketFileHandle *)[_NGConcreteSocketFileHandle alloc]
400                                           initWithSocket:self];
401   }
402   return [self->fileHandle autorelease];
403 }
404
405 /* options */
406
407 - (void)setOption:(int)_option level:(int)_level value:(void *)_value len:(int)_len {
408   if (setsockopt(fd, _level, _option, _value, _len) != 0) {
409     NSString *reason = nil;
410 #if defined(WIN32) && !defined(__CYGWIN32__)
411     int e = WSAGetLastError();
412
413    switch (e) {
414      case WSAEBADF:
415        reason = @"Could not set socket option, invalid file descriptor";
416        break;
417      case WSAEINVAL:
418        reason =
419          @"Could not set socket option, option is invalid or socket has been"
420          @"shut down";
421        break;
422      case WSAENOPROTOOPT:
423        reason = @"Could not set socket option, option is not supported by protocol";
424        break;
425      case WSAENOTSOCK:
426        reason = @"Could not set socket option, descriptor isn't a socket";
427        break;
428      default:
429        reason = [NSString stringWithFormat:@"Could not set socket option: %s",
430                             strerror(e)];
431        break;
432    }
433 #else
434     int e = errno;
435     
436     switch (e) {
437       case EBADF:
438         reason = @"Could not set socket option, invalid file descriptor";
439         break;
440       case EINVAL:
441         reason =
442           @"Could not set socket option, option is invalid or socket has been"
443           @"shut down";
444         break;
445       case ENOPROTOOPT:
446         reason = @"Could not set socket option, option is not supported by protocol";
447         break;
448       case ENOTSOCK:
449         reason = @"Could not set socket option, descriptor isn't a socket";
450         break;
451       default:
452         reason = [NSString stringWithFormat:@"Could not set socket option: %s",
453                              strerror(e)];
454         break;
455     }
456 #endif
457     [[[NGCouldNotSetSocketOptionException alloc]
458          initWithReason:reason option:_option level:_level] raise];
459   }
460 }
461 - (void)setOption:(int)_option value:(void *)_value len:(int)_len {
462   [self setOption:_option level:SOL_SOCKET value:_value len:_len];
463 }
464
465 - (void)getOption:(int)_option level:(int)_level value:(void *)_value
466   len:(int *)_len {
467   
468   if (getsockopt(fd, _level, _option, _value, _len) != 0) {
469     NSString *reason = nil;
470 #if defined(WIN32) && !defined(__CYGWIN32__)
471     int e = WSAGetLastError();
472     
473     switch (e) {
474       case WSAEBADF:
475         reason = @"Could not get socket option, invalid file descriptor";
476         break;
477       case WSAEINVAL:
478         reason =
479           @"Could not get socket option, option is invalid at the specified level";
480         break;
481       case WSAENOPROTOOPT:
482         reason = @"Could not get socket option, option is not supported by protocol";
483         break;
484       case WSAENOTSOCK:
485         reason = @"Could not get socket option, descriptor isn't a socket";
486         break;
487       case WSAEOPNOTSUPP:
488         reason =
489           @"Could not get socket option, operation is not supported by protocol";
490         break;
491       default:
492         reason = [NSString stringWithFormat:@"Could not get socket option: %s",
493                              strerror(e)];
494         break;
495     }
496 #else
497     int e = errno;
498     
499     switch (e) {
500       case EBADF:
501         reason = @"Could not get socket option, invalid file descriptor";
502         break;
503       case EINVAL:
504         reason =
505           @"Could not get socket option, option is invalid at the specified level";
506         break;
507       case ENOPROTOOPT:
508         reason = @"Could not get socket option, option is not supported by protocol";
509         break;
510       case ENOTSOCK:
511         reason = @"Could not get socket option, descriptor isn't a socket";
512         break;
513       case EOPNOTSUPP:
514         reason =
515           @"Could not get socket option, operation is not supported by protocol";
516         break;
517       default:
518         reason = [NSString stringWithFormat:@"Could not get socket option: %s",
519                              strerror(e)];
520         break;
521     }
522 #endif
523     [[[NGCouldNotGetSocketOptionException alloc]
524          initWithReason:reason option:_option level:_level] raise];
525   }
526 }
527 - (void)getOption:(int)_option value:(void *)_value len:(int *)_len {
528   [self getOption:_option level:SOL_SOCKET value:_value len:_len];
529 }
530
531 static int i_yes = 1;
532 static int i_no  = 0;
533
534 static inline void setBoolOption(id self, int _option, BOOL _flag) {
535   [self setOption:_option level:SOL_SOCKET
536         value:(_flag ? &i_yes : &i_no) len:4];
537 }
538 static inline BOOL getBoolOption(id self, int _option) {
539   int value, len;
540   [self getOption:_option level:SOL_SOCKET value:&value len:&len];
541   return (value ? YES : NO);
542 }
543
544 - (void)setDebug:(BOOL)_flag {
545   setBoolOption(self, SO_DEBUG, _flag);
546 }
547 - (BOOL)doesDebug {
548   return getBoolOption(self, SO_DEBUG);
549 }
550
551 - (void)setReuseAddress:(BOOL)_flag {
552   setBoolOption(self, SO_REUSEADDR, _flag);
553 }
554 - (BOOL)doesReuseAddress {
555   return getBoolOption(self, SO_REUSEADDR);
556 }
557
558 - (void)setKeepAlive:(BOOL)_flag {
559   setBoolOption(self, SO_KEEPALIVE, _flag);
560 }
561 - (BOOL)doesKeepAlive {
562   return getBoolOption(self, SO_KEEPALIVE);
563 }
564
565 - (void)setDontRoute:(BOOL)_flag {
566   setBoolOption(self, SO_DONTROUTE, _flag);
567 }
568 - (BOOL)doesNotRoute {
569   return getBoolOption(self, SO_DONTROUTE);
570 }
571
572 - (void)setSendBufferSize:(int)_size {
573   [self setOption:SO_SNDBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
574 }
575 - (int)sendBufferSize {
576   int size, len;
577   [self getOption:SO_SNDBUF level:SOL_SOCKET value:&size len:&len];
578   return size;
579 }
580
581 - (void)setReceiveBufferSize:(int)_size {
582   [self setOption:SO_RCVBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
583 }
584 - (int)receiveBufferSize {
585   int size, len;
586   [self getOption:SO_RCVBUF level:SOL_SOCKET value:&size len:&len];
587   return size;
588 }
589
590 - (int)getSocketError {
591   int error, len;
592   [self getOption:SO_ERROR level:SOL_SOCKET value:&error len:&len];
593   return error;
594 }
595
596 // description
597
598 - (NSString *)description {
599   return [NSString stringWithFormat:
600                      @"<%@[0x%08X]: fd=%i type=%i bound=%@ domain=%@>",
601                      NSStringFromClass([self class]), (unsigned)self,
602                      [self fileDescriptor],
603                      [self socketType],
604                      [self localAddress] ? [self localAddress] : (id)@"no",
605                      [self domain]
606                    ];
607 }
608
609 @end /* NGSocket */
610
611
612 @implementation _NGConcreteSocketFileHandle
613
614 - (id)initWithSocket:(id<NGSocket>)_socket {
615   return [super initWithStream:(id<NGStream>)_socket];
616 }
617
618 // accessors
619
620 - (int)fileDescriptor {
621   return [(NGSocket *)stream fileDescriptor];
622 }
623
624 @end /* _NGConcreteSocketFileHandle */
625
626 #if defined(WIN32) && !defined(__CYGWIN32__)
627
628 // Windows Descriptor functions
629
630 // ******************** Poll *********************
631
632 int NGPollDescriptor(SOCKET _fd, short _events, int _timeout) {
633   struct timeval timeout;
634   fd_set rSet;
635   fd_set wSet;
636   fd_set eSet;
637   int    result;
638   FD_ZERO(&rSet);
639   FD_ZERO(&wSet);
640   FD_ZERO(&eSet);
641
642   if (_events & POLLIN)  FD_SET(_fd, &rSet);
643   if (_events & POLLOUT) FD_SET(_fd, &wSet);
644   if (_events & POLLERR) FD_SET(_fd, &eSet);
645
646   timeout.tv_sec  = _timeout / 1000;
647   timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
648
649   do {
650     result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
651     if (result == -1) { // error
652       int e = WSAGetLastError();
653       if (e != WSAEINTR)
654         // only retry of interrupted or repeatable
655         break;
656     }
657   }
658   while (result == -1);
659
660   return (result < 0) ? -1 : result;
661 }
662
663 // ******************** Flags ********************
664
665 #if 0 
666 int NGGetDescriptorFlags(int _fd) {
667   int val;
668
669   val = fcntl(_fd, F_GETFL, 0);
670   if (val < 0)
671     [NGIOException raiseWithReason:@"could not get descriptor flags"];
672   return val;
673 }
674 void NGSetDescriptorFlags(int _fd, int _flags) {
675   if (fcntl(_fd, F_SETFL, _flags) == -1)
676     [NGIOException raiseWithReason:@"could not set descriptor flags"];
677 }
678
679 void NGAddDescriptorFlag (int _fd, int _flag) {
680   int val = NGGetDescriptorFlags(_fd);
681   NGSetDescriptorFlags(_fd, val | _flag);
682 }
683 #endif 
684
685 // ******************** NonBlocking IO ************
686
687 int NGDescriptorRecv(SOCKET _fd, char *_buf, int _len, int _flags, int _timeout) {
688   int errorCode;
689   int result;
690
691   result = recv(_fd, _buf, _len, _flags);
692   if (result == 0) return 0; // EOF
693
694   errorCode = errno;
695
696   if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
697 #if 0
698     struct pollfd pfd;
699     pfd.fd      = _fd;
700     pfd.events  = POLLRDNORM;
701     pfd.revents = 0;
702
703     do {
704       if ((result = poll(&pfd, 1, _timeout)) < 0) {
705         errorCode = errno;
706
707         // retry if interrupted
708         if ((errorCode != EINTR) && (errorCode != EAGAIN)) 
709           break;
710       }
711     }
712     while (result < 0);
713 #endif
714     result = 1;
715
716     if (result == 1) { // data waiting, try to read
717       result = recv(_fd, _buf, _len, _flags);
718       if (result == 0)
719         return 0; // EOF
720       else if (result == -1) {
721         errorCode = errno;
722
723         if (errorCode == WSAEWOULDBLOCK)
724           NSLog(@"WARNING: would block although descriptor was polled ..");
725       }
726     }
727     else if (result == 0) {
728       result = -2;
729     }
730     else
731       result = -1;
732   }
733
734   return result;
735 }
736
737 int NGDescriptorSend(SOCKET _fd, const char *_buf, int _len, int _flags,
738                      int _timeout) {
739   int errorCode;
740   int result;
741
742   result = send(_fd, _buf, _len, _flags);
743   if (result == 0) return 0; // EOF
744
745   errorCode = errno;
746
747   if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
748 #if 0
749     struct pollfd pfd;
750     pfd.fd      = _fd;
751     pfd.events  = POLLWRNORM;
752     pfd.revents = 0;
753
754     do {
755       if ((result = poll(&pfd, 1, _timeout)) < 0) {
756         errorCode = errno;
757
758         if (errorCode != WSAEINTR) // retry only if interrupted
759           break;
760       }
761     }
762     while (result < 0);
763 #endif
764     result = 1; // block ..
765
766     if (result == 1) { // data waiting, try to read
767       result = send(_fd, _buf, _len, _flags);
768       if (result == 0) return 0; // EOF
769     }
770     else if (result == 0) {
771 #if 0
772       NSLog(@"nonblock: send on %i timed out after %i milliseconds ..",
773              _fd, _timeout);
774 #endif
775       result = -2;
776     }
777     else
778       result = -1;
779   }
780
781   return result;
782 }
783
784 #endif /* WIN32 */