160 lines
4.4 KiB
C++
160 lines
4.4 KiB
C++
//
|
|
// 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 <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
|