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 "NGPassiveSocket.h" 23#include "NGSocketExceptions.h" 24#include "NGActiveSocket.h" 25#include "NGSocket+private.h" 26 27#if defined(__APPLE__) 28# include <sys/types.h> 29# include <sys/socket.h> 30#endif 31 32#if HAVE_SYS_ERRNO_H || defined(__APPLE__) 33# include <sys/errno.h> 34#endif 35 36#include "common.h" 37 38@interface NGActiveSocket(privateMethods) 39 40- (id)_initWithDescriptor:(int)_fd 41 localAddress:(id<NGSocketAddress>)_local 42 remoteAddress:(id<NGSocketAddress>)_remote; 43 44@end 45 46@implementation NGPassiveSocket 47 48+ (id)socketBoundToAddress:(id<NGSocketAddress>)_address { 49 volatile id sock; 50 51 sock = [[[self alloc] initWithDomain:[_address domain]] autorelease]; 52 [sock bindToAddress:_address]; 53 return sock; 54} 55 56- (id)initWithDomain:(id<NGSocketDomain>)_domain { // designated initializer 57 if ((self = [super initWithDomain:_domain])) { 58 backlogSize = -1; // -1 means 'not listening' 59 60 if ([NSThread isMultiThreaded]) 61 acceptLock = [[NSLock allocWithZone:[self zone]] init]; 62 else { 63 acceptLock = nil; 64 [[NSNotificationCenter defaultCenter] 65 addObserver:self 66 selector:@selector(taskNowMultiThreaded:) 67 name:NSWillBecomeMultiThreadedNotification 68 object:nil]; 69 } 70 71 if (self->fd != NGInvalidSocketDescriptor) { 72 int i_yes = 1; 73 74 if (setsockopt(self->fd, SOL_SOCKET, SO_REUSEADDR, 75 (void *)&i_yes, sizeof(int)) != 0) { 76 NSLog(@"WARNING: could not set SO_REUSEADDR option for socket %@: %s", 77 self, strerror(errno)); 78 } 79 } 80 } 81 return self; 82} 83 84- (void)dealloc { 85 [[NSNotificationCenter defaultCenter] 86 removeObserver:self 87 name:NSWillBecomeMultiThreadedNotification 88 object:nil]; 89 90 [self->acceptLock release]; 91 [super dealloc]; 92} 93 94- (void)taskNowMultiThreaded:(NSNotification *)_notification { 95 if (acceptLock == nil) acceptLock = [[NSLock alloc] init]; 96} 97 98// accessors 99 100- (BOOL)isListening { 101 return (backlogSize != -1); 102} 103- (BOOL)isOpen { 104 return [self isListening]; 105} 106 107- (id<NGSocketAddress>)localAddress { 108 return localAddress; 109} 110 111- (int)socketType { 112 return SOCK_STREAM; 113} 114 115/* operations */ 116 117#if defined(WIN32) && !defined(__CYGWIN32__) 118- (NSString *)reasonForLastError { 119 int errorCode = WSAGetLastError(); 120 121 switch (errorCode) { 122 case WSAEBADF: 123 return @"not a valid socket descriptor"; 124 case WSAENOTSOCK: 125 return @"descriptor is not a socket descriptor"; 126 case WSAEOPNOTSUPP: 127 return @"socket does not support listen"; 128 case WSAEINTR: 129 return @"interrupted by signal"; 130 case WSAEMFILE: 131 return @"descriptor table is full"; 132 133 default: 134 return [NSString stringWithCString:strerror(errorCode)]; 135 } 136} 137#else 138- (NSString *)reasonForLastError { 139 int errorCode = errno; 140 141 switch (errorCode) { 142 case EBADF: 143 return @"not a valid socket descriptor"; 144 case ENOTSOCK: 145 return @"descriptor is not a socket descriptor"; 146 case EOPNOTSUPP: 147 return @"socket does not support listen"; 148 case EINTR: 149 return @"interrupted by signal"; 150 case EMFILE: 151 return @"descriptor table is full"; 152 case EPROTONOSUPPORT: 153 return @"The protocol is not supported by the address family or " 154 @"implementation"; 155 case EPROTOTYPE: 156 return @"The socket type is not supported by the protocol"; 157 158 default: 159 return [NSString stringWithCString:strerror(errorCode)]; 160 } 161} 162#endif 163 164- (BOOL)listenWithBacklog:(int)_backlogSize { 165 // throws 166 // NGSocketIsAlreadyListeningException when the socket is in the listen state 167 // NGCouldNotListenException when the listen call failed 168 169 if ([self isListening]) { 170 [[[NGSocketIsAlreadyListeningException alloc] 171 initWithReason:@"already called listen" socket:self] raise]; 172 return NO; 173 } 174 175 if (listen([self fileDescriptor], _backlogSize) != 0) { 176 NSString *reason; 177 reason = [self reasonForLastError]; 178 reason = [@"Could not listen: %@" stringByAppendingString:reason]; 179 180 [[[NGCouldNotListenException alloc] 181 initWithReason:reason socket:self] raise]; 182 return NO; 183 } 184 185 /* set backlog size (and mark socket as 'listening') */ 186 self->backlogSize = _backlogSize; 187 return YES; 188} 189 190- (id<NGActiveSocket>)accept { 191 // throws 192 // NGCouldNotAcceptException when the socket is not listening 193 // NGCouldNotAcceptException when the accept call failed 194 195 id<NGActiveSocket> socket; 196 *(&socket) = nil; 197 198 if (![self isListening]) { 199 [[[NGCouldNotAcceptException alloc] 200 initWithReason:@"socket is not listening" socket:self] raise]; 201 } 202 203 SYNCHRONIZED(self->acceptLock) { 204 id<NGSocketAddress> local = nil; 205 id<NGSocketAddress> remote = nil; 206 socklen_t len; 207 char *data; 208 int newFd = NGInvalidSocketDescriptor; 209 210 len = [[self domain] addressRepresentationSize]; 211 data = calloc(1, len + 1); 212 213 if ((newFd = accept(fd, (void *)data, &len)) == -1) { 214 // call failed 215 NSString *reason = nil; 216 reason = [self reasonForLastError]; 217 reason = [@"Could not accept: " stringByAppendingString:reason]; 218 219 [[[NGCouldNotAcceptException alloc] 220 initWithReason:reason socket:self] raise]; 221 } 222 223 /* produce remote socket address object */ 224 remote = [[self domain] addressWithRepresentation:(void *)data 225 size:len]; 226 227 // getsockname if wildcard-IP-bind to get local IP address assigned 228 // to the connection 229 len = [[self domain] addressRepresentationSize]; 230 if (getsockname(newFd, (void *)data, &len) != 0) { // function is MT-safe 231 [[[NGSocketException alloc] 232 initWithReason:@"could not get local socket name" socket:self] 233 raise]; 234 } 235 local = [[self domain] addressWithRepresentation:(void *)data size:len]; 236 237 if (data) { 238 free(data); 239 data = NULL; 240 } 241 242 socket = [[NGActiveSocket alloc] 243 _initWithDescriptor:newFd 244 localAddress:local 245 remoteAddress:remote]; 246 socket = [socket autorelease]; 247 } 248 END_SYNCHRONIZED; 249 return socket; 250} 251 252// description 253 254- (NSString *)description { 255 return [NSString stringWithFormat:@"<PassiveSocket: address=%@>", 256 [self localAddress]]; 257} 258 259@end /* NGPassiveSocket */ 260