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