OLD | NEW |
| (Empty) |
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 | |
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 | |
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. | |
51 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. | |
69 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. | |
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 | |
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. | |
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.IsUnresolvedIP()) { | |
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 | |
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. | |
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. | |
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. | |
191 int MacAsyncSocket::Recv(void* buffer, size_t length, int64_t* timestamp) { | |
192 if (timestamp) { | |
193 *timestamp = -1; | |
194 } | |
195 int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer), | |
196 length, 0); | |
197 if (received == SOCKET_ERROR) error_ = errno; | |
198 | |
199 // Recv should only be called when there is data to read | |
200 ASSERT((received != 0) || (length == 0)); | |
201 return received; | |
202 } | |
203 | |
204 // Read data received from any remote party | |
205 int MacAsyncSocket::RecvFrom(void* buffer, | |
206 size_t length, | |
207 SocketAddress* out_addr, | |
208 int64_t* timestamp) { | |
209 if (timestamp) { | |
210 *timestamp = -1; | |
211 } | |
212 sockaddr_storage saddr; | |
213 socklen_t addr_len = sizeof(saddr); | |
214 int received = ::recvfrom(native_socket_, reinterpret_cast<char*>(buffer), | |
215 length, 0, reinterpret_cast<sockaddr*>(&saddr), | |
216 &addr_len); | |
217 if (received >= 0 && out_addr != NULL) { | |
218 SocketAddressFromSockAddrStorage(saddr, out_addr); | |
219 } else if (received == SOCKET_ERROR) { | |
220 error_ = errno; | |
221 } | |
222 return received; | |
223 } | |
224 | |
225 int MacAsyncSocket::Listen(int backlog) { | |
226 if (!valid()) { | |
227 return SOCKET_ERROR; | |
228 } | |
229 | |
230 int res = ::listen(native_socket_, backlog); | |
231 if (res != SOCKET_ERROR) | |
232 state_ = CS_CONNECTING; | |
233 else | |
234 error_ = errno; | |
235 | |
236 return res; | |
237 } | |
238 | |
239 MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) { | |
240 sockaddr_storage saddr; | |
241 socklen_t addr_len = sizeof(saddr); | |
242 | |
243 int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr), | |
244 &addr_len); | |
245 if (socket_fd == INVALID_SOCKET) { | |
246 error_ = errno; | |
247 return NULL; | |
248 } | |
249 | |
250 MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd); | |
251 if (s && s->valid()) { | |
252 s->state_ = CS_CONNECTED; | |
253 if (out_addr) | |
254 SocketAddressFromSockAddrStorage(saddr, out_addr); | |
255 } else { | |
256 delete s; | |
257 s = NULL; | |
258 } | |
259 return s; | |
260 } | |
261 | |
262 int MacAsyncSocket::Close() { | |
263 if (source_ != NULL) { | |
264 CFRunLoopSourceInvalidate(source_); | |
265 CFRelease(source_); | |
266 if (ss_) ss_->UnregisterSocket(this); | |
267 source_ = NULL; | |
268 } | |
269 | |
270 if (socket_ != NULL) { | |
271 CFSocketInvalidate(socket_); | |
272 CFRelease(socket_); | |
273 socket_ = NULL; | |
274 } | |
275 | |
276 if (resolver_) { | |
277 resolver_->Destroy(false); | |
278 resolver_ = NULL; | |
279 } | |
280 | |
281 native_socket_ = INVALID_SOCKET; // invalidates the socket | |
282 error_ = 0; | |
283 state_ = CS_CLOSED; | |
284 return 0; | |
285 } | |
286 | |
287 int MacAsyncSocket::EstimateMTU(uint16_t* mtu) { | |
288 ASSERT(false && "NYI"); | |
289 return -1; | |
290 } | |
291 | |
292 int MacAsyncSocket::GetError() const { | |
293 return error_; | |
294 } | |
295 | |
296 void MacAsyncSocket::SetError(int error) { | |
297 error_ = error; | |
298 } | |
299 | |
300 Socket::ConnState MacAsyncSocket::GetState() const { | |
301 return state_; | |
302 } | |
303 | |
304 int MacAsyncSocket::GetOption(Option opt, int* value) { | |
305 ASSERT(false && "NYI"); | |
306 return -1; | |
307 } | |
308 | |
309 int MacAsyncSocket::SetOption(Option opt, int value) { | |
310 ASSERT(false && "NYI"); | |
311 return -1; | |
312 } | |
313 | |
314 void MacAsyncSocket::EnableCallbacks() { | |
315 if (valid()) { | |
316 disabled_ = false; | |
317 CFSocketEnableCallBacks(socket_, current_callbacks_); | |
318 } | |
319 } | |
320 | |
321 void MacAsyncSocket::DisableCallbacks() { | |
322 if (valid()) { | |
323 disabled_ = true; | |
324 CFSocketDisableCallBacks(socket_, kCallbackFlags); | |
325 } | |
326 } | |
327 | |
328 MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family, | |
329 int native_socket) | |
330 : ss_(ss), | |
331 socket_(NULL), | |
332 native_socket_(native_socket), | |
333 source_(NULL), | |
334 current_callbacks_(0), | |
335 disabled_(false), | |
336 error_(0), | |
337 state_(CS_CLOSED), | |
338 resolver_(NULL) { | |
339 Initialize(family); | |
340 } | |
341 | |
342 // Create a new socket, wrapping the native socket if provided or creating one | |
343 // otherwise. In case of any failure, consume the native socket. We assume the | |
344 // wrapped socket is in the closed state. If this is not the case you must | |
345 // update the state_ field for this socket yourself. | |
346 void MacAsyncSocket::Initialize(int family) { | |
347 CFSocketContext ctx = { 0 }; | |
348 ctx.info = this; | |
349 | |
350 // First create the CFSocket | |
351 CFSocketRef cf_socket = NULL; | |
352 bool res = false; | |
353 if (native_socket_ == INVALID_SOCKET) { | |
354 cf_socket = CFSocketCreate(kCFAllocatorDefault, | |
355 family, SOCK_STREAM, IPPROTO_TCP, | |
356 kCallbackFlags, MacAsyncSocketCallBack, &ctx); | |
357 } else { | |
358 cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault, | |
359 native_socket_, kCallbackFlags, | |
360 MacAsyncSocketCallBack, &ctx); | |
361 } | |
362 | |
363 if (cf_socket) { | |
364 res = true; | |
365 socket_ = cf_socket; | |
366 native_socket_ = CFSocketGetNative(cf_socket); | |
367 current_callbacks_ = kCallbackFlags; | |
368 } | |
369 | |
370 if (res) { | |
371 // Make the underlying socket asynchronous | |
372 res = (-1 != ::fcntl(native_socket_, F_SETFL, | |
373 ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK)); | |
374 } | |
375 | |
376 if (res) { | |
377 // Add this socket to the run loop, at priority 1 so that it will be | |
378 // queued behind any pending signals. | |
379 source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1); | |
380 res = (source_ != NULL); | |
381 if (!res) errno = EINVAL; | |
382 } | |
383 | |
384 if (res) { | |
385 if (ss_) ss_->RegisterSocket(this); | |
386 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes); | |
387 } | |
388 | |
389 if (!res) { | |
390 int error = errno; | |
391 Close(); // Clears error_. | |
392 error_ = error; | |
393 } | |
394 } | |
395 | |
396 // Call CFRelease on the result when done using it | |
397 CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) { | |
398 sockaddr_storage saddr; | |
399 size_t len = address.ToSockAddrStorage(&saddr); | |
400 | |
401 const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr); | |
402 | |
403 CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault, | |
404 bytes, len); | |
405 | |
406 ASSERT(cf_address != NULL); | |
407 return cf_address; | |
408 } | |
409 | |
410 void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s, | |
411 CFSocketCallBackType callbackType, | |
412 CFDataRef address, | |
413 const void* data, | |
414 void* info) { | |
415 MacAsyncSocket* this_socket = | |
416 reinterpret_cast<MacAsyncSocket*>(info); | |
417 ASSERT(this_socket != NULL && this_socket->socket_ == s); | |
418 | |
419 // Don't signal any socket messages if the socketserver is not listening on | |
420 // them. When we are reenabled they will be requeued and will fire again. | |
421 if (this_socket->disabled_) | |
422 return; | |
423 | |
424 switch (callbackType) { | |
425 case kCFSocketReadCallBack: | |
426 // This callback is invoked in one of 3 situations: | |
427 // 1. A new connection is waiting to be accepted. | |
428 // 2. The remote end closed the connection (a recv will return 0). | |
429 // 3. Data is available to read. | |
430 // 4. The connection closed unhappily (recv will return -1). | |
431 if (this_socket->state_ == CS_CONNECTING) { | |
432 // Case 1. | |
433 this_socket->SignalReadEvent(this_socket); | |
434 } else { | |
435 char ch, amt; | |
436 amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK); | |
437 if (amt == 0) { | |
438 // Case 2. | |
439 this_socket->state_ = CS_CLOSED; | |
440 | |
441 // Disable additional callbacks or we will signal close twice. | |
442 CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack); | |
443 this_socket->current_callbacks_ &= ~kCFSocketReadCallBack; | |
444 this_socket->SignalCloseEvent(this_socket, 0); | |
445 } else if (amt > 0) { | |
446 // Case 3. | |
447 this_socket->SignalReadEvent(this_socket); | |
448 } else { | |
449 // Case 4. | |
450 int error = errno; | |
451 if (error == EAGAIN) { | |
452 // Observed in practice. Let's hope it's a spurious or out of date | |
453 // signal, since we just eat it. | |
454 } else { | |
455 this_socket->error_ = error; | |
456 this_socket->SignalCloseEvent(this_socket, error); | |
457 } | |
458 } | |
459 } | |
460 break; | |
461 | |
462 case kCFSocketConnectCallBack: | |
463 if (data != NULL) { | |
464 // An error occured in the background while connecting | |
465 this_socket->error_ = errno; | |
466 this_socket->state_ = CS_CLOSED; | |
467 this_socket->SignalCloseEvent(this_socket, this_socket->error_); | |
468 } else { | |
469 this_socket->state_ = CS_CONNECTED; | |
470 this_socket->SignalConnectEvent(this_socket); | |
471 } | |
472 break; | |
473 | |
474 case kCFSocketWriteCallBack: | |
475 // Update our callback tracking. Write doesn't reenable, so it's off now. | |
476 this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack; | |
477 this_socket->SignalWriteEvent(this_socket); | |
478 break; | |
479 | |
480 default: | |
481 ASSERT(false && "Invalid callback type for socket"); | |
482 } | |
483 } | |
484 | |
485 } // namespace rtc | |
OLD | NEW |