| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/async_connection.h" |
| |
| #include <base/bind.h> |
| #include <errno.h> |
| #include <netinet/in.h> |
| |
| #include <string> |
| |
| #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<void(bool, int)> &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<struct sockaddr *>(&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<struct sockaddr *>(&iaddr6); |
| addr_len = sizeof(iaddr6); |
| } else { |
| NOTREACHED(); |
| } |
| |
| return sockets_->Connect(fd_, sock_addr, addr_len); |
| } |
| |
| } // namespace shill |