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