blob: 7841b4bf596e08ebfec35934ffed5783c3f72779 [file] [log] [blame]
henrike@webrtc.org0e118e72013-07-10 00:45:36 +00001// Copyright 2010 Google Inc. All Rights Reserved.
2
3// thaloun@google.com (Tim Haloun)
4//
5// MacAsyncSocket is a kind of AsyncSocket. It does not support the SOCK_DGRAM
6// type (yet). It works asynchronously, which means that users of this socket
7// should connect to the various events declared in asyncsocket.h to receive
8// notifications about this socket. It uses CFSockets for signals, but prefers
9// the basic bsd socket operations rather than their CFSocket wrappers when
10// possible.
11
12#include <CoreFoundation/CoreFoundation.h>
13#include <fcntl.h>
14
15#include "talk/base/macasyncsocket.h"
16
17#include "talk/base/logging.h"
18#include "talk/base/macsocketserver.h"
19
20namespace talk_base {
21
22static const int kCallbackFlags = kCFSocketReadCallBack |
23 kCFSocketConnectCallBack |
24 kCFSocketWriteCallBack;
25
26MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family)
27 : ss_(ss),
28 socket_(NULL),
29 native_socket_(INVALID_SOCKET),
30 source_(NULL),
31 current_callbacks_(0),
32 disabled_(false),
33 error_(0),
34 state_(CS_CLOSED),
35 resolver_(NULL) {
36 Initialize(family);
37}
38
39MacAsyncSocket::~MacAsyncSocket() {
40 Close();
41}
42
43// Returns the address to which the socket is bound. If the socket is not
44// bound, then the any-address is returned.
45SocketAddress MacAsyncSocket::GetLocalAddress() const {
46 SocketAddress address;
47
48 // The CFSocket doesn't pick up on implicit binds from the connect call.
49 // Calling bind in before connect explicitly causes errors, so just query
50 // the underlying bsd socket.
51 sockaddr_storage addr;
52 socklen_t addrlen = sizeof(addr);
53 int result = ::getsockname(native_socket_,
54 reinterpret_cast<sockaddr*>(&addr), &addrlen);
55 if (result >= 0) {
56 SocketAddressFromSockAddrStorage(addr, &address);
57 }
58 return address;
59}
60
61// Returns the address to which the socket is connected. If the socket is not
62// connected, then the any-address is returned.
63SocketAddress MacAsyncSocket::GetRemoteAddress() const {
64 SocketAddress address;
65
66 // Use native_socket for consistency with GetLocalAddress.
67 sockaddr_storage addr;
68 socklen_t addrlen = sizeof(addr);
69 int result = ::getpeername(native_socket_,
70 reinterpret_cast<sockaddr*>(&addr), &addrlen);
71 if (result >= 0) {
72 SocketAddressFromSockAddrStorage(addr, &address);
73 }
74 return address;
75}
76
77// Bind the socket to a local address.
78int MacAsyncSocket::Bind(const SocketAddress& address) {
79 sockaddr_storage saddr = {0};
80 size_t len = address.ToSockAddrStorage(&saddr);
81 int err = ::bind(native_socket_, reinterpret_cast<sockaddr*>(&saddr), len);
82 if (err == SOCKET_ERROR) error_ = errno;
83 return err;
84}
85
86void MacAsyncSocket::OnResolveResult(SignalThread* thread) {
87 if (thread != resolver_) {
88 return;
89 }
sergeyu@chromium.org19da4652013-11-13 22:48:52 +000090 int error = resolver_->GetError();
henrike@webrtc.org0e118e72013-07-10 00:45:36 +000091 if (error == 0) {
92 error = DoConnect(resolver_->address());
93 } else {
94 Close();
95 }
96 if (error) {
97 error_ = error;
98 SignalCloseEvent(this, error_);
99 }
100}
101
102// Connect to a remote address.
103int MacAsyncSocket::Connect(const SocketAddress& addr) {
104 // TODO(djw): Consolidate all the connect->resolve->doconnect implementations.
105 if (state_ != CS_CLOSED) {
106 SetError(EALREADY);
107 return SOCKET_ERROR;
108 }
109 if (addr.IsUnresolved()) {
110 LOG(LS_VERBOSE) << "Resolving addr in MacAsyncSocket::Connect";
111 resolver_ = new AsyncResolver();
henrike@webrtc.org0e118e72013-07-10 00:45:36 +0000112 resolver_->SignalWorkDone.connect(this,
113 &MacAsyncSocket::OnResolveResult);
sergeyu@chromium.org19da4652013-11-13 22:48:52 +0000114 resolver_->Start(addr);
henrike@webrtc.org0e118e72013-07-10 00:45:36 +0000115 state_ = CS_CONNECTING;
116 return 0;
117 }
118 return DoConnect(addr);
119}
120
121int MacAsyncSocket::DoConnect(const SocketAddress& addr) {
122 if (!valid()) {
123 Initialize(addr.family());
124 if (!valid())
125 return SOCKET_ERROR;
126 }
127
128 sockaddr_storage saddr;
129 size_t len = addr.ToSockAddrStorage(&saddr);
130 int result = ::connect(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
131 len);
132
133 if (result != SOCKET_ERROR) {
134 state_ = CS_CONNECTED;
135 } else {
136 error_ = errno;
137 if (error_ == EINPROGRESS) {
138 state_ = CS_CONNECTING;
139 result = 0;
140 }
141 }
142 return result;
143}
144
145// Send to the remote end we're connected to.
146int MacAsyncSocket::Send(const void* buffer, size_t length) {
147 if (!valid()) {
148 return SOCKET_ERROR;
149 }
150
151 int sent = ::send(native_socket_, buffer, length, 0);
152
153 if (sent == SOCKET_ERROR) {
154 error_ = errno;
155
156 if (IsBlocking()) {
157 // Reenable the writable callback (once), since we are flow controlled.
158 CFSocketEnableCallBacks(socket_, kCallbackFlags);
159 current_callbacks_ = kCallbackFlags;
160 }
161 }
162 return sent;
163}
164
165// Send to the given address. We may or may not be connected to anyone.
166int MacAsyncSocket::SendTo(const void* buffer, size_t length,
167 const SocketAddress& address) {
168 if (!valid()) {
169 return SOCKET_ERROR;
170 }
171
172 sockaddr_storage saddr;
173 size_t len = address.ToSockAddrStorage(&saddr);
174 int sent = ::sendto(native_socket_, buffer, length, 0,
175 reinterpret_cast<sockaddr*>(&saddr), len);
176
177 if (sent == SOCKET_ERROR) {
178 error_ = errno;
179 }
180
181 return sent;
182}
183
184// Read data received from the remote end we're connected to.
185int MacAsyncSocket::Recv(void* buffer, size_t length) {
186 int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer),
187 length, 0);
188 if (received == SOCKET_ERROR) error_ = errno;
189
190 // Recv should only be called when there is data to read
191 ASSERT((received != 0) || (length == 0));
192 return received;
193}
194
195// Read data received from any remote party
196int MacAsyncSocket::RecvFrom(void* buffer, size_t length,
197 SocketAddress* out_addr) {
198 sockaddr_storage saddr;
199 socklen_t addr_len = sizeof(saddr);
200 int received = ::recvfrom(native_socket_, reinterpret_cast<char*>(buffer),
201 length, 0, reinterpret_cast<sockaddr*>(&saddr),
202 &addr_len);
203 if (received >= 0 && out_addr != NULL) {
204 SocketAddressFromSockAddrStorage(saddr, out_addr);
205 } else if (received == SOCKET_ERROR) {
206 error_ = errno;
207 }
208 return received;
209}
210
211int MacAsyncSocket::Listen(int backlog) {
212 if (!valid()) {
213 return SOCKET_ERROR;
214 }
215
216 int res = ::listen(native_socket_, backlog);
217 if (res != SOCKET_ERROR)
218 state_ = CS_CONNECTING;
219 else
220 error_ = errno;
221
222 return res;
223}
224
225MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) {
226 sockaddr_storage saddr;
227 socklen_t addr_len = sizeof(saddr);
228
229 int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
230 &addr_len);
231 if (socket_fd == INVALID_SOCKET) {
232 error_ = errno;
233 return NULL;
234 }
235
236 MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd);
237 if (s && s->valid()) {
238 s->state_ = CS_CONNECTED;
239 if (out_addr)
240 SocketAddressFromSockAddrStorage(saddr, out_addr);
241 } else {
242 delete s;
243 s = NULL;
244 }
245 return s;
246}
247
248int MacAsyncSocket::Close() {
249 if (source_ != NULL) {
250 CFRunLoopSourceInvalidate(source_);
251 CFRelease(source_);
252 if (ss_) ss_->UnregisterSocket(this);
253 source_ = NULL;
254 }
255
256 if (socket_ != NULL) {
257 CFSocketInvalidate(socket_);
258 CFRelease(socket_);
259 socket_ = NULL;
260 }
261
262 if (resolver_) {
263 resolver_->Destroy(false);
264 resolver_ = NULL;
265 }
266
267 native_socket_ = INVALID_SOCKET; // invalidates the socket
268 error_ = 0;
269 state_ = CS_CLOSED;
270 return 0;
271}
272
273int MacAsyncSocket::EstimateMTU(uint16* mtu) {
274 ASSERT(false && "NYI");
275 return -1;
276}
277
278int MacAsyncSocket::GetError() const {
279 return error_;
280}
281
282void MacAsyncSocket::SetError(int error) {
283 error_ = error;
284}
285
286Socket::ConnState MacAsyncSocket::GetState() const {
287 return state_;
288}
289
290int MacAsyncSocket::GetOption(Option opt, int* value) {
291 ASSERT(false && "NYI");
292 return -1;
293}
294
295int MacAsyncSocket::SetOption(Option opt, int value) {
296 ASSERT(false && "NYI");
297 return -1;
298}
299
300void MacAsyncSocket::EnableCallbacks() {
301 if (valid()) {
302 disabled_ = false;
303 CFSocketEnableCallBacks(socket_, current_callbacks_);
304 }
305}
306
307void MacAsyncSocket::DisableCallbacks() {
308 if (valid()) {
309 disabled_ = true;
310 CFSocketDisableCallBacks(socket_, kCallbackFlags);
311 }
312}
313
314MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family,
315 int native_socket)
316 : ss_(ss),
317 socket_(NULL),
318 native_socket_(native_socket),
319 source_(NULL),
320 current_callbacks_(0),
321 disabled_(false),
322 error_(0),
323 state_(CS_CLOSED),
324 resolver_(NULL) {
325 Initialize(family);
326}
327
328// Create a new socket, wrapping the native socket if provided or creating one
329// otherwise. In case of any failure, consume the native socket. We assume the
330// wrapped socket is in the closed state. If this is not the case you must
331// update the state_ field for this socket yourself.
332void MacAsyncSocket::Initialize(int family) {
333 CFSocketContext ctx = { 0 };
334 ctx.info = this;
335
336 // First create the CFSocket
337 CFSocketRef cf_socket = NULL;
338 bool res = false;
339 if (native_socket_ == INVALID_SOCKET) {
340 cf_socket = CFSocketCreate(kCFAllocatorDefault,
341 family, SOCK_STREAM, IPPROTO_TCP,
342 kCallbackFlags, MacAsyncSocketCallBack, &ctx);
343 } else {
344 cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
345 native_socket_, kCallbackFlags,
346 MacAsyncSocketCallBack, &ctx);
347 }
348
349 if (cf_socket) {
350 res = true;
351 socket_ = cf_socket;
352 native_socket_ = CFSocketGetNative(cf_socket);
353 current_callbacks_ = kCallbackFlags;
354 }
355
356 if (res) {
357 // Make the underlying socket asynchronous
358 res = (-1 != ::fcntl(native_socket_, F_SETFL,
359 ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK));
360 }
361
362 if (res) {
363 // Add this socket to the run loop, at priority 1 so that it will be
364 // queued behind any pending signals.
365 source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1);
366 res = (source_ != NULL);
367 if (!res) errno = EINVAL;
368 }
369
370 if (res) {
371 if (ss_) ss_->RegisterSocket(this);
372 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes);
373 }
374
375 if (!res) {
376 int error = errno;
377 Close(); // Clears error_.
378 error_ = error;
379 }
380}
381
382// Call CFRelease on the result when done using it
383CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) {
384 sockaddr_storage saddr;
385 size_t len = address.ToSockAddrStorage(&saddr);
386
387 const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr);
388
389 CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault,
390 bytes, len);
391
392 ASSERT(cf_address != NULL);
393 return cf_address;
394}
395
396void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s,
397 CFSocketCallBackType callbackType,
398 CFDataRef address,
399 const void* data,
400 void* info) {
401 MacAsyncSocket* this_socket =
402 reinterpret_cast<MacAsyncSocket*>(info);
403 ASSERT(this_socket != NULL && this_socket->socket_ == s);
404
405 // Don't signal any socket messages if the socketserver is not listening on
406 // them. When we are reenabled they will be requeued and will fire again.
407 if (this_socket->disabled_)
408 return;
409
410 switch (callbackType) {
411 case kCFSocketReadCallBack:
412 // This callback is invoked in one of 3 situations:
413 // 1. A new connection is waiting to be accepted.
414 // 2. The remote end closed the connection (a recv will return 0).
415 // 3. Data is available to read.
416 // 4. The connection closed unhappily (recv will return -1).
417 if (this_socket->state_ == CS_CONNECTING) {
418 // Case 1.
419 this_socket->SignalReadEvent(this_socket);
420 } else {
421 char ch, amt;
422 amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK);
423 if (amt == 0) {
424 // Case 2.
425 this_socket->state_ = CS_CLOSED;
426
427 // Disable additional callbacks or we will signal close twice.
428 CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack);
429 this_socket->current_callbacks_ &= ~kCFSocketReadCallBack;
430 this_socket->SignalCloseEvent(this_socket, 0);
431 } else if (amt > 0) {
432 // Case 3.
433 this_socket->SignalReadEvent(this_socket);
434 } else {
435 // Case 4.
436 int error = errno;
437 if (error == EAGAIN) {
438 // Observed in practice. Let's hope it's a spurious or out of date
439 // signal, since we just eat it.
440 } else {
441 this_socket->error_ = error;
442 this_socket->SignalCloseEvent(this_socket, error);
443 }
444 }
445 }
446 break;
447
448 case kCFSocketConnectCallBack:
449 if (data != NULL) {
450 // An error occured in the background while connecting
451 this_socket->error_ = errno;
452 this_socket->state_ = CS_CLOSED;
453 this_socket->SignalCloseEvent(this_socket, this_socket->error_);
454 } else {
455 this_socket->state_ = CS_CONNECTED;
456 this_socket->SignalConnectEvent(this_socket);
457 }
458 break;
459
460 case kCFSocketWriteCallBack:
461 // Update our callback tracking. Write doesn't reenable, so it's off now.
462 this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack;
463 this_socket->SignalWriteEvent(this_socket);
464 break;
465
466 default:
467 ASSERT(false && "Invalid callback type for socket");
468 }
469}
470
471} // namespace talk_base