1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
//
// OpenVPN
//
// Copyright (C) 2012-2022 OpenVPN Technologies, Inc.
// All rights reserved.
//
#pragma once
#include <utility>
#include <memory>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/common/jsonhelper.hpp>
namespace openvpn {
namespace WS {
// Helper class for HTTP client and server connections
// to strongly bind to a VPN client tunnel interface.
class ViaVPN : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<ViaVPN> Ptr;
OPENVPN_EXCEPTION(via_vpn_error);
enum GatewayType
{
NONE,
GW,
GW4,
GW6,
};
static bool is_enabled(const OptionList &opt)
{
return opt.exists("vpn-connection-info");
}
ViaVPN(const OptionList &opt)
{
const Option &o = opt.get("vpn-connection-info");
connection_info_fn = o.get(1, 256);
gw_type = parse_gw_type(o.get_optional(2, 16));
}
ViaVPN(std::string conn_info_fn,
const std::string &gw)
: connection_info_fn(std::move(conn_info_fn)),
gw_type(parse_gw_type(gw))
{
}
static ViaVPN::Ptr client_new_if_enabled(const OptionList &opt)
{
if (is_enabled(opt))
return ViaVPN::Ptr(new WS::ViaVPN(opt));
else
return ViaVPN::Ptr();
}
template <typename HOST>
Json::Value client_update_host(HOST &host) const
{
Json::Value root = json::parse_from_file(connection_info_fn);
set_host_field(host.local_addr, root, "vpn_ip4", connection_info_fn);
set_host_field(host.local_addr_alt, root, "vpn_ip6", connection_info_fn);
maybe_swap(host.local_addr, host.local_addr_alt);
// use gw4/gw6 as host hint
if (gw_type == GW || gw_type == GW4)
set_host_field(host.hint, root, "gw4", connection_info_fn);
if (gw_type == GW || gw_type == GW6)
set_host_field(host.hint, root, "gw6", connection_info_fn);
return root;
}
template <typename LISTEN_ITEM>
static IP::Addr server_local_addr(const LISTEN_ITEM &listen_item,
const GatewayType gw_type)
{
if (listen_item.addr.empty())
throw via_vpn_error("listen_item is empty");
// via-VPN processing enabled?
if (listen_item.addr[0] == '@')
{
const Json::Value root = json::parse_from_file(listen_item.addr.substr(1));
std::string ipstr;
if (gw_type == GW || gw_type == GW4)
set_host_field(ipstr, root, "vpn_ip4", listen_item.addr);
if (gw_type == GW || gw_type == GW6)
set_host_field(ipstr, root, "vpn_ip6", listen_item.addr);
if (ipstr.empty())
throw via_vpn_error("cannot find local address in " + listen_item.addr);
const IP::Addr ret = IP::Addr(ipstr, listen_item.addr);
OPENVPN_LOG("using local address " << ret.to_string() << " for " << listen_item.directive << ' ' << listen_item.addr);
return ret;
}
else
return IP::Addr(listen_item.addr, listen_item.directive);
}
// returns the "client-ip" directive pushed by the server
IP::Addr client_ip() const
{
const Json::Value root = json::parse_from_file(connection_info_fn);
return IP::Addr(json::get_string_ref(root, "client_ip", connection_info_fn), connection_info_fn);
}
std::string to_string() const
{
std::string ret;
ret.reserve(128);
ret += "[ViaVPN ";
ret += connection_info_fn;
if (gw_type != NONE)
{
ret += ' ';
ret += gw_type_to_string(gw_type);
}
ret += ']';
return ret;
}
private:
static void set_host_field(std::string &dest,
const Json::Value &root,
const std::string &name,
const std::string &title)
{
std::string value = json::get_string_optional(root, name, std::string(), title);
if (dest.empty() && !value.empty())
dest = std::move(value);
}
// if only one of s1 and s2 are non-empty, make sure it is s1
static void maybe_swap(std::string &s1, std::string &s2)
{
if (s1.empty() && !s2.empty())
std::swap(s1, s2);
}
static GatewayType parse_gw_type(const std::string &gw)
{
if (gw.empty())
return NONE;
else if (gw == "gw")
return GW;
else if (gw == "gw4")
return GW4;
else if (gw == "gw6")
return GW6;
else
throw via_vpn_error("ViaVPN: bad gw parameter");
}
static std::string gw_type_to_string(const GatewayType gw_type)
{
switch (gw_type)
{
case NONE:
return "GW-NONE";
case GW:
return "GW";
case GW4:
return "GW4";
case GW6:
return "GW6";
default:
return "GW-?";
}
}
std::string connection_info_fn;
GatewayType gw_type;
};
} // namespace WS
} // namespace openvpn
|