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 "NGPassiveSocket.h"
23 #include "NGSocketExceptions.h"
24 #include "NGActiveSocket.h"
25 #include "NGSocket+private.h"
27 #if defined(__APPLE__)
28 # include <sys/types.h>
29 # include <sys/socket.h>
32 #if HAVE_SYS_ERRNO_H || defined(__APPLE__)
33 # include <sys/errno.h>
38 @interface NGActiveSocket(privateMethods)
40 - (id)_initWithDescriptor:(int)_fd
41 localAddress:(id<NGSocketAddress>)_local
42 remoteAddress:(id<NGSocketAddress>)_remote;
46 @implementation NGPassiveSocket
48 + (id)socketBoundToAddress:(id<NGSocketAddress>)_address {
51 sock = [[[self alloc] initWithDomain:[_address domain]] autorelease];
52 [sock bindToAddress:_address];
56 - (id)initWithDomain:(id<NGSocketDomain>)_domain { // designated initializer
57 if ((self = [super initWithDomain:_domain])) {
58 backlogSize = -1; // -1 means 'not listening'
60 if ([NSThread isMultiThreaded])
61 acceptLock = [[NSLock allocWithZone:[self zone]] init];
64 [[NSNotificationCenter defaultCenter]
66 selector:@selector(taskNowMultiThreaded:)
67 name:NSWillBecomeMultiThreadedNotification
71 if (self->fd != NGInvalidSocketDescriptor) {
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));
85 [[NSNotificationCenter defaultCenter]
87 name:NSWillBecomeMultiThreadedNotification
90 [self->acceptLock release];
94 - (void)taskNowMultiThreaded:(NSNotification *)_notification {
95 if (acceptLock == nil) acceptLock = [[NSLock alloc] init];
100 - (BOOL)isListening {
101 return (backlogSize != -1);
104 return [self isListening];
107 - (id<NGSocketAddress>)localAddress {
117 #if defined(WIN32) && !defined(__CYGWIN32__)
118 - (NSString *)reasonForLastError {
119 int errorCode = WSAGetLastError();
123 return @"not a valid socket descriptor";
125 return @"descriptor is not a socket descriptor";
127 return @"socket does not support listen";
129 return
\a @"interrupted by signal";
131 return @"descriptor table is full";
134 return [NSString stringWithCString:strerror(errorCode)];
138 - (NSString *)reasonForLastError {
139 int errorCode = errno;
143 return @"not a valid socket descriptor";
145 return @"descriptor is not a socket descriptor";
147 return @"socket does not support listen";
149 return @"interrupted by signal";
151 return @"descriptor table is full";
152 case EPROTONOSUPPORT:
153 return @"The protocol is not supported by the address family or "
156 return @"The socket type is not supported by the protocol";
159 return [NSString stringWithCString:strerror(errorCode)];
164 - (BOOL)listenWithBacklog:(int)_backlogSize {
166 // NGSocketIsAlreadyListeningException when the socket is in the listen state
167 // NGCouldNotListenException when the listen call failed
169 if ([self isListening]) {
170 [[[NGSocketIsAlreadyListeningException alloc]
171 initWithReason:@"already called listen" socket:self] raise];
175 if (listen([self fileDescriptor], _backlogSize) != 0) {
177 reason = [self reasonForLastError];
178 reason = [@"Could not listen: %@" stringByAppendingString:reason];
180 [[[NGCouldNotListenException alloc]
181 initWithReason:reason socket:self] raise];
185 /* set backlog size (and mark socket as 'listening') */
186 self->backlogSize = _backlogSize;
190 - (id<NGActiveSocket>)accept {
192 // NGCouldNotAcceptException when the socket is not listening
193 // NGCouldNotAcceptException when the accept call failed
195 id<NGActiveSocket> socket;
198 if (![self isListening]) {
199 [[[NGCouldNotAcceptException alloc]
200 initWithReason:@"socket is not listening" socket:self] raise];
203 SYNCHRONIZED(self->acceptLock) {
204 id<NGSocketAddress> local = nil;
205 id<NGSocketAddress> remote = nil;
208 int newFd = NGInvalidSocketDescriptor;
210 len = [[self domain] addressRepresentationSize];
211 data = calloc(1, len + 1);
213 if ((newFd = accept(fd, (void *)data, &len)) == -1) {
215 NSString *reason = nil;
216 reason = [self reasonForLastError];
217 reason = [@"Could not accept: " stringByAppendingString:reason];
219 [[[NGCouldNotAcceptException alloc]
220 initWithReason:reason socket:self] raise];
223 /* produce remote socket address object */
224 remote = [[self domain] addressWithRepresentation:(void *)data
227 // getsockname if wildcard-IP-bind to get local IP address assigned
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]
235 local = [[self domain] addressWithRepresentation:(void *)data size:len];
242 socket = [[NGActiveSocket alloc]
243 _initWithDescriptor:newFd
245 remoteAddress:remote];
246 socket = [socket autorelease];
254 - (NSString *)description {
255 return [NSString stringWithFormat:@"<PassiveSocket: address=%@>",
256 [self localAddress]];
259 @end /* NGPassiveSocket */