blob: 54ad6046160e238520a0b9fb3a8ee9e8171386ed [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001// 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
20namespace talk_base {
21
22static const int kCallbackFlags = kCFSocketReadCallBack |
23 kCFSocketConnectCallBack |
24 kCFSocketWriteCallBack;
25
26MacAsyncSocket::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
39MacAsyncSocket::~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.
45SocketAddress 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.
63SocketAddress 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.
78int 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
86void MacAsyncSocket::OnResolveResult(SignalThread* thread) {
87 if (thread != resolver_) {
88 return;
89 }
90 int error = resolver_->error();
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.
103int 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_->set_address(addr);
113 resolver_->SignalWorkDone.connect(this,
114 &MacAsyncSocket::OnResolveResult);
115 resolver_->Start();
116 state_ = CS_CONNECTING;
117 return 0;
118 }
119 return DoConnect(addr);
120}
121
122int MacAsyncSocket::DoConnect(const SocketAddress& addr) {
123 if (!valid()) {
124 Initialize(addr.family());
125 if (!valid())
126 return SOCKET_ERROR;
127 }
128
129 sockaddr_storage saddr;
130 size_t len = addr.ToSockAddrStorage(&saddr);
131 int result = ::connect(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
132 len);
133
134 if (result != SOCKET_ERROR) {
135 state_ = CS_CONNECTED;
136 } else {
137 error_ = errno;
138 if (error_ == EINPROGRESS) {
139 state_ = CS_CONNECTING;
140 result = 0;
141 }
142 }
143 return result;
144}
145
146// Send to the remote end we're connected to.
147int MacAsyncSocket::Send(const void* buffer, size_t length) {
148 if (!valid()) {
149 return SOCKET_ERROR;
150 }
151
152 int sent = ::send(native_socket_, buffer, length, 0);
153
154 if (sent == SOCKET_ERROR) {
155 error_ = errno;
156
157 if (IsBlocking()) {
158 // Reenable the writable callback (once), since we are flow controlled.
159 CFSocketEnableCallBacks(socket_, kCallbackFlags);
160 current_callbacks_ = kCallbackFlags;
161 }
162 }
163 return sent;
164}
165
166// Send to the given address. We may or may not be connected to anyone.
167int MacAsyncSocket::SendTo(const void* buffer, size_t length,
168 const SocketAddress& address) {
169 if (!valid()) {
170 return SOCKET_ERROR;
171 }
172
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);
177
178 if (sent == SOCKET_ERROR) {
179 error_ = errno;
180 }
181
182 return sent;
183}
184
185// Read data received from the remote end we're connected to.
186int MacAsyncSocket::Recv(void* buffer, size_t length) {
187 int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer),
188 length, 0);
189 if (received == SOCKET_ERROR) error_ = errno;
190
191 // Recv should only be called when there is data to read
192 ASSERT((received != 0) || (length == 0));
193 return received;
194}
195
196// Read data received from any remote party
197int 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),
203 &addr_len);
204 if (received >= 0 && out_addr != NULL) {
205 SocketAddressFromSockAddrStorage(saddr, out_addr);
206 } else if (received == SOCKET_ERROR) {
207 error_ = errno;
208 }
209 return received;
210}
211
212int MacAsyncSocket::Listen(int backlog) {
213 if (!valid()) {
214 return SOCKET_ERROR;
215 }
216
217 int res = ::listen(native_socket_, backlog);
218 if (res != SOCKET_ERROR)
219 state_ = CS_CONNECTING;
220 else
221 error_ = errno;
222
223 return res;
224}
225
226MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) {
227 sockaddr_storage saddr;
228 socklen_t addr_len = sizeof(saddr);
229
230 int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
231 &addr_len);
232 if (socket_fd == INVALID_SOCKET) {
233 error_ = errno;
234 return NULL;
235 }
236
237 MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd);
238 if (s && s->valid()) {
239 s->state_ = CS_CONNECTED;
240 if (out_addr)
241 SocketAddressFromSockAddrStorage(saddr, out_addr);
242 } else {
243 delete s;
244 s = NULL;
245 }
246 return s;
247}
248
249int MacAsyncSocket::Close() {
250 if (source_ != NULL) {
251 CFRunLoopSourceInvalidate(source_);
252 CFRelease(source_);
253 if (ss_) ss_->UnregisterSocket(this);
254 source_ = NULL;
255 }
256
257 if (socket_ != NULL) {
258 CFSocketInvalidate(socket_);
259 CFRelease(socket_);
260 socket_ = NULL;
261 }
262
263 if (resolver_) {
264 resolver_->Destroy(false);
265 resolver_ = NULL;
266 }
267
268 native_socket_ = INVALID_SOCKET; // invalidates the socket
269 error_ = 0;
270 state_ = CS_CLOSED;
271 return 0;
272}
273
274int MacAsyncSocket::EstimateMTU(uint16* mtu) {
275 ASSERT(false && "NYI");
276 return -1;
277}
278
279int MacAsyncSocket::GetError() const {
280 return error_;
281}
282
283void MacAsyncSocket::SetError(int error) {
284 error_ = error;
285}
286
287Socket::ConnState MacAsyncSocket::GetState() const {
288 return state_;
289}
290
291int MacAsyncSocket::GetOption(Option opt, int* value) {
292 ASSERT(false && "NYI");
293 return -1;
294}
295
296int MacAsyncSocket::SetOption(Option opt, int value) {
297 ASSERT(false && "NYI");
298 return -1;
299}
300
301void MacAsyncSocket::EnableCallbacks() {
302 if (valid()) {
303 disabled_ = false;
304 CFSocketEnableCallBacks(socket_, current_callbacks_);
305 }
306}
307
308void MacAsyncSocket::DisableCallbacks() {
309 if (valid()) {
310 disabled_ = true;
311 CFSocketDisableCallBacks(socket_, kCallbackFlags);
312 }
313}
314
315MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family,
316 int native_socket)
317 : ss_(ss),
318 socket_(NULL),
319 native_socket_(native_socket),
320 source_(NULL),
321 current_callbacks_(0),
322 disabled_(false),
323 error_(0),
324 state_(CS_CLOSED),
325 resolver_(NULL) {
326 Initialize(family);
327}
328
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.
333void MacAsyncSocket::Initialize(int family) {
334 CFSocketContext ctx = { 0 };
335 ctx.info = this;
336
337 // First create the CFSocket
338 CFSocketRef cf_socket = NULL;
339 bool res = false;
340 if (native_socket_ == INVALID_SOCKET) {
341 cf_socket = CFSocketCreate(kCFAllocatorDefault,
342 family, SOCK_STREAM, IPPROTO_TCP,
343 kCallbackFlags, MacAsyncSocketCallBack, &ctx);
344 } else {
345 cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
346 native_socket_, kCallbackFlags,
347 MacAsyncSocketCallBack, &ctx);
348 }
349
350 if (cf_socket) {
351 res = true;
352 socket_ = cf_socket;
353 native_socket_ = CFSocketGetNative(cf_socket);
354 current_callbacks_ = kCallbackFlags;
355 }
356
357 if (res) {
358 // Make the underlying socket asynchronous
359 res = (-1 != ::fcntl(native_socket_, F_SETFL,
360 ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK));
361 }
362
363 if (res) {
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;
369 }
370
371 if (res) {
372 if (ss_) ss_->RegisterSocket(this);
373 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes);
374 }
375
376 if (!res) {
377 int error = errno;
378 Close(); // Clears error_.
379 error_ = error;
380 }
381}
382
383// Call CFRelease on the result when done using it
384CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) {
385 sockaddr_storage saddr;
386 size_t len = address.ToSockAddrStorage(&saddr);
387
388 const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr);
389
390 CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault,
391 bytes, len);
392
393 ASSERT(cf_address != NULL);
394 return cf_address;
395}
396
397void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s,
398 CFSocketCallBackType callbackType,
399 CFDataRef address,
400 const void* data,
401 void* info) {
402 MacAsyncSocket* this_socket =
403 reinterpret_cast<MacAsyncSocket*>(info);
404 ASSERT(this_socket != NULL && this_socket->socket_ == s);
405
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_)
409 return;
410
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) {
419 // Case 1.
420 this_socket->SignalReadEvent(this_socket);
421 } else {
422 char ch, amt;
423 amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK);
424 if (amt == 0) {
425 // Case 2.
426 this_socket->state_ = CS_CLOSED;
427
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) {
433 // Case 3.
434 this_socket->SignalReadEvent(this_socket);
435 } else {
436 // Case 4.
437 int error = errno;
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.
441 } else {
442 this_socket->error_ = error;
443 this_socket->SignalCloseEvent(this_socket, error);
444 }
445 }
446 }
447 break;
448
449 case kCFSocketConnectCallBack:
450 if (data != NULL) {
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_);
455 } else {
456 this_socket->state_ = CS_CONNECTED;
457 this_socket->SignalConnectEvent(this_socket);
458 }
459 break;
460
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);
465 break;
466
467 default:
468 ASSERT(false && "Invalid callback type for socket");
469 }
470}
471
472} // namespace talk_base