1 // Copyright 2010 Google Inc. All Rights Reserved.
3 // thaloun@google.com (Tim Haloun)
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
12 #include <CoreFoundation/CoreFoundation.h>
15 #include "talk/base/macasyncsocket.h"
17 #include "talk/base/logging.h"
18 #include "talk/base/macsocketserver.h"
22 static const int kCallbackFlags = kCFSocketReadCallBack |
23 kCFSocketConnectCallBack |
24 kCFSocketWriteCallBack;
26 MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family)
29 native_socket_(INVALID_SOCKET),
31 current_callbacks_(0),
39 MacAsyncSocket::~MacAsyncSocket() {
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;
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);
56 SocketAddressFromSockAddrStorage(addr, &address);
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;
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);
72 SocketAddressFromSockAddrStorage(addr, &address);
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;
86 void MacAsyncSocket::OnResolveResult(SignalThread* thread) {
87 if (thread != resolver_) {
90 int error = resolver_->error();
92 error = DoConnect(resolver_->address());
98 SignalCloseEvent(this, error_);
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) {
109 if (addr.IsUnresolved()) {
110 LOG(LS_VERBOSE) << "Resolving addr in MacAsyncSocket::Connect";
111 resolver_ = new AsyncResolver();
112 resolver_->set_address(addr);
113 resolver_->SignalWorkDone.connect(this,
114 &MacAsyncSocket::OnResolveResult);
116 state_ = CS_CONNECTING;
119 return DoConnect(addr);
122 int MacAsyncSocket::DoConnect(const SocketAddress& addr) {
124 Initialize(addr.family());
129 sockaddr_storage saddr;
130 size_t len = addr.ToSockAddrStorage(&saddr);
131 int result = ::connect(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
134 if (result != SOCKET_ERROR) {
135 state_ = CS_CONNECTED;
138 if (error_ == EINPROGRESS) {
139 state_ = CS_CONNECTING;
146 // Send to the remote end we're connected to.
147 int MacAsyncSocket::Send(const void* buffer, size_t length) {
152 int sent = ::send(native_socket_, buffer, length, 0);
154 if (sent == SOCKET_ERROR) {
158 // Reenable the writable callback (once), since we are flow controlled.
159 CFSocketEnableCallBacks(socket_, kCallbackFlags);
160 current_callbacks_ = kCallbackFlags;
166 // Send to the given address. We may or may not be connected to anyone.
167 int MacAsyncSocket::SendTo(const void* buffer, size_t length,
168 const SocketAddress& address) {
173 sockaddr_storage saddr;
174 size_t len = address.ToSockAddrStorage(&saddr);
175 int sent = ::sendto(native_socket_, buffer, length, 0,
176 reinterpret_cast<sockaddr*>(&saddr), len);
178 if (sent == SOCKET_ERROR) {
185 // Read data received from the remote end we're connected to.
186 int MacAsyncSocket::Recv(void* buffer, size_t length) {
187 int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer),
189 if (received == SOCKET_ERROR) error_ = errno;
191 // Recv should only be called when there is data to read
192 ASSERT((received != 0) || (length == 0));
196 // Read data received from any remote party
197 int MacAsyncSocket::RecvFrom(void* buffer, size_t length,
198 SocketAddress* out_addr) {
199 sockaddr_storage saddr;
200 socklen_t addr_len = sizeof(saddr);
201 int received = ::recvfrom(native_socket_, reinterpret_cast<char*>(buffer),
202 length, 0, reinterpret_cast<sockaddr*>(&saddr),
204 if (received >= 0 && out_addr != NULL) {
205 SocketAddressFromSockAddrStorage(saddr, out_addr);
206 } else if (received == SOCKET_ERROR) {
212 int MacAsyncSocket::Listen(int backlog) {
217 int res = ::listen(native_socket_, backlog);
218 if (res != SOCKET_ERROR)
219 state_ = CS_CONNECTING;
226 MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) {
227 sockaddr_storage saddr;
228 socklen_t addr_len = sizeof(saddr);
230 int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
232 if (socket_fd == INVALID_SOCKET) {
237 MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd);
238 if (s && s->valid()) {
239 s->state_ = CS_CONNECTED;
241 SocketAddressFromSockAddrStorage(saddr, out_addr);
249 int MacAsyncSocket::Close() {
250 if (source_ != NULL) {
251 CFRunLoopSourceInvalidate(source_);
253 if (ss_) ss_->UnregisterSocket(this);
257 if (socket_ != NULL) {
258 CFSocketInvalidate(socket_);
264 resolver_->Destroy(false);
268 native_socket_ = INVALID_SOCKET; // invalidates the socket
274 int MacAsyncSocket::EstimateMTU(uint16* mtu) {
275 ASSERT(false && "NYI");
279 int MacAsyncSocket::GetError() const {
283 void MacAsyncSocket::SetError(int error) {
287 Socket::ConnState MacAsyncSocket::GetState() const {
291 int MacAsyncSocket::GetOption(Option opt, int* value) {
292 ASSERT(false && "NYI");
296 int MacAsyncSocket::SetOption(Option opt, int value) {
297 ASSERT(false && "NYI");
301 void MacAsyncSocket::EnableCallbacks() {
304 CFSocketEnableCallBacks(socket_, current_callbacks_);
308 void MacAsyncSocket::DisableCallbacks() {
311 CFSocketDisableCallBacks(socket_, kCallbackFlags);
315 MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family,
319 native_socket_(native_socket),
321 current_callbacks_(0),
329 // Create a new socket, wrapping the native socket if provided or creating one
330 // otherwise. In case of any failure, consume the native socket. We assume the
331 // wrapped socket is in the closed state. If this is not the case you must
332 // update the state_ field for this socket yourself.
333 void MacAsyncSocket::Initialize(int family) {
334 CFSocketContext ctx = { 0 };
337 // First create the CFSocket
338 CFSocketRef cf_socket = NULL;
340 if (native_socket_ == INVALID_SOCKET) {
341 cf_socket = CFSocketCreate(kCFAllocatorDefault,
342 family, SOCK_STREAM, IPPROTO_TCP,
343 kCallbackFlags, MacAsyncSocketCallBack, &ctx);
345 cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
346 native_socket_, kCallbackFlags,
347 MacAsyncSocketCallBack, &ctx);
353 native_socket_ = CFSocketGetNative(cf_socket);
354 current_callbacks_ = kCallbackFlags;
358 // Make the underlying socket asynchronous
359 res = (-1 != ::fcntl(native_socket_, F_SETFL,
360 ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK));
364 // Add this socket to the run loop, at priority 1 so that it will be
365 // queued behind any pending signals.
366 source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1);
367 res = (source_ != NULL);
368 if (!res) errno = EINVAL;
372 if (ss_) ss_->RegisterSocket(this);
373 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes);
378 Close(); // Clears error_.
383 // Call CFRelease on the result when done using it
384 CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) {
385 sockaddr_storage saddr;
386 size_t len = address.ToSockAddrStorage(&saddr);
388 const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr);
390 CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault,
393 ASSERT(cf_address != NULL);
397 void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s,
398 CFSocketCallBackType callbackType,
402 MacAsyncSocket* this_socket =
403 reinterpret_cast<MacAsyncSocket*>(info);
404 ASSERT(this_socket != NULL && this_socket->socket_ == s);
406 // Don't signal any socket messages if the socketserver is not listening on
407 // them. When we are reenabled they will be requeued and will fire again.
408 if (this_socket->disabled_)
411 switch (callbackType) {
412 case kCFSocketReadCallBack:
413 // This callback is invoked in one of 3 situations:
414 // 1. A new connection is waiting to be accepted.
415 // 2. The remote end closed the connection (a recv will return 0).
416 // 3. Data is available to read.
417 // 4. The connection closed unhappily (recv will return -1).
418 if (this_socket->state_ == CS_CONNECTING) {
420 this_socket->SignalReadEvent(this_socket);
423 amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK);
426 this_socket->state_ = CS_CLOSED;
428 // Disable additional callbacks or we will signal close twice.
429 CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack);
430 this_socket->current_callbacks_ &= ~kCFSocketReadCallBack;
431 this_socket->SignalCloseEvent(this_socket, 0);
432 } else if (amt > 0) {
434 this_socket->SignalReadEvent(this_socket);
438 if (error == EAGAIN) {
439 // Observed in practice. Let's hope it's a spurious or out of date
440 // signal, since we just eat it.
442 this_socket->error_ = error;
443 this_socket->SignalCloseEvent(this_socket, error);
449 case kCFSocketConnectCallBack:
451 // An error occured in the background while connecting
452 this_socket->error_ = errno;
453 this_socket->state_ = CS_CLOSED;
454 this_socket->SignalCloseEvent(this_socket, this_socket->error_);
456 this_socket->state_ = CS_CONNECTED;
457 this_socket->SignalConnectEvent(this_socket);
461 case kCFSocketWriteCallBack:
462 // Update our callback tracking. Write doesn't reenable, so it's off now.
463 this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack;
464 this_socket->SignalWriteEvent(this_socket);
468 ASSERT(false && "Invalid callback type for socket");
472 } // namespace talk_base