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