]> err.no Git - sope/blob - sope-core/NGStreams/NGDatagramSocket.m
Id fixes
[sope] / sope-core / NGStreams / NGDatagramSocket.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #if defined(__APPLE__)
24 #  include <sys/types.h>
25 #  include <sys/socket.h>
26 #endif
27
28 #include <NGStreams/NGDescriptorFunctions.h>
29 #include "NGDatagramSocket.h"
30 #include "NGDatagramPacket.h"
31 #include "NGSocketExceptions.h"
32 #include "NGSocket+private.h"
33 #include "common.h"
34
35 #if !defined(POLLRDNORM)
36 #  define POLLRDNORM POLLIN
37 #endif
38
39 NSString *NGSocketTimedOutNotificationName = @"NGSocketTimedOutNotification";
40
41 @interface NGSocket(privateMethods)
42
43 - (void)_createSocketInDomain:(int)_domain;
44
45 - (void)setOption:(int)_option level:(int)_level value:(void *)_value len:(int)_len;
46 - (void)setOption:(int)_option value:(void *)_value len:(int)_len;
47 - (void)getOption:(int)_option level:(int)_level value:(void *)_val len:(int *)_len;
48 - (void)getOption:(int)_option value:(void *)_value len:(int *)_len;
49
50 @end
51
52 //static const int NGMaxTimeout = (int)-1;
53 static const NSTimeInterval NGNoTimeout = 0.0;
54
55 @implementation NGDatagramSocket
56
57 #if !defined(WIN32) || defined(__CYGWIN32__)
58
59 + (BOOL)socketPair:(id<NGSocket>[2])_pair inDomain:(id<NGSocketDomain>)_domain {
60   int fds[2];
61
62   _pair[0] = nil;
63   _pair[1] = nil;
64
65   if (socketpair([_domain socketDomain], SOCK_DGRAM, [_domain protocol],
66                  fds) == 0) {
67     NGDatagramSocket *s1 = nil;
68     NGDatagramSocket *s2 = nil;
69     
70     s1 = [[self alloc] _initWithDomain:_domain descriptor:fds[0]];
71     s2 = [[self alloc] _initWithDomain:_domain descriptor:fds[1]];
72     s1 = AUTORELEASE(s1);
73     s2 = AUTORELEASE(s2);
74
75     if ((s1 != nil) && (s2 != nil)) {
76       _pair[0] = s1;
77       _pair[1] = s2;
78
79       return YES;
80     }
81     else
82       return NO;
83   }
84   else {
85     int      e       = errno;
86     NSString *reason = nil;
87
88     switch (e) {
89       case EACCES:
90         reason = @"Not allowed to create socket of this type";
91         break;
92       case ENOMEM:
93         reason = @"Could not create socket: Insufficient user memory available";
94         break;
95       case EPROTONOSUPPORT:
96         reason = @"The protocol is not supported by the address family or "
97                  @"implementation";
98         break;
99       case EPROTOTYPE:
100         reason = @"The socket type is not supported by the protocol";
101         break;
102       case EMFILE:
103         reason = @"Could not create socket: descriptor table is full";
104         break;
105       case EOPNOTSUPP:
106         reason = @"The specified protocol does not permit creation of socket "
107                  @"pairs";
108         break;
109
110       default:
111         reason = [NSString stringWithFormat:@"Could not create socketpair: %s",
112                              strerror(e)];
113         break;
114     }
115     [[[NGCouldNotCreateSocketException alloc]
116               initWithReason:reason domain:_domain] raise];
117     return NO;
118   }
119 }
120
121 #endif
122
123 + (id)socketBoundToAddress:(id<NGSocketAddress>)_address {
124   volatile id sock = [[self alloc] initWithDomain:[_address domain]];
125
126   if (sock != nil) {
127     sock = AUTORELEASE(sock);
128     [sock bindToAddress:_address];
129   }
130   return sock;
131 }
132
133 - (id)initWithDomain:(id<NGSocketDomain>)_domain { // designated initializer
134   if ((self = [super initWithDomain:_domain])) {
135     [self setMaxPacketSize:2048];
136     [self setPacketFactory:(id)[NGDatagramPacket class]];
137     self->udpFlags.isConnected = NO;
138   }
139   return self;
140 }
141
142 // accessors
143
144 - (void)setMaxPacketSize:(int)_maxPacketSize {
145   self->maxPacketSize = _maxPacketSize;
146 }
147 - (int)maxPacketSize {
148   return self->maxPacketSize;
149 }
150
151 - (void)setPacketFactory:(id<NGDatagramPacketFactory>)_factory {
152   ASSIGN(self->packetFactory, _factory);
153 }
154 - (id<NGDatagramPacketFactory>)packetFactory {
155   return self->packetFactory;
156 }
157
158 - (int)socketType {
159   return SOCK_DGRAM;
160 }
161
162 - (BOOL)isConnected {
163   return self->udpFlags.isConnected;
164 }
165
166 // polling
167
168 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
169   short events = 0;
170
171   if (fd == NGInvalidSocketDescriptor)
172     return NO;
173
174   if (NGCanReadInStreamMode(_mode))  events |= POLLRDNORM;
175   if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
176
177   // timeout of 0 means return immediatly
178   return (NGPollDescriptor([self fileDescriptor], events, 0) == 1 ? NO : YES);
179 }
180
181 // sending
182
183 - (void)primarySendPacket:(id<NGDatagramPacket>)_packet {
184   int bytesWritten;
185
186   NSAssert([_packet receiver], @"packet has no destination !");
187
188   bytesWritten = sendto(self->fd, // socket
189                         [[_packet data] bytes], [[_packet data] length],
190                         0, // flags
191                         [[_packet receiver] internalAddressRepresentation],
192                         [[_packet receiver] addressRepresentationSize]);
193
194   if (!self->flags.isBound) // was not explictly bound, so get local address
195     [self kernelBoundAddress];
196
197   [_packet setSender:[self localAddress]];
198 }
199
200 - (BOOL)sendPacket:(id<NGDatagramPacket>)_packet timeout:(NSTimeInterval)_to {
201   if (_to > NGNoTimeout) {
202     int result = NGPollDescriptor([self fileDescriptor],
203                                   POLLWRNORM,
204                                   (int)(_to * 1000.0));
205
206     if (result == 0) {
207       // timeout
208       [[NSNotificationCenter defaultCenter]
209                              postNotificationName:NGSocketTimedOutNotificationName
210                              object:self];
211       return NO;
212     }
213     else if (result < 0) {
214       [[[NGSocketException alloc]
215            initWithReason:@"error during poll on UDP socket"] raise];
216       return NO;
217     }
218
219     // else receive packet ..
220   }
221   [self primarySendPacket:_packet];
222   return YES;
223 }
224
225 - (BOOL)sendPacket:(id<NGDatagramPacket>)_packet {
226   return [self sendPacket:_packet timeout:NGNoTimeout];
227 }
228
229 // receiving
230
231 - (id<NGDatagramPacket>)primaryReceivePacketWithMaxSize:(int)_maxSize {
232   id<NGSocketAddress>  remote  = nil;
233   id<NGDatagramPacket> packet = nil;
234   char    buffer[_maxSize];
235   size_t  size;
236   int     len   = [[self domain] addressRepresentationSize];
237   char    data[len];
238
239   size = recvfrom(self->fd, buffer, _maxSize,
240                   0, // flags
241                   (void *)data, &len);
242   remote = [[self domain] addressWithRepresentation:(void *)data size:len];
243
244   if (!self->flags.isBound) // was not explictly bound, so get local address
245     [self kernelBoundAddress];
246
247   packet = [[self packetFactory] packetWithBytes:buffer size:size];
248   [packet setReceiver:[self localAddress]];
249   [packet setSender:remote];
250
251   return packet;
252 }
253 - (id<NGDatagramPacket>)receivePacketWithMaxSize:(int)_size
254   timeout:(NSTimeInterval)_to {
255   
256   if (_to > NGNoTimeout) {
257     int result = NGPollDescriptor([self fileDescriptor],
258                                   POLLRDNORM,
259                                   (int)(_to * 1000.0));
260
261     if (result == 0) {
262       // timeout
263       [[NSNotificationCenter defaultCenter]
264                              postNotificationName:NGSocketTimedOutNotificationName
265                              object:self];
266       return nil;
267     }
268     else if (result < 0) {
269       [[[NGSocketException alloc]
270            initWithReason:@"error during poll on UDP socket"] raise];
271     }
272
273     // else receive packet ..
274   }
275   return [self primaryReceivePacketWithMaxSize:_size];
276 }
277
278 - (id<NGDatagramPacket>)receivePacketWithTimeout:(NSTimeInterval)_timeout {
279   return [self receivePacketWithMaxSize:[self maxPacketSize] timeout:_timeout];
280 }
281
282 - (id<NGDatagramPacket>)receivePacketWithMaxSize:(int)_maxPacketSize {
283   return [self receivePacketWithMaxSize:_maxPacketSize timeout:NGNoTimeout];
284 }
285 - (id<NGDatagramPacket>)receivePacket {
286   return [self receivePacketWithMaxSize:[self maxPacketSize] timeout:NGNoTimeout];
287 }
288
289 // ************************* options *************************
290
291 static int i_yes = 1;
292 static int i_no  = 0;
293
294 static inline void setBoolOption(id self, int _option, BOOL _flag) {
295   [self setOption:_option level:SOL_SOCKET
296         value:(_flag ? &i_yes : &i_no) len:4];
297 }
298 static inline BOOL getBoolOption(id self, int _option) {
299   int value, len;
300   [self getOption:_option level:SOL_SOCKET value:&value len:&len];
301   return (value ? YES : NO);
302 }
303
304 - (void)setBroadcast:(BOOL)_flag {
305   setBoolOption(self, SO_BROADCAST, _flag);
306 }
307 - (BOOL)doesBroadcast {
308   return getBoolOption(self, SO_BROADCAST);
309 }
310
311 // aborts, only supported for TCP
312
313 - (void)setDebug:(BOOL)_flag {
314   [self doesNotRecognizeSelector:_cmd];
315 }
316 - (BOOL)doesDebug {
317   [self doesNotRecognizeSelector:_cmd];
318   return NO;
319 }
320
321 @end