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