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 "NGPassiveSocket.h"
24 #include "NGSocketExceptions.h"
25 #include "NGActiveSocket.h"
26 #include "NGSocket+private.h"
28 #if defined(__APPLE__)
29 # include <sys/types.h>
30 # include <sys/socket.h>
33 #if HAVE_SYS_ERRNO_H || defined(__APPLE__)
34 # include <sys/errno.h>
39 @interface NGActiveSocket(privateMethods)
41 - (id)_initWithDescriptor:(int)_fd
42 localAddress:(id<NGSocketAddress>)_local
43 remoteAddress:(id<NGSocketAddress>)_remote;
47 @implementation NGPassiveSocket
49 + (id)socketBoundToAddress:(id<NGSocketAddress>)_address {
52 sock = [[[self alloc] initWithDomain:[_address domain]] autorelease];
53 [sock bindToAddress:_address];
57 - (id)initWithDomain:(id<NGSocketDomain>)_domain { // designated initializer
58 if ((self = [super initWithDomain:_domain])) {
59 backlogSize = -1; // -1 means 'not listening'
61 if ([NSThread isMultiThreaded])
62 acceptLock = [[NSLock allocWithZone:[self zone]] init];
65 [[NSNotificationCenter defaultCenter]
67 selector:@selector(taskNowMultiThreaded:)
68 name:NSWillBecomeMultiThreadedNotification
72 if (self->fd != NGInvalidSocketDescriptor) {
75 if (setsockopt(self->fd, SOL_SOCKET, SO_REUSEADDR,
76 (void *)&i_yes, sizeof(int)) != 0) {
77 NSLog(@"WARNING: could not set SO_REUSEADDR option for socket %@: %s",
78 self, strerror(errno));
86 [[NSNotificationCenter defaultCenter]
88 name:NSWillBecomeMultiThreadedNotification
91 [self->acceptLock release];
95 - (void)taskNowMultiThreaded:(NSNotification *)_notification {
96 if (acceptLock == nil) acceptLock = [[NSLock alloc] init];
101 - (BOOL)isListening {
102 return (backlogSize != -1);
105 return [self isListening];
108 - (id<NGSocketAddress>)localAddress {
118 #if defined(WIN32) && !defined(__CYGWIN32__)
119 - (NSString *)reasonForLastError {
120 int errorCode = WSAGetLastError();
124 return @"not a valid socket descriptor";
126 return @"descriptor is not a socket descriptor";
128 return @"socket does not support listen";
130 return
\a @"interrupted by signal";
132 return @"descriptor table is full";
135 return [NSString stringWithCString:strerror(errorCode)];
139 - (NSString *)reasonForLastError {
140 int errorCode = errno;
144 return @"not a valid socket descriptor";
146 return @"descriptor is not a socket descriptor";
148 return @"socket does not support listen";
150 return @"interrupted by signal";
152 return @"descriptor table is full";
153 case EPROTONOSUPPORT:
154 return @"The protocol is not supported by the address family or "
157 return @"The socket type is not supported by the protocol";
160 return [NSString stringWithCString:strerror(errorCode)];
165 - (BOOL)listenWithBacklog:(int)_backlogSize {
167 // NGSocketIsAlreadyListeningException when the socket is in the listen state
168 // NGCouldNotListenException when the listen call failed
170 if ([self isListening]) {
171 [[[NGSocketIsAlreadyListeningException alloc]
172 initWithReason:@"already called listen" socket:self] raise];
176 if (listen([self fileDescriptor], _backlogSize) != 0) {
178 reason = [self reasonForLastError];
179 reason = [@"Could not listen: %@" stringByAppendingString:reason];
181 [[[NGCouldNotListenException alloc]
182 initWithReason:reason socket:self] raise];
186 /* set backlog size (and mark socket as 'listening') */
187 self->backlogSize = _backlogSize;
191 - (id<NGActiveSocket>)accept {
193 // NGCouldNotAcceptException when the socket is not listening
194 // NGCouldNotAcceptException when the accept call failed
196 id<NGActiveSocket> socket;
199 if (![self isListening]) {
200 [[[NGCouldNotAcceptException alloc]
201 initWithReason:@"socket is not listening" socket:self] raise];
204 SYNCHRONIZED(self->acceptLock) {
205 id<NGSocketAddress> local = nil;
206 id<NGSocketAddress> remote = nil;
209 int newFd = NGInvalidSocketDescriptor;
211 len = [[self domain] addressRepresentationSize];
212 data = calloc(1, len + 1);
214 if ((newFd = accept(fd, (void *)data, &len)) == -1) {
216 NSString *reason = nil;
217 reason = [self reasonForLastError];
218 reason = [@"Could not accept: " stringByAppendingString:reason];
220 [[[NGCouldNotAcceptException alloc]
221 initWithReason:reason socket:self] raise];
224 /* produce remote socket address object */
225 remote = [[self domain] addressWithRepresentation:(void *)data
228 // getsockname if wildcard-IP-bind to get local IP address assigned
230 len = [[self domain] addressRepresentationSize];
231 if (getsockname(newFd, (void *)data, &len) != 0) { // function is MT-safe
232 [[[NGSocketException alloc]
233 initWithReason:@"could not get local socket name" socket:self]
236 local = [[self domain] addressWithRepresentation:(void *)data size:len];
243 socket = [[NGActiveSocket alloc]
244 _initWithDescriptor:newFd
246 remoteAddress:remote];
247 socket = [socket autorelease];
255 - (NSString *)description {
256 return [NSString stringWithFormat:@"<PassiveSocket: address=%@>",
257 [self localAddress]];
260 @end /* NGPassiveSocket */