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