• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 namespace talk_base {
21 
22 static const int kCallbackFlags = kCFSocketReadCallBack |
23                                   kCFSocketConnectCallBack |
24                                   kCFSocketWriteCallBack;
25 
MacAsyncSocket(MacBaseSocketServer * ss,int family)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 
~MacAsyncSocket()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.
GetLocalAddress() const45 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.
GetRemoteAddress() const63 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.
Bind(const SocketAddress & 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 
OnResolveResult(SignalThread * thread)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.
Connect(const SocketAddress & addr)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 
DoConnect(const SocketAddress & addr)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.
Send(const void * buffer,size_t length)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.
SendTo(const void * buffer,size_t length,const SocketAddress & address)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.
Recv(void * buffer,size_t length)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
RecvFrom(void * buffer,size_t length,SocketAddress * out_addr)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 
Listen(int backlog)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 
Accept(SocketAddress * out_addr)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 
Close()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 
EstimateMTU(uint16 * mtu)273 int MacAsyncSocket::EstimateMTU(uint16* mtu) {
274   ASSERT(false && "NYI");
275   return -1;
276 }
277 
GetError() const278 int MacAsyncSocket::GetError() const {
279   return error_;
280 }
281 
SetError(int error)282 void MacAsyncSocket::SetError(int error) {
283   error_ = error;
284 }
285 
GetState() const286 Socket::ConnState MacAsyncSocket::GetState() const {
287   return state_;
288 }
289 
GetOption(Option opt,int * value)290 int MacAsyncSocket::GetOption(Option opt, int* value) {
291   ASSERT(false && "NYI");
292   return -1;
293 }
294 
SetOption(Option opt,int value)295 int MacAsyncSocket::SetOption(Option opt, int value) {
296   ASSERT(false && "NYI");
297   return -1;
298 }
299 
EnableCallbacks()300 void MacAsyncSocket::EnableCallbacks() {
301   if (valid()) {
302     disabled_ = false;
303     CFSocketEnableCallBacks(socket_, current_callbacks_);
304   }
305 }
306 
DisableCallbacks()307 void MacAsyncSocket::DisableCallbacks() {
308   if (valid()) {
309     disabled_ = true;
310     CFSocketDisableCallBacks(socket_, kCallbackFlags);
311   }
312 }
313 
MacAsyncSocket(MacBaseSocketServer * ss,int family,int native_socket)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.
Initialize(int family)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
CopyCFAddress(const SocketAddress & address)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 
MacAsyncSocketCallBack(CFSocketRef s,CFSocketCallBackType callbackType,CFDataRef address,const void * data,void * info)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