blob: cd908b0b5710c97253ee0093b2a9e3678993a530 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "garnet/bin/guest/mgr/host_vsock_endpoint.h"
#include <fbl/auto_call.h>
#include <lib/async/default.h>
#include "lib/fxl/logging.h"
namespace guestmgr {
HostVsockEndpoint::HostVsockEndpoint(uint32_t cid) : VsockEndpoint(cid) {}
HostVsockEndpoint::~HostVsockEndpoint() = default;
void HostVsockEndpoint::AddBinding(
fidl::InterfaceRequest<fuchsia::guest::ManagedSocketEndpoint> request) {
bindings_.AddBinding(this, std::move(request));
}
void HostVsockEndpoint::Accept(uint32_t src_cid, uint32_t src_port,
uint32_t port, AcceptCallback callback) {
auto it = listeners_.find(port);
if (it == listeners_.end()) {
callback(ZX_ERR_CONNECTION_REFUSED, zx::socket());
return;
}
it->second->Accept(src_cid, src_port, port, std::move(callback));
}
void HostVsockEndpoint::Listen(uint32_t port,
fidl::InterfaceHandle<SocketAcceptor> acceptor,
ListenCallback callback) {
if (port_bitmap_.GetOne(port)) {
callback(ZX_ERR_ALREADY_BOUND);
return;
}
bool inserted;
auto acceptor_ptr = acceptor.Bind();
acceptor_ptr.set_error_handler([this, port]() {
port_bitmap_.ClearOne(port);
listeners_.erase(port);
});
std::tie(std::ignore, inserted) =
listeners_.emplace(port, std::move(acceptor_ptr));
if (!inserted) {
callback(ZX_ERR_ALREADY_BOUND);
return;
}
port_bitmap_.SetOne(port);
callback(ZX_OK);
}
void HostVsockEndpoint::Connect(
uint32_t cid, uint32_t port,
fuchsia::guest::ManagedSocketEndpoint::ConnectCallback callback) {
uint32_t src_port;
zx_status_t status = FindEphemeralPort(&src_port);
if (status != ZX_OK) {
callback(ZX_ERR_NO_RESOURCES, zx::socket());
return;
}
Connect(src_port, cid, port,
[this, src_port, callback = std::move(callback)](zx_status_t status,
zx::socket socket) mutable {
ConnectCallback(status, std::move(socket), src_port,
std::move(callback));
});
}
void HostVsockEndpoint::ConnectCallback(
zx_status_t status, zx::socket socket, uint32_t src_port,
fuchsia::guest::ManagedSocketEndpoint::ConnectCallback callback) {
auto free_port =
fbl::MakeAutoCall([this, src_port]() { FreePort(src_port); });
if (status != ZX_OK) {
callback(status, std::move(socket));
return;
}
auto conn = std::make_unique<Connection>();
conn->port = src_port;
status = socket.duplicate(ZX_RIGHT_WAIT, &conn->socket);
if (status != ZX_OK) {
callback(ZX_ERR_CONNECTION_REFUSED, zx::socket());
return;
}
conn->wait.set_trigger(ZX_SOCKET_PEER_CLOSED);
conn->wait.set_object(conn->socket.get());
conn->wait.set_handler(
[this, conn = conn.get()](...) { OnPeerClosed(conn); });
status = conn->wait.Begin(async_get_default());
if (status != ZX_OK) {
callback(ZX_ERR_CONNECTION_REFUSED, zx::socket());
return;
}
free_port.cancel();
connections_.emplace(conn->port, std::move(conn));
callback(ZX_OK, std::move(socket));
}
void HostVsockEndpoint::OnPeerClosed(Connection* conn) {
FreePort(conn->port);
connections_.erase(conn->port);
}
zx_status_t HostVsockEndpoint::FindEphemeralPort(uint32_t* port) {
size_t value;
zx_status_t status = port_bitmap_.Find(false, kFirstEphemeralPort,
kLastEphemeralPort, 1, &value);
if (status != ZX_OK) {
return ZX_ERR_NOT_FOUND;
}
status = port_bitmap_.SetOne(value);
*port = value;
return ZX_OK;
}
zx_status_t HostVsockEndpoint::FreePort(uint32_t port) {
return port_bitmap_.ClearOne(port);
}
} // namespace guestmgr