Home | History | Annotate | Download | only in base
      1 // Copyright 2010 Google Inc. All Rights Reserved.
      2 
      3 //         thaloun (at) 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 
     20 namespace talk_base {
     21 
     22 static const int kCallbackFlags = kCFSocketReadCallBack |
     23                                   kCFSocketConnectCallBack |
     24                                   kCFSocketWriteCallBack;
     25 
     26 MacAsyncSocket::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 
     39 MacAsyncSocket::~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.
     45 SocketAddress 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.
     63 SocketAddress 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.
     78 int 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 
     86 void 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.
    103 int 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 
    121 int 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.
    146 int 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.
    166 int 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.
    185 int 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
    196 int 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 
    211 int 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 
    225 MacAsyncSocket* 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 
    248 int 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 
    273 int MacAsyncSocket::EstimateMTU(uint16* mtu) {
    274   ASSERT(false && "NYI");
    275   return -1;
    276 }
    277 
    278 int MacAsyncSocket::GetError() const {
    279   return error_;
    280 }
    281 
    282 void MacAsyncSocket::SetError(int error) {
    283   error_ = error;
    284 }
    285 
    286 Socket::ConnState MacAsyncSocket::GetState() const {
    287   return state_;
    288 }
    289 
    290 int MacAsyncSocket::GetOption(Option opt, int* value) {
    291   ASSERT(false && "NYI");
    292   return -1;
    293 }
    294 
    295 int MacAsyncSocket::SetOption(Option opt, int value) {
    296   ASSERT(false && "NYI");
    297   return -1;
    298 }
    299 
    300 void MacAsyncSocket::EnableCallbacks() {
    301   if (valid()) {
    302     disabled_ = false;
    303     CFSocketEnableCallBacks(socket_, current_callbacks_);
    304   }
    305 }
    306 
    307 void MacAsyncSocket::DisableCallbacks() {
    308   if (valid()) {
    309     disabled_ = true;
    310     CFSocketDisableCallBacks(socket_, kCallbackFlags);
    311   }
    312 }
    313 
    314 MacAsyncSocket::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.
    332 void 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
    383 CFDataRef 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 
    396 void 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