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