#include "common/error.h" #include #include namespace ovg { namespace { class OvgCategory : public std::error_category { public: const char *name() const noexcept override { return "ovg"; } std::string message(int v) const override { switch (static_cast(v)) { case Error::Ok: return "ok"; case Error::Cancelled: return "cancelled"; case Error::Timeout: return "timed out"; case Error::NotConnected: return "egress not connected"; case Error::EgressGone: return "egress destroyed"; case Error::EgressDraining: return "egress is draining"; case Error::ConnectionRefused: return "connection refused"; case Error::HostUnreachable: return "host unreachable"; case Error::NetworkUnreachable: return "network unreachable"; case Error::ResolveFailed: return "name resolution failed"; case Error::ProtocolError: return "protocol error"; case Error::AuthFailed: return "authentication failed"; case Error::NotSupported: return "not supported"; case Error::ResourceExhausted: return "resource exhausted"; case Error::TunnelSetupFailed: return "tunnel setup failed"; case Error::ConfigInvalid: return "invalid configuration"; case Error::UpstreamFailure: return "upstream failure"; case Error::Internal: return "internal error"; } return "unknown error " + std::to_string(v); } }; const OvgCategory g_category{}; } // namespace const std::error_category &error_category() { return g_category; } std::error_code make_error_code(Error e) { return {static_cast(e), g_category}; } uint8_t socks5_reply_for(const std::error_code &ec) { // RFC 1928 ยง6 REP values. constexpr uint8_t kSucceeded = 0x00; constexpr uint8_t kGeneralFailure = 0x01; constexpr uint8_t kNetworkUnreachable = 0x03; constexpr uint8_t kHostUnreachable = 0x04; constexpr uint8_t kConnectionRefused = 0x05; constexpr uint8_t kTtlExpired = 0x06; constexpr uint8_t kCommandNotSupported = 0x07; if (!ec) return kSucceeded; if (ec.category() == g_category) { switch (static_cast(ec.value())) { case Error::Ok: return kSucceeded; case Error::ConnectionRefused: return kConnectionRefused; case Error::HostUnreachable: case Error::ResolveFailed: return kHostUnreachable; case Error::NetworkUnreachable: case Error::NotConnected: case Error::EgressGone: case Error::EgressDraining: return kNetworkUnreachable; case Error::Timeout: return kTtlExpired; case Error::NotSupported: return kCommandNotSupported; default: return kGeneralFailure; } } // System errors. The egress layer is supposed to translate these into the // codes above before a reply is ever built (egress/direct_egress.cpp // map_ec), so reaching here means something slipped through -- answer as // precisely as we still can rather than flattening it to 0x01. if (ec == std::errc::connection_refused) return kConnectionRefused; if (ec == std::errc::host_unreachable) return kHostUnreachable; if (ec == std::errc::network_unreachable) return kNetworkUnreachable; if (ec == std::errc::timed_out) return kTtlExpired; // ...and asio's category, which carries errno values but does *not* declare // itself equivalent to std::errc, so every comparison above is false for it. // Matching on the value is only meaningful because both errno-based // categories agree on the numbers on our target (Linux); a category that // numbers its codes differently falls through to the general failure, which // is what it would have done anyway. if (ec.category().name() == std::string("asio.system")) { switch (ec.value()) { case ECONNREFUSED: return kConnectionRefused; case EHOSTUNREACH: return kHostUnreachable; case ENETUNREACH: return kNetworkUnreachable; case ETIMEDOUT: return kTtlExpired; default: break; } } return kGeneralFailure; } } // namespace ovg