2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-09-26 22:53:21 -07:00
|
|
|
|
|
|
|
|
#include "wpi/PortForwarder.h"
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include "fmt/format.h"
|
2019-09-26 22:53:21 -07:00
|
|
|
#include "wpi/DenseMap.h"
|
|
|
|
|
#include "wpi/EventLoopRunner.h"
|
|
|
|
|
#include "wpi/uv/GetAddrInfo.h"
|
|
|
|
|
#include "wpi/uv/Tcp.h"
|
|
|
|
|
#include "wpi/uv/Timer.h"
|
|
|
|
|
|
|
|
|
|
using namespace wpi;
|
|
|
|
|
|
|
|
|
|
struct PortForwarder::Impl {
|
|
|
|
|
public:
|
|
|
|
|
EventLoopRunner runner;
|
|
|
|
|
DenseMap<unsigned int, std::weak_ptr<uv::Tcp>> servers;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PortForwarder::PortForwarder() : m_impl{new Impl} {}
|
|
|
|
|
|
|
|
|
|
PortForwarder& PortForwarder::GetInstance() {
|
|
|
|
|
static PortForwarder instance;
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CopyStream(uv::Stream& in, std::weak_ptr<uv::Stream> outWeak) {
|
|
|
|
|
in.data.connect([&in, outWeak](uv::Buffer& buf, size_t len) {
|
|
|
|
|
uv::Buffer buf2 = buf.Dup();
|
|
|
|
|
buf2.len = len;
|
|
|
|
|
auto out = outWeak.lock();
|
|
|
|
|
if (!out) {
|
2021-01-01 10:27:49 -08:00
|
|
|
buf2.Deallocate();
|
2019-09-26 22:53:21 -07:00
|
|
|
in.Close();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-06 19:51:14 -07:00
|
|
|
out->Write({buf2}, [](auto bufs, uv::Error) {
|
2020-12-28 12:58:06 -08:00
|
|
|
for (auto buf : bufs) {
|
|
|
|
|
buf.Deallocate();
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
void PortForwarder::Add(unsigned int port, std::string_view remoteHost,
|
2019-09-26 22:53:21 -07:00
|
|
|
unsigned int remotePort) {
|
|
|
|
|
m_impl->runner.ExecSync([&](uv::Loop& loop) {
|
|
|
|
|
auto server = uv::Tcp::Create(loop);
|
|
|
|
|
|
|
|
|
|
// bind to local port
|
|
|
|
|
server->Bind("", port);
|
|
|
|
|
|
|
|
|
|
// when we get a connection, accept it
|
|
|
|
|
server->connection.connect([serverPtr = server.get(),
|
2021-06-06 16:13:58 -07:00
|
|
|
host = std::string{remoteHost}, remotePort] {
|
2019-09-26 22:53:21 -07:00
|
|
|
auto& loop = serverPtr->GetLoopRef();
|
|
|
|
|
auto client = serverPtr->Accept();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!client) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
|
|
|
|
|
// close on error
|
|
|
|
|
client->error.connect(
|
|
|
|
|
[clientPtr = client.get()](uv::Error err) { clientPtr->Close(); });
|
|
|
|
|
|
|
|
|
|
// connected flag
|
|
|
|
|
auto connected = std::make_shared<bool>(false);
|
|
|
|
|
client->SetData(connected);
|
|
|
|
|
|
|
|
|
|
auto remote = uv::Tcp::Create(loop);
|
|
|
|
|
remote->error.connect(
|
|
|
|
|
[remotePtr = remote.get(),
|
|
|
|
|
clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
|
|
|
|
|
remotePtr->Close();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (auto client = clientWeak.lock()) {
|
|
|
|
|
client->Close();
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// resolve address
|
|
|
|
|
uv::GetAddrInfo(
|
|
|
|
|
loop,
|
|
|
|
|
[clientWeak = std::weak_ptr<uv::Tcp>(client),
|
|
|
|
|
remoteWeak = std::weak_ptr<uv::Tcp>(remote)](const addrinfo& addr) {
|
|
|
|
|
auto remote = remoteWeak.lock();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!remote) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
|
|
|
|
|
// connect to remote address/port
|
|
|
|
|
remote->Connect(*addr.ai_addr, [remotePtr = remote.get(),
|
|
|
|
|
remoteWeak, clientWeak] {
|
|
|
|
|
auto client = clientWeak.lock();
|
|
|
|
|
if (!client) {
|
|
|
|
|
remotePtr->Close();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
*(client->GetData<bool>()) = true;
|
|
|
|
|
|
|
|
|
|
// close both when either side closes
|
|
|
|
|
client->end.connect([clientPtr = client.get(), remoteWeak] {
|
|
|
|
|
clientPtr->Close();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (auto remote = remoteWeak.lock()) {
|
|
|
|
|
remote->Close();
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
});
|
|
|
|
|
remotePtr->end.connect([remotePtr, clientWeak] {
|
|
|
|
|
remotePtr->Close();
|
2020-12-28 12:58:06 -08:00
|
|
|
if (auto client = clientWeak.lock()) {
|
|
|
|
|
client->Close();
|
|
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// copy bidirectionally
|
|
|
|
|
client->StartRead();
|
|
|
|
|
remotePtr->StartRead();
|
|
|
|
|
CopyStream(*client, remoteWeak);
|
|
|
|
|
CopyStream(*remotePtr, clientWeak);
|
|
|
|
|
});
|
|
|
|
|
},
|
2021-06-06 16:13:58 -07:00
|
|
|
host, fmt::to_string(remotePort));
|
2019-09-26 22:53:21 -07:00
|
|
|
|
|
|
|
|
// time out for connection
|
|
|
|
|
uv::Timer::SingleShot(loop, uv::Timer::Time{500},
|
|
|
|
|
[connectedWeak = std::weak_ptr<bool>(connected),
|
|
|
|
|
clientWeak = std::weak_ptr<uv::Tcp>(client),
|
|
|
|
|
remoteWeak = std::weak_ptr<uv::Tcp>(remote)] {
|
|
|
|
|
if (auto connected = connectedWeak.lock()) {
|
|
|
|
|
if (!*connected) {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (auto client = clientWeak.lock()) {
|
2019-09-26 22:53:21 -07:00
|
|
|
client->Close();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
|
|
|
|
if (auto remote = remoteWeak.lock()) {
|
2019-09-26 22:53:21 -07:00
|
|
|
remote->Close();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2019-09-26 22:53:21 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// start listening for incoming connections
|
|
|
|
|
server->Listen();
|
|
|
|
|
|
|
|
|
|
m_impl->servers[port] = server;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PortForwarder::Remove(unsigned int port) {
|
|
|
|
|
m_impl->runner.ExecSync([&](uv::Loop& loop) {
|
|
|
|
|
if (auto server = m_impl->servers.lookup(port).lock()) {
|
|
|
|
|
server->Close();
|
|
|
|
|
m_impl->servers.erase(port);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|