1 // Copyright 2010 Google Inc. All Rights Reserved. 2 3 // thaloun (at) 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 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 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. 45 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. 63 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. 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 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. 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 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. 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. 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. 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 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 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 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 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 273 int MacAsyncSocket::EstimateMTU(uint16* mtu) { 274 ASSERT(false && "NYI"); 275 return -1; 276 } 277 278 int MacAsyncSocket::GetError() const { 279 return error_; 280 } 281 282 void MacAsyncSocket::SetError(int error) { 283 error_ = error; 284 } 285 286 Socket::ConnState MacAsyncSocket::GetState() const { 287 return state_; 288 } 289 290 int MacAsyncSocket::GetOption(Option opt, int* value) { 291 ASSERT(false && "NYI"); 292 return -1; 293 } 294 295 int MacAsyncSocket::SetOption(Option opt, int value) { 296 ASSERT(false && "NYI"); 297 return -1; 298 } 299 300 void MacAsyncSocket::EnableCallbacks() { 301 if (valid()) { 302 disabled_ = false; 303 CFSocketEnableCallBacks(socket_, current_callbacks_); 304 } 305 } 306 307 void MacAsyncSocket::DisableCallbacks() { 308 if (valid()) { 309 disabled_ = true; 310 CFSocketDisableCallBacks(socket_, kCallbackFlags); 311 } 312 } 313 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. 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 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 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