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
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  }
115  if (addr.IsUnresolvedIP()) {
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
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.
191int 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
202int 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
217int 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
231MacAsyncSocket* 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
254int 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
279int MacAsyncSocket::EstimateMTU(uint16_t* mtu) {
280  ASSERT(false && "NYI");
281  return -1;
282}
283
284int MacAsyncSocket::GetError() const {
285  return error_;
286}
287
288void MacAsyncSocket::SetError(int error) {
289  error_ = error;
290}
291
292Socket::ConnState MacAsyncSocket::GetState() const {
293  return state_;
294}
295
296int MacAsyncSocket::GetOption(Option opt, int* value) {
297  ASSERT(false && "NYI");
298  return -1;
299}
300
301int MacAsyncSocket::SetOption(Option opt, int value) {
302  ASSERT(false && "NYI");
303  return -1;
304}
305
306void MacAsyncSocket::EnableCallbacks() {
307  if (valid()) {
308    disabled_ = false;
309    CFSocketEnableCallBacks(socket_, current_callbacks_);
310  }
311}
312
313void MacAsyncSocket::DisableCallbacks() {
314  if (valid()) {
315    disabled_ = true;
316    CFSocketDisableCallBacks(socket_, kCallbackFlags);
317  }
318}
319
320MacAsyncSocket::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.
338void 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
389CFDataRef 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
402void 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