// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/async_connection.h" #include #include #include #include #include "shill/event_dispatcher.h" #include "shill/net/ip_address.h" #include "shill/net/sockets.h" using base::Bind; using base::Callback; using base::Unretained; using std::string; namespace shill { AsyncConnection::AsyncConnection(const string& interface_name, EventDispatcher* dispatcher, Sockets* sockets, const Callback& callback) : interface_name_(interface_name), dispatcher_(dispatcher), sockets_(sockets), callback_(callback), fd_(-1), connect_completion_callback_( Bind(&AsyncConnection::OnConnectCompletion, Unretained(this))) { } AsyncConnection::~AsyncConnection() { Stop(); } bool AsyncConnection::Start(const IPAddress& address, int port) { DCHECK_LT(fd_, 0); int family = PF_INET; if (address.family() == IPAddress::kFamilyIPv6) { family = PF_INET6; } fd_ = sockets_->Socket(family, SOCK_STREAM, 0); if (fd_ < 0 || sockets_->SetNonBlocking(fd_) < 0) { error_ = sockets_->ErrorString(); PLOG(ERROR) << "Async socket setup failed"; Stop(); return false; } if (!interface_name_.empty() && sockets_->BindToDevice(fd_, interface_name_) < 0) { error_ = sockets_->ErrorString(); PLOG(ERROR) << "Async socket failed to bind to device"; Stop(); return false; } int ret = ConnectTo(address, port); if (ret == 0) { callback_.Run(true, fd_); // Passes ownership fd_ = -1; return true; } if (sockets_->Error() != EINPROGRESS) { error_ = sockets_->ErrorString(); PLOG(ERROR) << "Async socket connection failed"; Stop(); return false; } connect_completion_handler_.reset( dispatcher_->CreateReadyHandler(fd_, IOHandler::kModeOutput, connect_completion_callback_)); error_ = string(); return true; } void AsyncConnection::Stop() { connect_completion_handler_.reset(); if (fd_ >= 0) { sockets_->Close(fd_); fd_ = -1; } } void AsyncConnection::OnConnectCompletion(int fd) { CHECK_EQ(fd_, fd); bool success = false; int returned_fd = -1; if (sockets_->GetSocketError(fd_) != 0) { error_ = sockets_->ErrorString(); PLOG(ERROR) << "Async GetSocketError returns failure"; } else { returned_fd = fd_; fd_ = -1; success = true; } Stop(); // Run the callback last, since it may end up freeing this instance. callback_.Run(success, returned_fd); // Passes ownership } int AsyncConnection::ConnectTo(const IPAddress& address, int port) { struct sockaddr* sock_addr = nullptr; socklen_t addr_len = 0; struct sockaddr_in iaddr; struct sockaddr_in6 iaddr6; if (address.family() == IPAddress::kFamilyIPv4) { CHECK_EQ(sizeof(iaddr.sin_addr.s_addr), address.GetLength()); memset(&iaddr, 0, sizeof(iaddr)); iaddr.sin_family = AF_INET; memcpy(&iaddr.sin_addr.s_addr, address.address().GetConstData(), sizeof(iaddr.sin_addr.s_addr)); iaddr.sin_port = htons(port); sock_addr = reinterpret_cast(&iaddr); addr_len = sizeof(iaddr); } else if (address.family() == IPAddress::kFamilyIPv6) { CHECK_EQ(sizeof(iaddr6.sin6_addr.s6_addr), address.GetLength()); memset(&iaddr6, 0, sizeof(iaddr6)); iaddr6.sin6_family = AF_INET6; memcpy(&iaddr6.sin6_addr.s6_addr, address.address().GetConstData(), sizeof(iaddr6.sin6_addr.s6_addr)); iaddr6.sin6_port = htons(port); sock_addr = reinterpret_cast(&iaddr6); addr_len = sizeof(iaddr6); } else { NOTREACHED(); } return sockets_->Connect(fd_, sock_addr, addr_len); } } // namespace shill