Core Foundation Sockets  v0.2(28.0)
 All Classes Files Functions Variables Properties
CFSocket.m
Go to the documentation of this file.
1 // CFSockets CFSocket.m
2 //
3 // Copyright © 2009–2013, Roy Ratcliffe, Pioneering Software, United Kingdom
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the “Software”), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EITHER
16 // EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO
18 // EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
19 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22 //
23 //------------------------------------------------------------------------------
24 
25 #import "CFSocket.h"
26 #import "CFStreamPair.h"
27 
28 // for setsockopt(2)
29 #import <sys/socket.h>
30 
31 // for IPPROTO_TCP
32 #import <netinet/in.h>
33 
34 @implementation CFSocket
35 
36 @synthesize delegate = _delegate;
37 
38 - (id)init
39 {
40  return (self = nil);
41 }
42 
43 // designated initialiser
44 - (id)initWithSocketRef:(CFSocketRef)socket
45 {
46  if ((self = [super init]))
47  {
48  if (socket)
49  {
50  _socket = socket;
51  }
52  else
53  {
54  self = nil;
55  }
56  }
57  return self;
58 }
59 
60 - (id)initWithProtocolFamily:(int)family socketType:(int)type protocol:(int)protocol
61 {
62  CFSocketContext context = { .info = (__bridge void *)self };
63  return [self initWithSocketRef:CFSocketCreate(kCFAllocatorDefault, family, type, protocol, kCFSocketAcceptCallBack, __CFSocketCallOut, &context)];
64 }
65 
67 {
68  return [self initWithProtocolFamily:PF_INET6 socketType:SOCK_STREAM protocol:IPPROTO_TCP];
69 }
70 
72 {
73  return [self initWithProtocolFamily:PF_INET socketType:SOCK_STREAM protocol:IPPROTO_TCP];
74 }
75 
77 {
78  self = [self initForTCPv6];
79  if (self == nil) self = [self initForTCPv4];
80  return self;
81 }
82 
83 - (id)initWithNativeHandle:(NSSocketNativeHandle)nativeHandle
84 {
85  CFSocketContext context = { .info = (__bridge void *)self };
86  return [self initWithSocketRef:CFSocketCreateWithNative(kCFAllocatorDefault, nativeHandle, kCFSocketAcceptCallBack, __CFSocketCallOut, &context)];
87 }
88 
89 - (BOOL)setAddress:(NSData *)addressData error:(NSError **)outError
90 {
91  CFSocketError error = CFSocketSetAddress(_socket, (__bridge CFDataRef)addressData);
92  BOOL success = (error == kCFSocketSuccess);
93  if (!success)
94  {
95  if (outError && *outError == nil)
96  {
97  *outError = [NSError errorWithDomain:CFSocketErrorDomain code:error userInfo:nil];
98  }
99  }
100  return success;
101 }
102 
103 - (BOOL)connectToAddress:(NSData *)addressData timeout:(NSTimeInterval)timeout error:(NSError **)outError
104 {
105  CFSocketError error = CFSocketConnectToAddress(_socket, (__bridge CFDataRef)addressData, timeout);
106  BOOL success = (error == kCFSocketSuccess);
107  if (!success)
108  {
109  if (outError && *outError == nil)
110  {
111  *outError = [NSError errorWithDomain:CFSocketErrorDomain code:error userInfo:nil];
112  }
113  }
114  return success;
115 }
116 
117 - (void)invalidate
118 {
119  // Never close the underlying native socket without first invalidating.
120  CFSocketInvalidate(_socket);
121 }
122 
123 - (BOOL)isValid
124 {
125  return CFSocketIsValid(_socket) != false;
126 }
127 
128 - (NSData *)address
129 {
130  return CFBridgingRelease(CFSocketCopyAddress(_socket));
131 }
132 
133 - (NSData *)peerAddress
134 {
135  return CFBridgingRelease(CFSocketCopyPeerAddress(_socket));
136 }
137 
138 - (NSSocketNativeHandle)nativeHandle
139 {
140  return CFSocketGetNative(_socket);
141 }
142 
143 - (BOOL)setReuseAddressOption:(BOOL)flag
144 {
145  int option = (flag == NO) ? 0 : 1;
146  return 0 == setsockopt([self nativeHandle], SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
147 }
148 
150 {
151  uint8_t sockaddr[SOCK_MAXADDRLEN];
152  socklen_t len = sizeof(sockaddr);
153  return 0 == getsockname([self nativeHandle], (struct sockaddr *)sockaddr, &len) && len >= offsetof(struct sockaddr, sa_data) ? ((struct sockaddr *)sockaddr)->sa_family : AF_MAX;
154 }
155 
156 - (int)port
157 {
158  int port;
159  switch ([self addressFamily])
160  {
161  case AF_INET:
162  port = ntohs(((struct sockaddr_in *)[[self address] bytes])->sin_port);
163  break;
164  case AF_INET6:
165  port = ntohs(((struct sockaddr_in6 *)[[self address] bytes])->sin6_port);
166  break;
167  default:
168  port = 0;
169  }
170  return port;
171 }
172 
174 {
175  // NSRunLoop is not toll-free bridged to CFRunLoop, even though their names
176  // might suggest that they are.
177  if (_runLoopSource == NULL)
178  {
179  _runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
180  CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
181  }
182 }
183 
185 {
186  if (_runLoopSource)
187  {
188  CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
189  CFRelease(_runLoopSource);
190  _runLoopSource = NULL;
191  }
192 }
193 
195 {
196  CFSocketDisableCallBacks(_socket, kCFSocketAcceptCallBack);
197 }
198 
200 {
201  // The Read, Accept and Data callbacks are mutually exclusive.
202  CFSocketEnableCallBacks(_socket, kCFSocketAcceptCallBack);
203 }
204 
205 - (void)acceptNativeHandle:(NSSocketNativeHandle)nativeHandle
206 {
207  id<CFSocketDelegate> delegate = [self delegate];
208  if (delegate)
209  {
210  if ([delegate respondsToSelector:@selector(socket:acceptNativeHandle:)])
211  {
212  [delegate socket:self acceptNativeHandle:nativeHandle];
213  }
214  else if ([delegate respondsToSelector:@selector(socket:acceptStreamPair:)])
215  {
216  CFStreamPair *streamPair = [[CFStreamPair alloc] initWithSocketNativeHandle:nativeHandle];
217  if (streamPair)
218  {
219  [delegate socket:self acceptStreamPair:streamPair];
220  }
221  }
222  else
223  {
224  close(nativeHandle);
225  }
226  }
227  else
228  {
229  close(nativeHandle);
230  }
231 }
232 
233 - (void)dealloc
234 {
235  // The de-allocator does not need to wonder if the underlying socket exists,
236  // or not. By contract, the socket must exist. This assumes, of course, that
237  // a failed initialisation sequence does not invoke the
238  // de-allocator. However, you cannot assume that. Assigning self to nil
239  // under ARC de-allocates the instance and invokes the -dealloc method.
241  if (_socket)
242  {
243  CFRelease(_socket);
244  _socket = NULL;
245  }
246 }
247 
248 @end
249 
250 NSString *const CFSocketErrorDomain = @"CFSocketErrorDomain";
251 
252 void __CFSocketCallOut(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
253 {
254  switch (type)
255  {
256  case kCFSocketAcceptCallBack:
257  {
258  // Next Step meets Core Foundation socket native handle type in the
259  // next statement. You can use them interchangeably. Apple
260  // type-define both as int. They are really Unix socket
261  // descriptors. The external interface uses the Next Step
262  // definition, since the Next Step foundation framework is the most
263  // immediate dependency.
264  [(__bridge CFSocket *)info acceptNativeHandle:*(CFSocketNativeHandle *)data];
265  break;
266  }
267  default:
268  ;
269  }
270 }