2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
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
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.
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
23 #include <NGStreams/NGConcreteStreamFileHandle.h>
24 #include "NGSocketExceptions.h"
26 #include "NGSocket+private.h"
27 #include "NGInternetSocketDomain.h"
30 #if defined(__APPLE__)
31 # include <sys/types.h>
32 # include <sys/socket.h>
35 #if defined(HAVE_UNISTD_H) || defined(__APPLE__)
41 @interface _NGConcreteSocketFileHandle : NGConcreteStreamFileHandle
45 - (id)initWithSocket:(id<NGSocket>)_socket;
49 @interface NSObject(WildcardAddresses)
50 - (BOOL)isWildcardAddress;
54 # define SockAddrLenType socklen_t
56 # define SockAddrLenType int
58 # define SockAddrLenType size_t
61 @implementation NGSocket
63 #if defined(WIN32) && !defined(__CYGWIN32__)
65 static BOOL isInitialized = NO;
66 static WSADATA wsaData;
76 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
77 NSLog(@"WARNING: Could not start Windows sockets !");
79 NSLog(@"WinSock version %i.%i.",
80 LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
84 static void _killWinSock(void) __attribute__((destructor));
85 static void _killWinSock(void) {
86 fprintf(stderr, "killing Windows sockets ..\n");
95 + (id)socketInDomain:(id<NGSocketDomain>)_domain {
96 return [[[self alloc] initWithDomain:_domain] autorelease];
100 return [self initWithDomain:[NGInternetSocketDomain domain]];
103 #if defined(WIN32) && !defined(__CYGWIN32__)
104 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(SOCKET)_fd {
106 - (id)_initWithDomain:(id<NGSocketDomain>)_domain descriptor:(int)_fd {
108 if ((self = [super init])) {
110 self->flags.closeOnFree = YES;
111 self->flags.isBound = (_fd == NGInvalidSocketDescriptor) ? NO : YES;
112 self->domain = [_domain retain];
114 if (_fd == NGInvalidSocketDescriptor)
115 [self primaryCreateSocket];
119 - (id)initWithDomain:(id<NGSocketDomain>)_domain {
120 return [self _initWithDomain:_domain descriptor:NGInvalidSocketDescriptor];
124 if (self->flags.closeOnFree)
127 NSLog(@"WARNING: socket was not 'closeOnFree' !");
133 [self->lastException release];
134 [self->localAddress release];
135 [self->domain release];
136 self->fileHandle = nil;
142 - (BOOL)primaryCreateSocket {
144 // NGCouldNotCreateSocketException if the socket creation failed
146 fd = socket([domain socketDomain], [self socketType], [domain protocol]);
148 #if defined(WIN32) && !defined(__CYGWIN32__)
149 if (fd == SOCKET_ERROR) { // error
150 int e = WSAGetLastError();
151 NSString *reason = nil;
155 reason = @"Not allowed to create socket of this type";
158 reason = @"Could not create socket: descriptor table is full";
160 case WSAEPROTONOSUPPORT:
161 reason = @"Could not create socket: The protocol type or the specified "
162 @"protocol is not supported within this domain";
165 reason = [NSString stringWithFormat:@"Could not create socket: %s",
170 if (fd == -1) { // error
172 NSString *reason = nil;
176 reason = @"Not allowed to create socket of this type";
179 reason = @"Could not create socket: descriptor table is full";
182 reason = @"Could not create socket: Insufficient user memory available";
184 case EPROTONOSUPPORT:
185 reason = @"Could not create socket: The protocol type or the specified "
186 @"protocol is not supported within this domain";
189 reason = [NSString stringWithFormat:@"Could not create socket: %s",
195 [[[NGCouldNotCreateSocketException alloc]
196 initWithReason:reason domain:domain] raise];
203 if (self->fd != NGInvalidSocketDescriptor) {
205 NSLog(@"%@: closing socket fd %i", self, self->fd);
207 #if defined(WIN32) && !defined(__CYGWIN32__)
208 closesocket(self->fd);
212 self->fd = NGInvalidSocketDescriptor;
214 if (self->flags.isBound) {
215 self->flags.isBound = NO;
216 [[self domain] cleanupAddress:self->localAddress
217 afterCloseOfSocket:self];
220 self->flags.isBound = NO;
227 - (void)setLastException:(NSException *)_exception {
228 /* NOTE: watch out for cycles !!! */
230 ASSIGN(self->lastException, _exception);
232 - (NSException *)lastException {
234 return self->lastException;
236 - (void)resetLastException {
238 ASSIGN(self->lastException,(id)nil);
241 - (BOOL)primaryBindToAddress:(id<NGSocketAddress>)_address {
243 // NGCouldNotBindSocketException if the bind failed
245 [[self domain] prepareAddress:_address
246 forBindWithSocket:self];
249 (struct sockaddr *)[_address internalAddressRepresentation],
250 [_address addressRepresentationSize]) != 0) {
251 NSString *reason = nil;
252 #if defined(WIN32) && !defined(__CYGWIN32__)
253 int errorCode = WSAGetLastError();
255 int errorCode = errno;
260 reason = [NSString stringWithCString:strerror(errorCode)];
264 reason = [NSString stringWithFormat:@"Could not bind to address %@: %@",
267 [[[NGCouldNotBindSocketException alloc]
268 initWithReason:reason socket:self address:_address] raise];
272 /* bind was successful */
274 ASSIGN(self->localAddress, _address);
275 self->flags.isBound = YES;
279 - (BOOL)bindToAddress:(id<NGSocketAddress>)_address {
281 // NGSocketAlreadyBoundException if the socket is already bound
282 // NGCouldNotCreateSocketException if the socket creation failed
283 // NGCouldNotBindSocketException if the bind failed
285 // check whether socket is already bound (either manually or by the kernel)
287 [[[NGSocketAlreadyBoundException alloc]
288 initWithReason:@"socket is already bound." socket:self] raise];
291 if (_address == nil) {
292 /* let kernel bind address */
293 return [self kernelBoundAddress];
297 if (![self primaryBindToAddress:_address])
300 /* check for wildcard port */
302 if ([_address respondsToSelector:@selector(isWildcardAddress)]) {
303 if ([(id)_address isWildcardAddress]) {
304 SockAddrLenType len = [[_address domain] addressRepresentationSize];
305 char data[len]; // struct sockaddr
307 if (getsockname(fd, (void *)&data, &len) == 0) { // function is MT-safe
308 id<NGSocketAddress> boundAddr;
310 boundAddr = [[_address domain]
311 addressWithRepresentation:&(data[0])
314 NSLog(@"got sock name (addr-len=%d, %s, %d) %@ ..",
316 inet_ntoa( (((struct sockaddr_in *)(&data[0]))->sin_addr)),
317 ntohs(((struct sockaddr_in *)(&data[0]))->sin_port),
320 ASSIGN(self->localAddress, boundAddr);
323 // could not get local socket name, THROW
324 NSLog(@"ERROR: couldn't resolve wildcard address %@", _address);
331 - (BOOL)kernelBoundAddress {
332 SockAddrLenType len = [[self domain] addressRepresentationSize];
335 // check whether socket is already bound (either manually or by the kernel)
337 [[[NGSocketAlreadyBoundException alloc]
338 initWithReason:@"socket is already bound." socket:self] raise];
343 NSLog(@"socket: kernel bound address of %i in domain %@",
344 self->fd, [self domain]);
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];
354 if (self->localAddress) { // release old address
355 [self->localAddress release];
356 self->localAddress = nil;
358 self->localAddress = [[self domain] addressWithRepresentation:(void *)data
360 self->localAddress = [self->localAddress retain];
361 self->flags.isBound = YES;
367 - (id<NGSocketAddress>)localAddress {
368 return self->localAddress;
372 return self->flags.isBound;
376 [self subclassResponsibility:_cmd];
380 - (id<NGSocketDomain>)domain {
384 #if defined(WIN32) && !defined(__CYGWIN32__)
385 - (SOCKET)fileDescriptor {
387 - (int)fileDescriptor {
392 - (void)resetFileHandle { // called by the NSFileHandle on dealloc
393 self->fileHandle = nil;
395 - (NSFileHandle *)fileHandle {
396 /* the filehandle will reset itself from the stream when being deallocated */
397 if (self->fileHandle == nil) {
399 [(_NGConcreteSocketFileHandle *)[_NGConcreteSocketFileHandle alloc]
400 initWithSocket:self];
402 return [self->fileHandle autorelease];
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();
415 reason = @"Could not set socket option, invalid file descriptor";
419 @"Could not set socket option, option is invalid or socket has been"
423 reason = @"Could not set socket option, option is not supported by protocol";
426 reason = @"Could not set socket option, descriptor isn't a socket";
429 reason = [NSString stringWithFormat:@"Could not set socket option: %s",
438 reason = @"Could not set socket option, invalid file descriptor";
442 @"Could not set socket option, option is invalid or socket has been"
446 reason = @"Could not set socket option, option is not supported by protocol";
449 reason = @"Could not set socket option, descriptor isn't a socket";
452 reason = [NSString stringWithFormat:@"Could not set socket option: %s",
457 [[[NGCouldNotSetSocketOptionException alloc]
458 initWithReason:reason option:_option level:_level] raise];
461 - (void)setOption:(int)_option value:(void *)_value len:(int)_len {
462 [self setOption:_option level:SOL_SOCKET value:_value len:_len];
465 - (void)getOption:(int)_option level:(int)_level value:(void *)_value
468 if (getsockopt(fd, _level, _option, _value, _len) != 0) {
469 NSString *reason = nil;
470 #if defined(WIN32) && !defined(__CYGWIN32__)
471 int e = WSAGetLastError();
475 reason = @"Could not get socket option, invalid file descriptor";
479 @"Could not get socket option, option is invalid at the specified level";
482 reason = @"Could not get socket option, option is not supported by protocol";
485 reason = @"Could not get socket option, descriptor isn't a socket";
489 @"Could not get socket option, operation is not supported by protocol";
492 reason = [NSString stringWithFormat:@"Could not get socket option: %s",
501 reason = @"Could not get socket option, invalid file descriptor";
505 @"Could not get socket option, option is invalid at the specified level";
508 reason = @"Could not get socket option, option is not supported by protocol";
511 reason = @"Could not get socket option, descriptor isn't a socket";
515 @"Could not get socket option, operation is not supported by protocol";
518 reason = [NSString stringWithFormat:@"Could not get socket option: %s",
523 [[[NGCouldNotGetSocketOptionException alloc]
524 initWithReason:reason option:_option level:_level] raise];
527 - (void)getOption:(int)_option value:(void *)_value len:(int *)_len {
528 [self getOption:_option level:SOL_SOCKET value:_value len:_len];
531 static int i_yes = 1;
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];
538 static inline BOOL getBoolOption(id self, int _option) {
540 [self getOption:_option level:SOL_SOCKET value:&value len:&len];
541 return (value ? YES : NO);
544 - (void)setDebug:(BOOL)_flag {
545 setBoolOption(self, SO_DEBUG, _flag);
548 return getBoolOption(self, SO_DEBUG);
551 - (void)setReuseAddress:(BOOL)_flag {
552 setBoolOption(self, SO_REUSEADDR, _flag);
554 - (BOOL)doesReuseAddress {
555 return getBoolOption(self, SO_REUSEADDR);
558 - (void)setKeepAlive:(BOOL)_flag {
559 setBoolOption(self, SO_KEEPALIVE, _flag);
561 - (BOOL)doesKeepAlive {
562 return getBoolOption(self, SO_KEEPALIVE);
565 - (void)setDontRoute:(BOOL)_flag {
566 setBoolOption(self, SO_DONTROUTE, _flag);
568 - (BOOL)doesNotRoute {
569 return getBoolOption(self, SO_DONTROUTE);
572 - (void)setSendBufferSize:(int)_size {
573 [self setOption:SO_SNDBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
575 - (int)sendBufferSize {
577 [self getOption:SO_SNDBUF level:SOL_SOCKET value:&size len:&len];
581 - (void)setReceiveBufferSize:(int)_size {
582 [self setOption:SO_RCVBUF level:SOL_SOCKET value:&_size len:sizeof(_size)];
584 - (int)receiveBufferSize {
586 [self getOption:SO_RCVBUF level:SOL_SOCKET value:&size len:&len];
590 - (int)getSocketError {
592 [self getOption:SO_ERROR level:SOL_SOCKET value:&error len:&len];
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],
604 [self localAddress] ? [self localAddress] : (id)@"no",
612 @implementation _NGConcreteSocketFileHandle
614 - (id)initWithSocket:(id<NGSocket>)_socket {
615 return [super initWithStream:(id<NGStream>)_socket];
620 - (int)fileDescriptor {
621 return [(NGSocket *)stream fileDescriptor];
624 @end /* _NGConcreteSocketFileHandle */
626 #if defined(WIN32) && !defined(__CYGWIN32__)
628 // Windows Descriptor functions
630 // ******************** Poll *********************
632 int NGPollDescriptor(SOCKET _fd, short _events, int _timeout) {
633 struct timeval timeout;
642 if (_events & POLLIN) FD_SET(_fd, &rSet);
643 if (_events & POLLOUT) FD_SET(_fd, &wSet);
644 if (_events & POLLERR) FD_SET(_fd, &eSet);
646 timeout.tv_sec = _timeout / 1000;
647 timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
650 result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
651 if (result == -1) { // error
652 int e = WSAGetLastError();
654 // only retry of interrupted or repeatable
658 while (result == -1);
660 return (result < 0) ? -1 : result;
663 // ******************** Flags ********************
666 int NGGetDescriptorFlags(int _fd) {
669 val = fcntl(_fd, F_GETFL, 0);
671 [NGIOException raiseWithReason:@"could not get descriptor flags"];
674 void NGSetDescriptorFlags(int _fd, int _flags) {
675 if (fcntl(_fd, F_SETFL, _flags) == -1)
676 [NGIOException raiseWithReason:@"could not set descriptor flags"];
679 void NGAddDescriptorFlag (int _fd, int _flag) {
680 int val = NGGetDescriptorFlags(_fd);
681 NGSetDescriptorFlags(_fd, val | _flag);
685 // ******************** NonBlocking IO ************
687 int NGDescriptorRecv(SOCKET _fd, char *_buf, int _len, int _flags, int _timeout) {
691 result = recv(_fd, _buf, _len, _flags);
692 if (result == 0) return 0; // EOF
696 if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
700 pfd.events = POLLRDNORM;
704 if ((result = poll(&pfd, 1, _timeout)) < 0) {
707 // retry if interrupted
708 if ((errorCode != EINTR) && (errorCode != EAGAIN))
716 if (result == 1) { // data waiting, try to read
717 result = recv(_fd, _buf, _len, _flags);
720 else if (result == -1) {
723 if (errorCode == WSAEWOULDBLOCK)
724 NSLog(@"WARNING: would block although descriptor was polled ..");
727 else if (result == 0) {
737 int NGDescriptorSend(SOCKET _fd, const char *_buf, int _len, int _flags,
742 result = send(_fd, _buf, _len, _flags);
743 if (result == 0) return 0; // EOF
747 if ((result == -1) && (errorCode == WSAEWOULDBLOCK)) { // retry
751 pfd.events = POLLWRNORM;
755 if ((result = poll(&pfd, 1, _timeout)) < 0) {
758 if (errorCode != WSAEINTR) // retry only if interrupted
764 result = 1; // block ..
766 if (result == 1) { // data waiting, try to read
767 result = send(_fd, _buf, _len, _flags);
768 if (result == 0) return 0; // EOF
770 else if (result == 0) {
772 NSLog(@"nonblock: send on %i timed out after %i milliseconds ..",