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 26namespace rtc { 27 28static const int kCallbackFlags = kCFSocketReadCallBack | 29 kCFSocketConnectCallBack | 30 kCFSocketWriteCallBack; 31 32MacAsyncSocket::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 45MacAsyncSocket::~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. 51SocketAddress 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. 69SocketAddress 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. 84int 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 92void 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. 109int 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 127int 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. 152int 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. 172int 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. 191int 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 202int 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 217int 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 231MacAsyncSocket* 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 254int 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 279int MacAsyncSocket::EstimateMTU(uint16_t* mtu) { 280 ASSERT(false && "NYI"); 281 return -1; 282} 283 284int MacAsyncSocket::GetError() const { 285 return error_; 286} 287 288void MacAsyncSocket::SetError(int error) { 289 error_ = error; 290} 291 292Socket::ConnState MacAsyncSocket::GetState() const { 293 return state_; 294} 295 296int MacAsyncSocket::GetOption(Option opt, int* value) { 297 ASSERT(false && "NYI"); 298 return -1; 299} 300 301int MacAsyncSocket::SetOption(Option opt, int value) { 302 ASSERT(false && "NYI"); 303 return -1; 304} 305 306void MacAsyncSocket::EnableCallbacks() { 307 if (valid()) { 308 disabled_ = false; 309 CFSocketEnableCallBacks(socket_, current_callbacks_); 310 } 311} 312 313void MacAsyncSocket::DisableCallbacks() { 314 if (valid()) { 315 disabled_ = true; 316 CFSocketDisableCallBacks(socket_, kCallbackFlags); 317 } 318} 319 320MacAsyncSocket::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. 338void 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 389CFDataRef 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 402void 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