1// 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  }
90  int error = resolver_->GetError();
91  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();
112    resolver_->SignalWorkDone.connect(this,
113                                      &MacAsyncSocket::OnResolveResult);
114    resolver_->Start(addr);
115    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
472