2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include <NGStreams/NGConcreteStreamFileHandle.h>
23 #include "NGSocketExceptions.h"
25 #include "NGSocket+private.h"
26 #include "NGInternetSocketDomain.h"
29 #if defined(__APPLE__)
30 # include <sys/types.h>
31 # include <sys/socket.h>
34 #if defined(HAVE_UNISTD_H) || defined(__APPLE__)
40 @interface _NGConcreteSocketFileHandle : NGConcreteStreamFileHandle
44 - (id)initWithSocket:(id<NGSocket>)_socket;
48 @interface NSObject(WildcardAddresses)
49 - (BOOL)isWildcardAddress;
53 # define SockAddrLenType socklen_t
55 # define SockAddrLenType unsigned int
57 # define SockAddrLenType size_t
60 @implementation NGSocket
62 #if defined(WIN32) && !defined(__CYGWIN32__)
64 static BOOL isInitialized = NO;
65 static WSADATA wsaData;
75 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
76 NSLog(@"WARNING: Could not start Windows sockets !");
78 NSLog(@"WinSock version %i.%i.",
79 LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
83 static void _killWinSock(void) __attribute__((destructor));
84 static void _killWinSock(void) {
85 fprintf(stderr, "killing Windows sockets ..\n");
94 + (id)socketInDomain:(id<NGSocketDomain>)_domain {
95 return [[[self alloc] initWithDomain:_domain] autorelease];
99 return [self initWithDomain:[NGInternetSocketDomain domain]];
102 #if defined(WIN32) && !defined(__CYGWIN32__)
103 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(SOCKET)_fd {
105 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(int)_fd {
107 if ((self = [super init])) {
109 self->flags.closeOnFree = YES;
110 self->flags.isBound = (_fd == NGInvalidSocketDescriptor) ? NO : YES;
111 self->domain = [_domain retain];
113 if (_fd == NGInvalidSocketDescriptor)
114 [self primaryCreateSocket];
118 - (id)initWithDomain:(id<NGSocketDomain>)_domain {
119 return [self _initWithDomain:_domain descriptor:NGInvalidSocketDescriptor];
123 if (self->flags.closeOnFree)
126 NSLog(@"WARNING: socket was not 'closeOnFree' !");
132 [self->lastException release];
133 [self->localAddress release];
134 [self->domain release];
135 self->fileHandle = nil;
141 - (BOOL)primaryCreateSocket {
143 // NGCouldNotCreateSocketException if the socket creation failed
145 fd = socket([domain socketDomain], [self socketType], [domain protocol]);
147 #if defined(WIN32) && !defined(__CYGWIN32__)
148 if (fd == SOCKET_ERROR) { // error
149 int e = WSAGetLastError();
150 NSString *reason = nil;
154 reason = @"Not allowed to create socket of this type";
157 reason = @"Could not create socket: descriptor table is full";
159 case WSAEPROTONOSUPPORT:
160 reason = @"Could not create socket: The protocol type or the specified "
161 @"protocol is not supported within this domain";
164 reason = [NSString stringWithFormat:@"Could not create socket: %s",
169 if (fd == -1) { // error
171 NSString *reason = nil;
175 reason = @"Not allowed to create socket of this type";
178 reason = @"Could not create socket: descriptor table is full";
181 reason = @"Could not create socket: Insufficient user memory available";
183 case EPROTONOSUPPORT:
184 reason = @"Could not create socket: The protocol type or the specified "
185 @"protocol is not supported within this domain";
188 reason = [NSString stringWithFormat:@"Could not create socket: %s",
194 [[[NGCouldNotCreateSocketException alloc]
195 initWithReason:reason domain:domain] raise];
202 if (self->fd != NGInvalidSocketDescriptor) {
204 NSLog(@"%@: closing socket fd %i", self, self->fd);
206 #if defined(WIN32) && !defined(__CYGWIN32__)
207 closesocket(self->fd);
211 self->fd = NGInvalidSocketDescriptor;
213 if (self->flags.isBound) {
214 self->flags.isBound = NO;
215 [[self domain] cleanupAddress:self->localAddress
216 afterCloseOfSocket:self];
219 self->flags.isBound = NO;
226 - (void)setLastException:(NSException *)_exception {
227 /* NOTE: watch out for cycles !!! */
229 ASSIGN(self->lastException, _exception);
231 - (NSException *)lastException {
233 return self->lastException;
235 - (void)resetLastException {
237 ASSIGN(self->lastException,(id)nil);
240 - (BOOL)primaryBindToAddress:(id<NGSocketAddress>)_address {
242 // NGCouldNotBindSocketException if the bind failed
244 [[self domain] prepareAddress:_address
245 forBindWithSocket:self];
248 (struct sockaddr *)[_address internalAddressRepresentation],
249 [_address addressRepresentationSize]) != 0) {
250 NSString *reason = nil;
251 #if defined(WIN32) && !defined(__CYGWIN32__)
252 int errorCode = WSAGetLastError();
254 int errorCode = errno;
259 reason = [NSString stringWithCString:strerror(errorCode)];
263 reason = [NSString stringWithFormat:@"Could not bind to address %@: %@",
266 [[[NGCouldNotBindSocketException alloc]
267 initWithReason:reason socket:self address:_address] raise];
271 /* bind was successful */
273 ASSIGN(self->localAddress, _address);
274 self->flags.isBound = YES;
278 - (BOOL)bindToAddress:(id<NGSocketAddress>)_address {
280 // NGSocketAlreadyBoundException if the socket is already bound
281 // NGCouldNotCreateSocketException if the socket creation failed
282 // NGCouldNotBindSocketException if the bind failed
284 // check whether socket is already bound (either manually or by the kernel)
286 [[[NGSocketAlreadyBoundException alloc]
287 initWithReason:@"socket is already bound." socket:self] raise];
290 if (_address == nil) {
291 /* let kernel bind address */
292 return [self kernelBoundAddress];
296 if (![self primaryBindToAddress:_address])
299 /* check for wildcard port */
301 if ([_address respondsToSelector:@selector(isWildcardAddress)]) {
302 if ([(id)_address isWildcardAddress]) {
303 SockAddrLenType len = [[_address domain] addressRepresentationSize];
304 char data[len]; // struct sockaddr
306 if (getsockname(fd, (void *)&data, &len) == 0) { // function is MT-safe
307 id<NGSocketAddress> boundAddr;
309 boundAddr = [[_address domain]
310 addressWithRepresentation:&(data[0])
313 NSLog(@"got sock name (addr-len=%d, %s, %d) %@ ..",
315 inet_ntoa( (((struct sockaddr_in *)(&data[0]))->sin_addr)),
316 ntohs(((struct sockaddr_in *)(&data[0]))->sin_port),
319 ASSIGN(self->localAddress, boundAddr);
322 // could not get local socket name, THROW
323 NSLog(@"ERROR: couldn't resolve wildcard address %@", _address);
330 - (BOOL)kernelBoundAddress {
331 SockAddrLenType len = [[self domain] addressRepresentationSize];
334 // check whether socket is already bound (either manually or by the kernel)
336 [[[NGSocketAlreadyBoundException alloc]
337 initWithReason:@"socket is already bound." socket:self] raise];
342 NSLog(@"socket: kernel bound address of %i in domain %@",
343 self->fd, [self domain]);
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];
353 if (self->localAddress) { // release old address
354 [self->localAddress release];
355 self->localAddress = nil;
357 self->localAddress = [[self domain] addressWithRepresentation:(void *)data
359 self->localAddress = [self->localAddress retain];
360 self->flags.isBound = YES;
366 - (id<NGSocketAddress>)localAddress {
367 return self->localAddress;
371 return self->flags.isBound;
375 [self subclassResponsibility:_cmd];
379 - (id<NGSocketDomain>)domain {
383 #if defined(WIN32) && !defined(__CYGWIN32__)
384 - (SOCKET)fileDescriptor {
386 - (int)fileDescriptor {
391 - (void)resetFileHandle { // called by the NSFileHandle on dealloc
392 self->fileHandle = nil;
394 - (NSFileHandle *)fileHandle {
395 /* the filehandle will reset itself from the stream when being deallocated */
396 if (self->fileHandle == nil) {
398 [(_NGConcreteSocketFileHandle *)[_NGConcreteSocketFileHandle alloc]
399 initWithSocket:self];
401 return [self->fileHandle autorelease];
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();
414 reason = @"Could not set socket option, invalid file descriptor";
418 @"Could not set socket option, option is invalid or socket has been"
422 reason = @"Could not set socket option, option is not supported by protocol";
425 reason = @"Could not set socket option, descriptor isn't a socket";
428 reason = [NSString stringWithFormat:@"Could not set socket option: %s",
437 reason = @"Could not set socket option, invalid file descriptor";
441 @"Could not set socket option, option is invalid or socket has been"
445 reason = @"Could not set socket option, option is not supported by protocol";
448 reason = @"Could not set socket option, descriptor isn't a socket";
451 reason = [NSString stringWithFormat:@"Could not set socket option: %s",
456 [[[NGCouldNotSetSocketOptionException alloc]
457 initWithReason:reason option:_option level:_level] raise];
460 - (void)setOption:(int)_option value:(void *)_value len:(int)_len {
461 [self setOption:_option level:SOL_SOCKET value:_value len:_len];
464 - (void)getOption:(int)_option level:(int)_level value:(void *)_value
470 rc = getsockopt(fd, _level, _option, _value, &tlen);
471 if (_len) *_len = tlen;
473 NSString *reason = nil;
474 #if defined(WIN32) && !defined(__CYGWIN32__)
475 int e = WSAGetLastError();
479 reason = @"Could not get socket option, invalid file descriptor";
483 @"Could not get socket option, option is invalid at the specified level";
486 reason = @"Could not get socket option, option is not supported by protocol";
489 reason = @"Could not get socket option, descriptor isn't a socket";
493 @"Could not get socket option, operation is not supported by protocol";
496 reason = [NSString stringWithFormat:@"Could not get socket option: %s",
505 reason = @"Could not get socket option, invalid file descriptor";
509 @"Could not get socket option, option is invalid at the specified level";
512 reason = @"Could not get socket option, option is not supported by protocol";
515 reason = @"Could not get socket option, descriptor isn't a socket";
519 @"Could not get socket option, operation is not supported by protocol";
522 reason = [NSString stringWithFormat:@"Could not get socket option: %s",
527 [[[NGCouldNotGetSocketOptionException alloc]
528 initWithReason:reason option:_option level:_level] raise];
531 - (void)getOption:(int)_option value:(void *)_value len:(int *)_len {
532 [self getOption:_option level:SOL_SOCKET value:_value len:_len];
535 static int i_yes = 1;
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];
542 static inline BOOL getBoolOption(id self, int _option) {
544 [self getOption:_option level:SOL_SOCKET value:&value len:&len];
545 return (value ? YES : NO);
548 - (void)setDebug:(BOOL)_flag {
549 setBoolOption(self, SO_DEBUG, _flag);
552 return getBoolOption(self, SO_DEBUG);
555 - (void)setReuseAddress:(BOOL)_flag {
556 setBoolOption(self, SO_REUSEADDR, _flag);
558 - (BOOL)doesReuseAddress {
559 return getBoolOption(self, SO_REUSEADDR);
562 - (void)setKeepAlive:(BOOL)_flag {
563 setBoolOption(self, SO_KEEPALIVE, _flag);
565 - (BOOL)doesKeepAlive {
566 return getBoolOption(self, SO_KEEPALIVE);
569 - (void)setDontRoute:(BOOL)_flag {
570 setBoolOption(self, SO_DONTROUTE, _flag);
572 - (BOOL)doesNotRoute {
573 return getBoolOption(self, SO_DONTROUTE);
576 - (void)setSendBufferSize:(int)_size {
577 [self setOption:SO_SNDBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
579 - (int)sendBufferSize {
581 [self getOption:SO_SNDBUF level:SOL_SOCKET value:&size len:&len];
585 - (void)setReceiveBufferSize:(int)_size {
586 [self setOption:SO_RCVBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
588 - (int)receiveBufferSize {
590 [self getOption:SO_RCVBUF level:SOL_SOCKET value:&size len:&len];
594 - (int)getSocketError {
596 [self getOption:SO_ERROR level:SOL_SOCKET value:&error len:&len];
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],
608 [self localAddress] ? [self localAddress] : (id)@"no",
616 @implementation _NGConcreteSocketFileHandle
618 - (id)initWithSocket:(id<NGSocket>)_socket {
619 return [super initWithStream:(id<NGStream>)_socket];
624 - (int)fileDescriptor {
625 return [(NGSocket *)stream fileDescriptor];
628 @end /* _NGConcreteSocketFileHandle */
630 #if defined(WIN32) && !defined(__CYGWIN32__)
632 // Windows Descriptor functions
634 // ******************** Poll *********************
636 int NGPollDescriptor(SOCKET _fd, short _events, int _timeout) {
637 struct timeval timeout;
646 if (_events & POLLIN) FD_SET(_fd, &rSet);
647 if (_events & POLLOUT) FD_SET(_fd, &wSet);
648 if (_events & POLLERR) FD_SET(_fd, &eSet);
650 timeout.tv_sec = _timeout / 1000;
651 timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
654 result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
655 if (result == -1) { // error
656 int e = WSAGetLastError();
658 // only retry of interrupted or repeatable
662 while (result == -1);
664 return (result < 0) ? -1 : result;
667 // ******************** Flags ********************
670 int NGGetDescriptorFlags(int _fd) {
673 val = fcntl(_fd, F_GETFL, 0);
675 [NGIOException raiseWithReason:@"could not get descriptor flags"];
678 void NGSetDescriptorFlags(int _fd, int _flags) {
679 if (fcntl(_fd, F_SETFL, _flags) == -1)
680 [NGIOException raiseWithReason:@"could not set descriptor flags"];
683 void NGAddDescriptorFlag (int _fd, int _flag) {
684 int val = NGGetDescriptorFlags(_fd);
685 NGSetDescriptorFlags(_fd, val | _flag);
689 // ******************** NonBlocking IO ************
691 int NGDescriptorRecv(SOCKET _fd, char *_buf, int _len, int _flags, int _timeout) {
695 result = recv(_fd, _buf, _len, _flags);
696 if (result == 0) return 0; // EOF
700 if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
704 pfd.events = POLLRDNORM;
708 if ((result = poll(&pfd, 1, _timeout)) < 0) {
711 // retry if interrupted
712 if ((errorCode != EINTR) && (errorCode != EAGAIN))
720 if (result == 1) { // data waiting, try to read
721 result = recv(_fd, _buf, _len, _flags);
724 else if (result == -1) {
727 if (errorCode == WSAEWOULDBLOCK)
728 NSLog(@"WARNING: would block although descriptor was polled ..");
731 else if (result == 0) {
741 int NGDescriptorSend(SOCKET _fd, const char *_buf, int _len, int _flags,
746 result = send(_fd, _buf, _len, _flags);
747 if (result == 0) return 0; // EOF
751 if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
755 pfd.events = POLLWRNORM;
759 if ((result = poll(&pfd, 1, _timeout)) < 0) {
762 if (errorCode != WSAEINTR) // retry only if interrupted
768 result = 1; // block ..
770 if (result == 1) { // data waiting, try to read
771 result = send(_fd, _buf, _len, _flags);
772 if (result == 0) return 0; // EOF
774 else if (result == 0) {
776 NSLog(@"nonblock: send on %i timed out after %i milliseconds ..",