1 /*
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
26 namespace rtc {
27
28 static const int kCallbackFlags = kCFSocketReadCallBack |
29 kCFSocketConnectCallBack |
30 kCFSocketWriteCallBack;
31
MacAsyncSocket(MacBaseSocketServer * ss,int family)32 MacAsyncSocket::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
~MacAsyncSocket()45 MacAsyncSocket::~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.
GetLocalAddress() const51 SocketAddress 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.
GetRemoteAddress() const69 SocketAddress 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.
Bind(const SocketAddress & address)84 int 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
OnResolveResult(SignalThread * thread)92 void 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.
Connect(const SocketAddress & addr)109 int 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 }
115 if (addr.IsUnresolved()) {
116 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
DoConnect(const SocketAddress & addr)127 int 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.
Send(const void * buffer,size_t length)152 int 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.
SendTo(const void * buffer,size_t length,const SocketAddress & address)172 int 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.
Recv(void * buffer,size_t length)191 int MacAsyncSocket::Recv(void* buffer, size_t length) {
192 int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer),
193 length, 0);
194 if (received == SOCKET_ERROR) error_ = errno;
195
196 // Recv should only be called when there is data to read
197 ASSERT((received != 0) || (length == 0));
198 return received;
199 }
200
201 // Read data received from any remote party
RecvFrom(void * buffer,size_t length,SocketAddress * out_addr)202 int MacAsyncSocket::RecvFrom(void* buffer, size_t length,
203 SocketAddress* out_addr) {
204 sockaddr_storage saddr;
205 socklen_t addr_len = sizeof(saddr);
206 int received = ::recvfrom(native_socket_, reinterpret_cast<char*>(buffer),
207 length, 0, reinterpret_cast<sockaddr*>(&saddr),
208 &addr_len);
209 if (received >= 0 && out_addr != NULL) {
210 SocketAddressFromSockAddrStorage(saddr, out_addr);
211 } else if (received == SOCKET_ERROR) {
212 error_ = errno;
213 }
214 return received;
215 }
216
Listen(int backlog)217 int MacAsyncSocket::Listen(int backlog) {
218 if (!valid()) {
219 return SOCKET_ERROR;
220 }
221
222 int res = ::listen(native_socket_, backlog);
223 if (res != SOCKET_ERROR)
224 state_ = CS_CONNECTING;
225 else
226 error_ = errno;
227
228 return res;
229 }
230
Accept(SocketAddress * out_addr)231 MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) {
232 sockaddr_storage saddr;
233 socklen_t addr_len = sizeof(saddr);
234
235 int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
236 &addr_len);
237 if (socket_fd == INVALID_SOCKET) {
238 error_ = errno;
239 return NULL;
240 }
241
242 MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd);
243 if (s && s->valid()) {
244 s->state_ = CS_CONNECTED;
245 if (out_addr)
246 SocketAddressFromSockAddrStorage(saddr, out_addr);
247 } else {
248 delete s;
249 s = NULL;
250 }
251 return s;
252 }
253
Close()254 int MacAsyncSocket::Close() {
255 if (source_ != NULL) {
256 CFRunLoopSourceInvalidate(source_);
257 CFRelease(source_);
258 if (ss_) ss_->UnregisterSocket(this);
259 source_ = NULL;
260 }
261
262 if (socket_ != NULL) {
263 CFSocketInvalidate(socket_);
264 CFRelease(socket_);
265 socket_ = NULL;
266 }
267
268 if (resolver_) {
269 resolver_->Destroy(false);
270 resolver_ = NULL;
271 }
272
273 native_socket_ = INVALID_SOCKET; // invalidates the socket
274 error_ = 0;
275 state_ = CS_CLOSED;
276 return 0;
277 }
278
EstimateMTU(uint16 * mtu)279 int MacAsyncSocket::EstimateMTU(uint16* mtu) {
280 ASSERT(false && "NYI");
281 return -1;
282 }
283
GetError() const284 int MacAsyncSocket::GetError() const {
285 return error_;
286 }
287
SetError(int error)288 void MacAsyncSocket::SetError(int error) {
289 error_ = error;
290 }
291
GetState() const292 Socket::ConnState MacAsyncSocket::GetState() const {
293 return state_;
294 }
295
GetOption(Option opt,int * value)296 int MacAsyncSocket::GetOption(Option opt, int* value) {
297 ASSERT(false && "NYI");
298 return -1;
299 }
300
SetOption(Option opt,int value)301 int MacAsyncSocket::SetOption(Option opt, int value) {
302 ASSERT(false && "NYI");
303 return -1;
304 }
305
EnableCallbacks()306 void MacAsyncSocket::EnableCallbacks() {
307 if (valid()) {
308 disabled_ = false;
309 CFSocketEnableCallBacks(socket_, current_callbacks_);
310 }
311 }
312
DisableCallbacks()313 void MacAsyncSocket::DisableCallbacks() {
314 if (valid()) {
315 disabled_ = true;
316 CFSocketDisableCallBacks(socket_, kCallbackFlags);
317 }
318 }
319
MacAsyncSocket(MacBaseSocketServer * ss,int family,int native_socket)320 MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family,
321 int native_socket)
322 : ss_(ss),
323 socket_(NULL),
324 native_socket_(native_socket),
325 source_(NULL),
326 current_callbacks_(0),
327 disabled_(false),
328 error_(0),
329 state_(CS_CLOSED),
330 resolver_(NULL) {
331 Initialize(family);
332 }
333
334 // Create a new socket, wrapping the native socket if provided or creating one
335 // otherwise. In case of any failure, consume the native socket. We assume the
336 // wrapped socket is in the closed state. If this is not the case you must
337 // update the state_ field for this socket yourself.
Initialize(int family)338 void MacAsyncSocket::Initialize(int family) {
339 CFSocketContext ctx = { 0 };
340 ctx.info = this;
341
342 // First create the CFSocket
343 CFSocketRef cf_socket = NULL;
344 bool res = false;
345 if (native_socket_ == INVALID_SOCKET) {
346 cf_socket = CFSocketCreate(kCFAllocatorDefault,
347 family, SOCK_STREAM, IPPROTO_TCP,
348 kCallbackFlags, MacAsyncSocketCallBack, &ctx);
349 } else {
350 cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
351 native_socket_, kCallbackFlags,
352 MacAsyncSocketCallBack, &ctx);
353 }
354
355 if (cf_socket) {
356 res = true;
357 socket_ = cf_socket;
358 native_socket_ = CFSocketGetNative(cf_socket);
359 current_callbacks_ = kCallbackFlags;
360 }
361
362 if (res) {
363 // Make the underlying socket asynchronous
364 res = (-1 != ::fcntl(native_socket_, F_SETFL,
365 ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK));
366 }
367
368 if (res) {
369 // Add this socket to the run loop, at priority 1 so that it will be
370 // queued behind any pending signals.
371 source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1);
372 res = (source_ != NULL);
373 if (!res) errno = EINVAL;
374 }
375
376 if (res) {
377 if (ss_) ss_->RegisterSocket(this);
378 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes);
379 }
380
381 if (!res) {
382 int error = errno;
383 Close(); // Clears error_.
384 error_ = error;
385 }
386 }
387
388 // Call CFRelease on the result when done using it
CopyCFAddress(const SocketAddress & address)389 CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) {
390 sockaddr_storage saddr;
391 size_t len = address.ToSockAddrStorage(&saddr);
392
393 const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr);
394
395 CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault,
396 bytes, len);
397
398 ASSERT(cf_address != NULL);
399 return cf_address;
400 }
401
MacAsyncSocketCallBack(CFSocketRef s,CFSocketCallBackType callbackType,CFDataRef address,const void * data,void * info)402 void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s,
403 CFSocketCallBackType callbackType,
404 CFDataRef address,
405 const void* data,
406 void* info) {
407 MacAsyncSocket* this_socket =
408 reinterpret_cast<MacAsyncSocket*>(info);
409 ASSERT(this_socket != NULL && this_socket->socket_ == s);
410
411 // Don't signal any socket messages if the socketserver is not listening on
412 // them. When we are reenabled they will be requeued and will fire again.
413 if (this_socket->disabled_)
414 return;
415
416 switch (callbackType) {
417 case kCFSocketReadCallBack:
418 // This callback is invoked in one of 3 situations:
419 // 1. A new connection is waiting to be accepted.
420 // 2. The remote end closed the connection (a recv will return 0).
421 // 3. Data is available to read.
422 // 4. The connection closed unhappily (recv will return -1).
423 if (this_socket->state_ == CS_CONNECTING) {
424 // Case 1.
425 this_socket->SignalReadEvent(this_socket);
426 } else {
427 char ch, amt;
428 amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK);
429 if (amt == 0) {
430 // Case 2.
431 this_socket->state_ = CS_CLOSED;
432
433 // Disable additional callbacks or we will signal close twice.
434 CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack);
435 this_socket->current_callbacks_ &= ~kCFSocketReadCallBack;
436 this_socket->SignalCloseEvent(this_socket, 0);
437 } else if (amt > 0) {
438 // Case 3.
439 this_socket->SignalReadEvent(this_socket);
440 } else {
441 // Case 4.
442 int error = errno;
443 if (error == EAGAIN) {
444 // Observed in practice. Let's hope it's a spurious or out of date
445 // signal, since we just eat it.
446 } else {
447 this_socket->error_ = error;
448 this_socket->SignalCloseEvent(this_socket, error);
449 }
450 }
451 }
452 break;
453
454 case kCFSocketConnectCallBack:
455 if (data != NULL) {
456 // An error occured in the background while connecting
457 this_socket->error_ = errno;
458 this_socket->state_ = CS_CLOSED;
459 this_socket->SignalCloseEvent(this_socket, this_socket->error_);
460 } else {
461 this_socket->state_ = CS_CONNECTED;
462 this_socket->SignalConnectEvent(this_socket);
463 }
464 break;
465
466 case kCFSocketWriteCallBack:
467 // Update our callback tracking. Write doesn't reenable, so it's off now.
468 this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack;
469 this_socket->SignalWriteEvent(this_socket);
470 break;
471
472 default:
473 ASSERT(false && "Invalid callback type for socket");
474 }
475 }
476
477 } // namespace rtc
478