blob: 63530ec7f661fdbd85798e2db66dfbe9b4385c56 [file] [log] [blame]
// Copyright (c) 2011 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 <errno.h>
#include <netinet/in.h>
#include <string>
#include "shill/event_dispatcher.h"
#include "shill/ip_address.h"
#include "shill/sockets.h"
using std::string;
namespace shill {
AsyncConnection::AsyncConnection(const string &interface_name,
EventDispatcher *dispatcher,
Sockets *sockets,
Callback2<bool, int>::Type *callback)
: interface_name_(interface_name),
dispatcher_(dispatcher),
sockets_(sockets),
callback_(callback),
fd_(-1),
connect_completion_callback_(
NewCallback(this, &AsyncConnection::OnConnectCompletion)) { }
AsyncConnection::~AsyncConnection() {
Stop();
}
bool AsyncConnection::Start(const IPAddress &address, int port) {
DCHECK(fd_ < 0);
fd_ = sockets_->Socket(PF_INET, 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;
}
struct sockaddr_in iaddr;
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);
socklen_t addrlen = sizeof(iaddr);
int ret = sockets_->Connect(fd_,
reinterpret_cast<struct sockaddr *>(&iaddr),
addrlen);
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_.get()));
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);
if (sockets_->GetSocketError(fd_) != 0) {
error_ = sockets_->ErrorString();
PLOG(ERROR) << "Async GetSocketError returns failure";
callback_->Run(false, -1);
} else {
callback_->Run(true, fd_); // Passes ownership
fd_ = -1;
}
Stop();
}
} // namespace shill