Files
iceBear67andClaude Opus 5 b2ba45c9f8 OpenVPN client with an authenticated SOCKS5 front door
A userspace VPN gateway: builds an OpenVPN tunnel to a VPNGate node with
the OpenVPN 3 core, terminates it in-process with lwIP, and serves SOCKS5
(RFC 1928/1929, CONNECT and UDP ASSOCIATE) over it. No root, no tun
device, no routing table changes.

Layout follows the module boundaries in docs/ARCHITECTURE.md:

  vpngate/   directory fetch + CSV parse (lines run to ~13.5 KB, so the
             parser streams rather than splitting on newlines)
  selector/  two-phase pick: cheap prior over the whole list, then real
             TCP handshake timing of the top K
  ovpn/      openvpn3 driven through TunBuilder, packets over a socketpair
  netstack/  lwIP: the TCP/IP stack that makes "no root" possible
  egress/    the swappable way out, and make-before-break switching
  socks5/    the front door
  health/    per-window scoring, and the decision to move
  app/       wiring, admin HTTP, signals

docs/FEASIBILITY.md is the analysis this was built from, including the
one requirement that is not physically possible -- carrying established
TCP connections across a node switch -- and what is done instead
(zero-progress redial, UDP re-homing, grace-period drain).

Tests: 155 without the tunnel egress, 172 with it. The seam is the egress
factory; selection, scoring, history and probing all run for real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 04:38:39 +00:00

121 lines
4.6 KiB
C++

// A ~100-line test harness.
//
// Pulling in GTest would mean either a system package we cannot assume or a
// FetchContent download on every clean build. The tests here need registration,
// assertions and a non-zero exit code; that is all this provides.
#pragma once
#include <concepts>
#include <functional>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
namespace ovgtest {
struct Case {
std::string name;
std::function<void()> fn;
};
std::vector<Case> &registry();
struct Registrar {
Registrar(std::string name, std::function<void()> fn);
};
// Thrown by the CHECK macros; caught per-case by the runner.
struct Failure {
std::string message;
};
// Thrown by SKIP: the environment cannot support the test (e.g. a sandbox that
// transparently accepts every outbound connection, so nothing can be
// blackholed). Reported distinctly from a pass so it cannot hide a regression.
struct Skipped {
std::string reason;
};
[[noreturn]] void fail(const char *file, int line, const std::string &what);
[[noreturn]] void skip(const std::string &reason);
// Absolute path to tests/data, injected by CMake.
std::string data_path(const std::string &leaf);
int run_all(int argc, char **argv);
// Best-effort stringification: streamable types get printed, everything else
// (IpAddress, enums without an operator<<, ...) degrades to a placeholder
// rather than failing to compile.
template <typename T>
concept Streamable = requires(std::ostream &os, const T &v) { os << v; };
template <typename T>
std::string to_str(const T &v) {
if constexpr (Streamable<T>) {
std::ostringstream os;
os << v;
return os.str();
} else {
return "<value>";
}
}
inline std::string to_str(bool v) { return v ? "true" : "false"; }
inline std::string to_str(const std::string &v) { return "\"" + v + "\""; }
} // namespace ovgtest
#define OVG_TEST(name) \
static void name(); \
static ::ovgtest::Registrar ovg_reg_##name(#name, name); \
static void name()
#define SKIP(reason) ::ovgtest::skip(reason)
#define CHECK(cond) \
do { \
if (!(cond)) ::ovgtest::fail(__FILE__, __LINE__, "CHECK(" #cond ")"); \
} while (0)
#define CHECK_EQ(a, b) \
do { \
const auto &ovg_a = (a); \
const auto &ovg_b = (b); \
if (!(ovg_a == ovg_b)) \
::ovgtest::fail(__FILE__, __LINE__, \
"CHECK_EQ(" #a ", " #b ")\n left = " + \
::ovgtest::to_str(ovg_a) + \
"\n right = " + ::ovgtest::to_str(ovg_b)); \
} while (0)
#define CHECK_NE(a, b) \
do { \
if ((a) == (b)) \
::ovgtest::fail(__FILE__, __LINE__, "CHECK_NE(" #a ", " #b ")"); \
} while (0)
#define CHECK_LT(a, b) \
do { \
const auto &ovg_a = (a); \
const auto &ovg_b = (b); \
if (!(ovg_a < ovg_b)) \
::ovgtest::fail(__FILE__, __LINE__, \
"CHECK_LT(" #a ", " #b ")\n left = " + \
::ovgtest::to_str(ovg_a) + \
"\n right = " + ::ovgtest::to_str(ovg_b)); \
} while (0)
#define CHECK_GT(a, b) CHECK_LT(b, a)
#define CHECK_NEAR(a, b, tol) \
do { \
const double ovg_d = static_cast<double>(a) - static_cast<double>(b); \
if (ovg_d > (tol) || -ovg_d > (tol)) \
::ovgtest::fail(__FILE__, __LINE__, \
"CHECK_NEAR(" #a ", " #b ")\n left = " + \
::ovgtest::to_str(static_cast<double>(a)) + \
"\n right = " + \
::ovgtest::to_str(static_cast<double>(b))); \
} while (0)