• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 namespace rtc {
27 
28 static const int kCallbackFlags = kCFSocketReadCallBack |
29                                   kCFSocketConnectCallBack |
30                                   kCFSocketWriteCallBack;
31 
MacAsyncSocket(MacBaseSocketServer * ss,int family)32 MacAsyncSocket::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 
~MacAsyncSocket()45 MacAsyncSocket::~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.
GetLocalAddress() const51 SocketAddress 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.
GetRemoteAddress() const69 SocketAddress 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.
Bind(const SocketAddress & address)84 int 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 
OnResolveResult(SignalThread * thread)92 void 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.
Connect(const SocketAddress & addr)109 int 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.IsUnresolved()) {
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 
DoConnect(const SocketAddress & addr)127 int 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.
Send(const void * buffer,size_t length)152 int 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.
SendTo(const void * buffer,size_t length,const SocketAddress & address)172 int 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.
Recv(void * buffer,size_t length)191 int 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
RecvFrom(void * buffer,size_t length,SocketAddress * out_addr)202 int 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 
Listen(int backlog)217 int 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 
Accept(SocketAddress * out_addr)231 MacAsyncSocket* 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 
Close()254 int 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 
EstimateMTU(uint16 * mtu)279 int MacAsyncSocket::EstimateMTU(uint16* mtu) {
280   ASSERT(false && "NYI");
281   return -1;
282 }
283 
GetError() const284 int MacAsyncSocket::GetError() const {
285   return error_;
286 }
287 
SetError(int error)288 void MacAsyncSocket::SetError(int error) {
289   error_ = error;
290 }
291 
GetState() const292 Socket::ConnState MacAsyncSocket::GetState() const {
293   return state_;
294 }
295 
GetOption(Option opt,int * value)296 int MacAsyncSocket::GetOption(Option opt, int* value) {
297   ASSERT(false && "NYI");
298   return -1;
299 }
300 
SetOption(Option opt,int value)301 int MacAsyncSocket::SetOption(Option opt, int value) {
302   ASSERT(false && "NYI");
303   return -1;
304 }
305 
EnableCallbacks()306 void MacAsyncSocket::EnableCallbacks() {
307   if (valid()) {
308     disabled_ = false;
309     CFSocketEnableCallBacks(socket_, current_callbacks_);
310   }
311 }
312 
DisableCallbacks()313 void MacAsyncSocket::DisableCallbacks() {
314   if (valid()) {
315     disabled_ = true;
316     CFSocketDisableCallBacks(socket_, kCallbackFlags);
317   }
318 }
319 
MacAsyncSocket(MacBaseSocketServer * ss,int family,int native_socket)320 MacAsyncSocket::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.
Initialize(int family)338 void 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
CopyCFAddress(const SocketAddress & address)389 CFDataRef 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 
MacAsyncSocketCallBack(CFSocketRef s,CFSocketCallBackType callbackType,CFDataRef address,const void * data,void * info)402 void 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