henrike@webrtc.org | 47be73b | 2014-05-13 18:00:26 +0000 | [diff] [blame] | 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.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 | |
| 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) { |
| 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 |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 279 | int MacAsyncSocket::EstimateMTU(uint16* mtu) { |
| 280 | ASSERT(false && "NYI"); |
| 281 | return -1; |
| 282 | } |
| 283 | |
| 284 | int MacAsyncSocket::GetError() const { |
| 285 | return error_; |
| 286 | } |
| 287 | |
| 288 | void MacAsyncSocket::SetError(int error) { |
| 289 | error_ = error; |
| 290 | } |
| 291 | |
| 292 | Socket::ConnState MacAsyncSocket::GetState() const { |
| 293 | return state_; |
| 294 | } |
| 295 | |
| 296 | int MacAsyncSocket::GetOption(Option opt, int* value) { |
| 297 | ASSERT(false && "NYI"); |
| 298 | return -1; |
| 299 | } |
| 300 | |
| 301 | int MacAsyncSocket::SetOption(Option opt, int value) { |
| 302 | ASSERT(false && "NYI"); |
| 303 | return -1; |
| 304 | } |
| 305 | |
| 306 | void MacAsyncSocket::EnableCallbacks() { |
| 307 | if (valid()) { |
| 308 | disabled_ = false; |
| 309 | CFSocketEnableCallBacks(socket_, current_callbacks_); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | void MacAsyncSocket::DisableCallbacks() { |
| 314 | if (valid()) { |
| 315 | disabled_ = true; |
| 316 | CFSocketDisableCallBacks(socket_, kCallbackFlags); |
| 317 | } |
| 318 | } |
| 319 | |
| 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. |
| 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 |
| 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 | |
| 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 |