mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_BUILDER_BASE_H
|
||||
#define OPENVPN_TUN_BUILDER_BASE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openvpn {
|
||||
class TunBuilderBase
|
||||
{
|
||||
public:
|
||||
// Tun builder methods, loosely based on the Android VpnService.Builder
|
||||
// abstraction. These methods comprise an abstraction layer that
|
||||
// allows the OpenVPN C++ core to call out to external methods for
|
||||
// establishing the tunnel, adding routes, etc.
|
||||
|
||||
// All methods returning bool use the return
|
||||
// value to indicate success (true) or fail (false).
|
||||
// tun_builder_new() should be called first, then arbitrary setter methods,
|
||||
// and finally tun_builder_establish to return the socket descriptor
|
||||
// for the session. IP addresses are pre-validated before being passed to
|
||||
// these methods.
|
||||
// This interface is based on Android's VpnService.Builder.
|
||||
|
||||
// Callback to construct a new tun builder
|
||||
// Should be called first.
|
||||
virtual bool tun_builder_new()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional callback that indicates OSI layer, should be 2 or 3.
|
||||
// Defaults to 3.
|
||||
virtual bool tun_builder_set_layer(int layer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback to set address of remote server
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_remote_address(const std::string& address, bool ipv6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add network address to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
virtual bool tun_builder_add_address(const std::string& address,
|
||||
int prefix_length,
|
||||
const std::string& gateway, // optional
|
||||
bool ipv6,
|
||||
bool net30)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional callback to set default value for route metric.
|
||||
// Guaranteed to be called before other methods that deal
|
||||
// with routes such as tun_builder_add_route and
|
||||
// tun_builder_reroute_gw. Route metric is ignored
|
||||
// if < 0.
|
||||
virtual bool tun_builder_set_route_metric_default(int metric)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback to reroute default gateway to VPN interface.
|
||||
// ipv4 is true if the default route to be added should be IPv4.
|
||||
// ipv6 is true if the default route to be added should be IPv6.
|
||||
// flags are defined in RGWFlags (rgwflags.hpp).
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_reroute_gw(bool ipv4,
|
||||
bool ipv6,
|
||||
unsigned int flags)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add route to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
virtual bool tun_builder_add_route(const std::string& address,
|
||||
int prefix_length,
|
||||
int metric,
|
||||
bool ipv6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to exclude route from VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
virtual bool tun_builder_exclude_route(const std::string& address,
|
||||
int prefix_length,
|
||||
int metric,
|
||||
bool ipv6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add DNS server to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// If reroute_dns is true, all DNS traffic should be routed over the
|
||||
// tunnel, while if false, only DNS traffic that matches an added search
|
||||
// domain should be routed.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
virtual bool tun_builder_add_dns_server(const std::string& address, bool ipv6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add search domain to DNS resolver
|
||||
// May be called more than once per tun_builder session
|
||||
// See tun_builder_add_dns_server above for description of
|
||||
// reroute_dns parameter.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
virtual bool tun_builder_add_search_domain(const std::string& domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to set MTU of the VPN interface
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_mtu(int mtu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to set the session name
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_session_name(const std::string& name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add a host which should bypass the proxy
|
||||
// May be called more than once per tun_builder session
|
||||
virtual bool tun_builder_add_proxy_bypass(const std::string& bypass_host)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to set the proxy "Auto Config URL"
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_proxy_auto_config_url(const std::string& url)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to set the HTTP proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_proxy_http(const std::string& host, int port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to set the HTTPS proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
virtual bool tun_builder_set_proxy_https(const std::string& host, int port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback to add Windows WINS server to VPN interface.
|
||||
// WINS server addresses are always IPv4.
|
||||
// May be called more than once per tun_builder session.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
virtual bool tun_builder_add_wins_server(const std::string& address)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional callback that indicates whether IPv6 traffic should be
|
||||
// blocked, to prevent unencrypted IPv6 packet leakage when the
|
||||
// tunnel is IPv4-only, but the local machine has IPv6 connectivity
|
||||
// to the internet. Enabled by "block-ipv6" config var.
|
||||
virtual bool tun_builder_set_block_ipv6(bool block_ipv6)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Optional callback to set a DNS suffix on tun/tap adapter.
|
||||
// Currently only implemented on Windows, where it will
|
||||
// set the "Connection-specific DNS Suffix" property on
|
||||
// the TAP driver.
|
||||
virtual bool tun_builder_set_adapter_domain_suffix(const std::string& name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback to establish the VPN tunnel, returning a file descriptor
|
||||
// to the tunnel, which the caller will henceforth own. Returns -1
|
||||
// if the tunnel could not be established.
|
||||
// Always called last after tun_builder session has been configured.
|
||||
virtual int tun_builder_establish()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Return true if tun interface may be persisted, i.e. rolled
|
||||
// into a new session with properties untouched. This method
|
||||
// is only called after all other tests of persistence
|
||||
// allowability succeed, therefore it can veto persistence.
|
||||
// If persistence is ultimately enabled,
|
||||
// tun_builder_establish_lite() will be called. Otherwise,
|
||||
// tun_builder_establish() will be called.
|
||||
virtual bool tun_builder_persist()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// When the exclude local network option is enabled this
|
||||
// function is called to get a list of local networks so routes
|
||||
// to exclude them from the VPN network are generated
|
||||
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
|
||||
virtual const std::vector<std::string> tun_builder_get_local_networks(bool ipv6)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Indicates a reconnection with persisted tun state.
|
||||
virtual void tun_builder_establish_lite()
|
||||
{
|
||||
}
|
||||
|
||||
// Indicates that tunnel is being torn down.
|
||||
// If disconnect == true, then the teardown is occurring
|
||||
// prior to final disconnect.
|
||||
virtual void tun_builder_teardown(bool disconnect) {}
|
||||
|
||||
virtual ~TunBuilderBase() {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,821 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// An artificial TunBuilder object, used to log the tun builder settings,
|
||||
// but doesn't actually configure anything.
|
||||
|
||||
#ifndef OPENVPN_TUN_BUILDER_CAPTURE_H
|
||||
#define OPENVPN_TUN_BUILDER_CAPTURE_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/jsonlib.hpp>
|
||||
#include <openvpn/tun/builder/base.hpp>
|
||||
#include <openvpn/client/rgopt.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/http/urlparse.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
#include <openvpn/common/jsonhelper.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class TunBuilderCapture : public TunBuilderBase, public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TunBuilderCapture> Ptr;
|
||||
|
||||
// builder data classes
|
||||
|
||||
class RemoteAddress
|
||||
{
|
||||
public:
|
||||
std::string address;
|
||||
bool ipv6 = false;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret = address;
|
||||
if (ipv6)
|
||||
ret += " [IPv6]";
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return !address.empty();
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
IP::Addr(address, title, ipv6 ? IP::Addr::V6 : IP::Addr::V4);
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["address"] = Json::Value(address);
|
||||
root["ipv6"] = Json::Value(ipv6);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
if (!json::is_dict(root, title))
|
||||
return;
|
||||
json::to_string(root, address, "address", title);
|
||||
json::to_bool(root, ipv6, "ipv6", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class RerouteGW
|
||||
{
|
||||
public:
|
||||
bool ipv4 = false;
|
||||
bool ipv6 = false;
|
||||
unsigned int flags = 0;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
const RedirectGatewayFlags rgf(flags);
|
||||
os << "IPv4=" << ipv4 << " IPv6=" << ipv6 << " flags=" << rgf.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
// nothing to validate
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["ipv4"] = Json::Value(ipv4);
|
||||
root["ipv6"] = Json::Value(ipv6);
|
||||
root["flags"] = Json::Value((Json::UInt)flags);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_bool(root, ipv4, "ipv4", title);
|
||||
json::to_bool(root, ipv6, "ipv6", title);
|
||||
json::to_uint(root, flags, "flags", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class RouteBase
|
||||
{
|
||||
public:
|
||||
std::string address;
|
||||
int prefix_length = 0;
|
||||
int metric = -1; // optional
|
||||
std::string gateway; // optional
|
||||
bool ipv6 = false;
|
||||
bool net30 = false;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << address << '/' << prefix_length;
|
||||
if (!gateway.empty())
|
||||
os << " -> " << gateway;
|
||||
if (metric >= 0)
|
||||
os << " [METRIC=" << metric << ']';
|
||||
if (ipv6)
|
||||
os << " [IPv6]";
|
||||
if (net30)
|
||||
os << " [net30]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["address"] = Json::Value(address);
|
||||
root["prefix_length"] = Json::Value(prefix_length);
|
||||
root["metric"] = Json::Value(metric);
|
||||
root["gateway"] = Json::Value(gateway);
|
||||
root["ipv6"] = Json::Value(ipv6);
|
||||
root["net30"] = Json::Value(net30);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, address, "address", title);
|
||||
json::to_int(root, prefix_length, "prefix_length", title);
|
||||
json::to_int(root, metric, "metric", title);
|
||||
json::to_string(root, gateway, "gateway", title);
|
||||
json::to_bool(root, ipv6, "ipv6", title);
|
||||
json::to_bool(root, net30, "net30", title);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void validate_(const std::string& title, const bool require_canonical) const
|
||||
{
|
||||
const IP::Addr::Version ver = ipv6 ? IP::Addr::V6 : IP::Addr::V4;
|
||||
const IP::Route route = IP::route_from_string_prefix(address, prefix_length, title, ver);
|
||||
if (require_canonical && !route.is_canonical())
|
||||
OPENVPN_THROW_EXCEPTION(title << " : not a canonical route: " << route);
|
||||
if (!gateway.empty())
|
||||
IP::Addr(gateway, title + ".gateway", ver);
|
||||
if (net30 && route.prefix_len != 30)
|
||||
OPENVPN_THROW_EXCEPTION(title << " : not a net30 route: " << route);
|
||||
}
|
||||
};
|
||||
|
||||
class RouteAddress : public RouteBase // may be non-canonical
|
||||
{
|
||||
public:
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
validate_(title, false);
|
||||
}
|
||||
};
|
||||
|
||||
class Route : public RouteBase // must be canonical
|
||||
{
|
||||
public:
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
validate_(title, true);
|
||||
}
|
||||
};
|
||||
|
||||
class DNSServer
|
||||
{
|
||||
public:
|
||||
std::string address;
|
||||
bool ipv6 = false;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret = address;
|
||||
if (ipv6)
|
||||
ret += " [IPv6]";
|
||||
return ret;
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
IP::Addr(address, title, ipv6 ? IP::Addr::V6 : IP::Addr::V4);
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["address"] = Json::Value(address);
|
||||
root["ipv6"] = Json::Value(ipv6);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, address, "address", title);
|
||||
json::to_bool(root, ipv6, "ipv6", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class SearchDomain
|
||||
{
|
||||
public:
|
||||
std::string domain;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return domain;
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
HostPort::validate_host(domain, title);
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["domain"] = Json::Value(domain);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, domain, "domain", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class ProxyBypass
|
||||
{
|
||||
public:
|
||||
std::string bypass_host;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return bypass_host;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return !bypass_host.empty();
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
if (defined())
|
||||
HostPort::validate_host(bypass_host, title);
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["bypass_host"] = Json::Value(bypass_host);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, bypass_host, "bypass_host", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class ProxyAutoConfigURL
|
||||
{
|
||||
public:
|
||||
std::string url;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
bool defined() const {
|
||||
return !url.empty();
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
try {
|
||||
if (defined())
|
||||
(URL::Parse(url));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW_EXCEPTION(title << " : error parsing ProxyAutoConfigURL: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["url"] = Json::Value(url);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
if (!json::is_dict(root, title))
|
||||
return;
|
||||
json::to_string(root, url, "url", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class ProxyHostPort
|
||||
{
|
||||
public:
|
||||
std::string host;
|
||||
int port = 0;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << host << ' ' << port;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool defined() const {
|
||||
return !host.empty();
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
if (defined())
|
||||
{
|
||||
HostPort::validate_host(host, title);
|
||||
HostPort::validate_port(port, title);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["host"] = Json::Value(host);
|
||||
root["port"] = Json::Value(port);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
if (!json::is_dict(root, title))
|
||||
return;
|
||||
json::to_string(root, host, "host", title);
|
||||
json::to_int(root, port, "port", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class WINSServer
|
||||
{
|
||||
public:
|
||||
std::string address;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret = address;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void validate(const std::string& title) const
|
||||
{
|
||||
IP::Addr(address, title, IP::Addr::V4);
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["address"] = Json::Value(address);
|
||||
return root;
|
||||
}
|
||||
|
||||
void from_json(const Json::Value& root, const std::string& title)
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, address, "address", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual bool tun_builder_set_remote_address(const std::string& address, bool ipv6) override
|
||||
{
|
||||
remote_address.address = address;
|
||||
remote_address.ipv6 = ipv6;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_address(const std::string& address, int prefix_length, const std::string& gateway, bool ipv6, bool net30) override
|
||||
{
|
||||
RouteAddress r;
|
||||
r.address = address;
|
||||
r.prefix_length = prefix_length;
|
||||
r.gateway = gateway;
|
||||
r.ipv6 = ipv6;
|
||||
r.net30 = net30;
|
||||
if (ipv6)
|
||||
tunnel_address_index_ipv6 = (int)tunnel_addresses.size();
|
||||
else
|
||||
tunnel_address_index_ipv4 = (int)tunnel_addresses.size();
|
||||
tunnel_addresses.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_reroute_gw(bool ipv4, bool ipv6, unsigned int flags) override
|
||||
{
|
||||
reroute_gw.ipv4 = ipv4;
|
||||
reroute_gw.ipv6 = ipv6;
|
||||
reroute_gw.flags = flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_route_metric_default(int metric) override
|
||||
{
|
||||
route_metric_default = metric;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_route(const std::string& address, int prefix_length, int metric, bool ipv6) override
|
||||
{
|
||||
Route r;
|
||||
r.address = address;
|
||||
r.prefix_length = prefix_length;
|
||||
r.metric = metric;
|
||||
r.ipv6 = ipv6;
|
||||
add_routes.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_exclude_route(const std::string& address, int prefix_length, int metric, bool ipv6) override
|
||||
{
|
||||
Route r;
|
||||
r.address = address;
|
||||
r.prefix_length = prefix_length;
|
||||
r.metric = metric;
|
||||
r.ipv6 = ipv6;
|
||||
exclude_routes.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_dns_server(const std::string& address, bool ipv6) override
|
||||
{
|
||||
DNSServer dns;
|
||||
dns.address = address;
|
||||
dns.ipv6 = ipv6;
|
||||
dns_servers.push_back(dns);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_search_domain(const std::string& domain) override
|
||||
{
|
||||
SearchDomain dom;
|
||||
dom.domain = domain;
|
||||
search_domains.push_back(dom);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_adapter_domain_suffix(const std::string& name) override
|
||||
{
|
||||
adapter_domain_suffix = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_layer(int layer) override
|
||||
{
|
||||
this->layer = Layer::from_value(layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_mtu(int mtu) override
|
||||
{
|
||||
this->mtu = mtu;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_session_name(const std::string& name) override
|
||||
{
|
||||
session_name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_proxy_bypass(const std::string& bypass_host) override
|
||||
{
|
||||
ProxyBypass b;
|
||||
b.bypass_host = bypass_host;
|
||||
proxy_bypass.push_back(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_proxy_auto_config_url(const std::string& url) override
|
||||
{
|
||||
proxy_auto_config_url.url = url;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_proxy_http(const std::string& host, int port) override
|
||||
{
|
||||
http_proxy.host = host;
|
||||
http_proxy.port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_proxy_https(const std::string& host, int port) override
|
||||
{
|
||||
https_proxy.host = host;
|
||||
https_proxy.port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_add_wins_server(const std::string& address) override
|
||||
{
|
||||
WINSServer wins;
|
||||
wins.address = address;
|
||||
wins_servers.push_back(wins);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool tun_builder_set_block_ipv6(bool value) override
|
||||
{
|
||||
block_ipv6 = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset_tunnel_addresses()
|
||||
{
|
||||
tunnel_addresses.clear();
|
||||
tunnel_address_index_ipv4 = -1;
|
||||
tunnel_address_index_ipv6 = -1;
|
||||
}
|
||||
|
||||
void reset_dns_servers()
|
||||
{
|
||||
dns_servers.clear();
|
||||
}
|
||||
|
||||
const RouteAddress* vpn_ipv4() const
|
||||
{
|
||||
if (tunnel_address_index_ipv4 >= 0)
|
||||
return &tunnel_addresses[tunnel_address_index_ipv4];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RouteAddress* vpn_ipv6() const
|
||||
{
|
||||
if (tunnel_address_index_ipv6 >= 0)
|
||||
return &tunnel_addresses[tunnel_address_index_ipv6];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RouteAddress* vpn_ip(const IP::Addr::Version v) const
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case IP::Addr::V4:
|
||||
return vpn_ipv4();
|
||||
case IP::Addr::V6:
|
||||
return vpn_ipv6();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void validate() const
|
||||
{
|
||||
validate_layer("root");
|
||||
validate_mtu("root");
|
||||
remote_address.validate("remote_address");
|
||||
validate_list(tunnel_addresses, "tunnel_addresses");
|
||||
validate_tunnel_address_indices("root");
|
||||
reroute_gw.validate("reroute_gw");
|
||||
validate_list(add_routes, "add_routes");
|
||||
validate_list(exclude_routes, "exclude_routes");
|
||||
validate_list(dns_servers, "dns_servers");
|
||||
validate_list(search_domains, "search_domains");
|
||||
validate_list(proxy_bypass, "proxy_bypass");
|
||||
proxy_auto_config_url.validate("proxy_auto_config_url");
|
||||
http_proxy.validate("http_proxy");
|
||||
https_proxy.validate("https_proxy");
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Session Name: " << session_name << std::endl;
|
||||
os << "Layer: " << layer.str() << std::endl;
|
||||
if (mtu)
|
||||
os << "MTU: " << mtu << std::endl;
|
||||
os << "Remote Address: " << remote_address.to_string() << std::endl;
|
||||
render_list(os, "Tunnel Addresses", tunnel_addresses);
|
||||
os << "Reroute Gateway: " << reroute_gw.to_string() << std::endl;
|
||||
os << "Block IPv6: " << (block_ipv6 ? "yes" : "no") << std::endl;
|
||||
if (route_metric_default >= 0)
|
||||
os << "Route Metric Default: " << route_metric_default << std::endl;
|
||||
render_list(os, "Add Routes", add_routes);
|
||||
render_list(os, "Exclude Routes", exclude_routes);
|
||||
render_list(os, "DNS Servers", dns_servers);
|
||||
render_list(os, "Search Domains", search_domains);
|
||||
if (!adapter_domain_suffix.empty())
|
||||
os << "Adapter Domain Suffix: " << adapter_domain_suffix << std::endl;
|
||||
if (!proxy_bypass.empty())
|
||||
render_list(os, "Proxy Bypass", proxy_bypass);
|
||||
if (proxy_auto_config_url.defined())
|
||||
os << "Proxy Auto Config URL: " << proxy_auto_config_url.to_string() << std::endl;
|
||||
if (http_proxy.defined())
|
||||
os << "HTTP Proxy: " << http_proxy.to_string() << std::endl;
|
||||
if (https_proxy.defined())
|
||||
os << "HTTPS Proxy: " << https_proxy.to_string() << std::endl;
|
||||
if (!wins_servers.empty())
|
||||
render_list(os, "WINS Servers", wins_servers);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
|
||||
Json::Value to_json() const
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["session_name"] = Json::Value(session_name);
|
||||
root["mtu"] = Json::Value(mtu);
|
||||
root["layer"] = Json::Value(layer.value());
|
||||
if (remote_address.defined())
|
||||
root["remote_address"] = remote_address.to_json();
|
||||
json::from_vector(root, tunnel_addresses, "tunnel_addresses");
|
||||
root["tunnel_address_index_ipv4"] = Json::Value(tunnel_address_index_ipv4);
|
||||
root["tunnel_address_index_ipv6"] = Json::Value(tunnel_address_index_ipv6);
|
||||
root["reroute_gw"] = reroute_gw.to_json();
|
||||
root["block_ipv6"] = Json::Value(block_ipv6);
|
||||
root["route_metric_default"] = Json::Value(route_metric_default);
|
||||
json::from_vector(root, add_routes, "add_routes");
|
||||
json::from_vector(root, exclude_routes, "exclude_routes");
|
||||
json::from_vector(root, dns_servers, "dns_servers");
|
||||
json::from_vector(root, wins_servers, "wins_servers");
|
||||
json::from_vector(root, search_domains, "search_domains");
|
||||
root["adapter_domain_suffix"] = Json::Value(adapter_domain_suffix);
|
||||
json::from_vector(root, proxy_bypass, "proxy_bypass");
|
||||
if (proxy_auto_config_url.defined())
|
||||
root["proxy_auto_config_url"] = proxy_auto_config_url.to_json();
|
||||
if (http_proxy.defined())
|
||||
root["http_proxy"] = http_proxy.to_json();
|
||||
if (https_proxy.defined())
|
||||
root["https_proxy"] = https_proxy.to_json();
|
||||
return root;
|
||||
}
|
||||
|
||||
static TunBuilderCapture::Ptr from_json(const Json::Value& root)
|
||||
{
|
||||
const std::string title = "root";
|
||||
TunBuilderCapture::Ptr tbc(new TunBuilderCapture);
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, tbc->session_name, "session_name", title);
|
||||
tbc->layer = Layer::from_value(json::get_int(root, "layer", title));
|
||||
json::to_int(root, tbc->mtu, "mtu", title);
|
||||
tbc->remote_address.from_json(root["remote_address"], "remote_address");
|
||||
json::to_vector(root, tbc->tunnel_addresses, "tunnel_addresses", title);
|
||||
json::to_int(root, tbc->tunnel_address_index_ipv4, "tunnel_address_index_ipv4", title);
|
||||
json::to_int(root, tbc->tunnel_address_index_ipv6, "tunnel_address_index_ipv6", title);
|
||||
tbc->reroute_gw.from_json(root["reroute_gw"], "reroute_gw");
|
||||
json::to_bool(root, tbc->block_ipv6, "block_ipv6", title);
|
||||
json::to_int(root, tbc->route_metric_default, "route_metric_default", title);
|
||||
json::to_vector(root, tbc->add_routes, "add_routes", title);
|
||||
json::to_vector(root, tbc->exclude_routes, "exclude_routes", title);
|
||||
json::to_vector(root, tbc->dns_servers, "dns_servers", title);
|
||||
json::to_vector(root, tbc->wins_servers, "wins_servers", title);
|
||||
json::to_vector(root, tbc->search_domains, "search_domains", title);
|
||||
json::to_string(root, tbc->adapter_domain_suffix, "adapter_domain_suffix", title);
|
||||
json::to_vector(root, tbc->proxy_bypass, "proxy_bypass", title);
|
||||
tbc->proxy_auto_config_url.from_json(root["proxy_auto_config_url"], "proxy_auto_config_url");
|
||||
tbc->http_proxy.from_json(root["http_proxy"], "http_proxy");
|
||||
tbc->https_proxy.from_json(root["https_proxy"], "https_proxy");
|
||||
return tbc;
|
||||
}
|
||||
|
||||
#endif // HAVE_JSON
|
||||
|
||||
// builder data
|
||||
std::string session_name;
|
||||
int mtu = 0;
|
||||
Layer layer{Layer::OSI_LAYER_3}; // OSI layer
|
||||
RemoteAddress remote_address; // real address of server
|
||||
std::vector<RouteAddress> tunnel_addresses; // local tunnel addresses
|
||||
int tunnel_address_index_ipv4 = -1; // index into tunnel_addresses for IPv4 entry (or -1 if undef)
|
||||
int tunnel_address_index_ipv6 = -1; // index into tunnel_addresses for IPv6 entry (or -1 if undef)
|
||||
RerouteGW reroute_gw; // redirect-gateway info
|
||||
bool block_ipv6 = false; // block IPv6 traffic while VPN is active
|
||||
int route_metric_default = -1; // route-metric directive
|
||||
std::vector<Route> add_routes; // routes that should be added to tunnel
|
||||
std::vector<Route> exclude_routes; // routes that should be excluded from tunnel
|
||||
std::vector<DNSServer> dns_servers; // VPN DNS servers
|
||||
std::vector<SearchDomain> search_domains; // domain suffixes whose DNS requests should be tunnel-routed
|
||||
std::string adapter_domain_suffix; // domain suffix on tun/tap adapter (currently Windows only)
|
||||
|
||||
std::vector<ProxyBypass> proxy_bypass; // hosts that should bypass proxy
|
||||
ProxyAutoConfigURL proxy_auto_config_url;
|
||||
ProxyHostPort http_proxy;
|
||||
ProxyHostPort https_proxy;
|
||||
|
||||
std::vector<WINSServer> wins_servers; // Windows WINS servers
|
||||
|
||||
private:
|
||||
template <typename LIST>
|
||||
static void render_list(std::ostream& os, const std::string& title, const LIST& list)
|
||||
{
|
||||
os << title << ':' << std::endl;
|
||||
for (auto &e : list)
|
||||
os << " " << e.to_string() << std::endl;
|
||||
}
|
||||
|
||||
template <typename LIST>
|
||||
static void validate_list(const LIST& list, const std::string& title)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto &e : list)
|
||||
{
|
||||
e.validate(title + '[' + openvpn::to_string(i) + ']');
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_tunnel_index(const int index) const
|
||||
{
|
||||
if (index == -1)
|
||||
return true;
|
||||
return index >= 0 && index <= tunnel_addresses.size();
|
||||
}
|
||||
|
||||
void validate_tunnel_address_indices(const std::string& title) const
|
||||
{
|
||||
if (!validate_tunnel_index(tunnel_address_index_ipv4))
|
||||
OPENVPN_THROW_EXCEPTION(title << ".tunnel_address_index_ipv4 : IPv4 tunnel address index out of range: " << tunnel_address_index_ipv4);
|
||||
if (!validate_tunnel_index(tunnel_address_index_ipv6))
|
||||
OPENVPN_THROW_EXCEPTION(title << ".tunnel_address_index_ipv6 : IPv6 tunnel address index out of range: " << tunnel_address_index_ipv6);
|
||||
const RouteAddress* r4 = vpn_ipv4();
|
||||
if (r4 && r4->ipv6)
|
||||
OPENVPN_THROW_EXCEPTION(title << ".tunnel_address_index_ipv4 : IPv4 tunnel address index points to wrong address type: " << r4->to_string());
|
||||
const RouteAddress* r6 = vpn_ipv6();
|
||||
if (r6 && !r6->ipv6)
|
||||
OPENVPN_THROW_EXCEPTION(title << ".tunnel_address_index_ipv6 : IPv6 tunnel address index points to wrong address type: " << r6->to_string());
|
||||
}
|
||||
|
||||
void validate_mtu(const std::string& title) const
|
||||
{
|
||||
if (mtu < 0 || mtu > 65536)
|
||||
OPENVPN_THROW_EXCEPTION(title << ".mtu : MTU out of range: " << mtu);
|
||||
}
|
||||
|
||||
void validate_layer(const std::string& title) const
|
||||
{
|
||||
if (!layer.defined())
|
||||
OPENVPN_THROW_EXCEPTION(title << ": layer undefined");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,317 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Generic, cross-platform tun interface that drives a TunBuilderBase API.
|
||||
// Fully supports IPv6. To make this work on a given platform, define
|
||||
// a TunBuilderBase for the platform.
|
||||
|
||||
#ifndef OPENVPN_TUN_BUILDER_CLIENT_H
|
||||
#define OPENVPN_TUN_BUILDER_CLIENT_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunBuilderClient {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_builder_error);
|
||||
|
||||
// struct used to pass received tun packets
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
// our TunPersist class, specialized for Unix file descriptors
|
||||
typedef TunPersistTemplate<ScopedFD> TunPersist;
|
||||
|
||||
// A simplified tun interface where pre-existing
|
||||
// socket is provided.
|
||||
template <typename ReadHandler>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor>
|
||||
{
|
||||
typedef TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor> Base;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
Tun(openvpn_io::io_context& io_context,
|
||||
const int socket,
|
||||
const bool retain_sd_arg,
|
||||
const bool tun_prefix_arg,
|
||||
ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: Base(read_handler_arg, frame_arg, stats_arg)
|
||||
{
|
||||
Base::stream = new openvpn_io::posix::stream_descriptor(io_context, socket);
|
||||
Base::name_ = "tun";
|
||||
Base::retain_stream = retain_sd_arg;
|
||||
Base::tun_prefix = tun_prefix_arg;
|
||||
}
|
||||
|
||||
~Tun() { Base::stop(); }
|
||||
};
|
||||
|
||||
// A factory for the Client class
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
int n_parallel; // number of parallel async reads on tun socket
|
||||
bool retain_sd;
|
||||
bool tun_prefix;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
EmulateExcludeRouteFactory::Ptr eer_factory;
|
||||
TunPersist::Ptr tun_persist;
|
||||
TunBuilderBase* builder;
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli) override;
|
||||
|
||||
void finalize(const bool disconnected) override
|
||||
{
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
: n_parallel(8), retain_sd(false), tun_prefix(false), builder(nullptr) {}
|
||||
};
|
||||
|
||||
// The tun interface
|
||||
class Client : public TunClient
|
||||
{
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend class TunIO<Client*, PacketFrom, openvpn_io::posix::stream_descriptor>; // calls tun_read_handler
|
||||
|
||||
typedef Tun<Client*> TunImpl;
|
||||
|
||||
public:
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
if (config->tun_persist)
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
else
|
||||
tun_persist.reset(new TunPersist(false, config->retain_sd, config->builder)); // short-term
|
||||
|
||||
try {
|
||||
int sd = -1;
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
sd = tun_persist->obj();
|
||||
state = tun_persist->state();
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
|
||||
// indicate reconnection with persisted state
|
||||
config->builder->tun_builder_establish_lite();
|
||||
}
|
||||
else
|
||||
{
|
||||
TunBuilderBase* tb = config->builder;
|
||||
|
||||
// reset target tun builder object
|
||||
if (!tb->tun_builder_new())
|
||||
throw tun_builder_error("tun_builder_new failed");
|
||||
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// configure the tun builder
|
||||
TunProp::configure_builder(tb, state.get(), config->stats.get(), server_addr,
|
||||
config->tun_prop, opt, config->eer_factory.get(), false);
|
||||
|
||||
// start tun
|
||||
sd = tb->tun_builder_establish();
|
||||
}
|
||||
|
||||
if (sd == -1)
|
||||
{
|
||||
parent.tun_error(Error::TUN_IFACE_CREATE, "cannot acquire tun interface socket");
|
||||
return;
|
||||
}
|
||||
|
||||
// persist state
|
||||
if (tun_persist->persist_tun_state(sd, state))
|
||||
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
|
||||
|
||||
impl.reset(new TunImpl(io_context,
|
||||
sd,
|
||||
true,
|
||||
config->tun_prefix,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats
|
||||
));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
// signal that we are connected
|
||||
parent.tun_connected();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->close();
|
||||
stop();
|
||||
parent.tun_error(Error::TUN_SETUP_FAILED, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const override
|
||||
{
|
||||
if (impl)
|
||||
return impl->name();
|
||||
else
|
||||
return "UNDEF_TUN";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect() override
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->set_disconnect();
|
||||
}
|
||||
|
||||
virtual void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
halt(false),
|
||||
state(new TunProp::State())
|
||||
{
|
||||
}
|
||||
|
||||
bool send(Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
return impl->write(buf);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
}
|
||||
|
||||
void tun_error_handler(const Error::Type errtype, // called by TunImpl
|
||||
const openvpn_io::error_code* error)
|
||||
{
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // owns the tun socket descriptor
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
bool halt;
|
||||
TunProp::State::Ptr state;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli)
|
||||
{
|
||||
return TunClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_BUILDER_RGWFLAGS_H
|
||||
#define OPENVPN_TUN_BUILDER_RGWFLAGS_H
|
||||
|
||||
namespace openvpn {
|
||||
namespace RGWFlags {
|
||||
// These flags are passed as the flags argument to TunBuilderBase::tun_builder_reroute_gw
|
||||
// NOTE: must not collide with RG_x flags in rgopt.hpp.
|
||||
enum {
|
||||
EmulateExcludeRoutes=(1<<16),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun setup base class for unix
|
||||
|
||||
#ifndef OPENVPN_TUN_BUILDER_SETUP_H
|
||||
#define OPENVPN_TUN_BUILDER_SETUP_H
|
||||
|
||||
#include <openvpn/common/jsonlib.hpp>
|
||||
#include <openvpn/common/destruct.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunBuilderSetup {
|
||||
struct Config
|
||||
{
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() = 0;
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) = 0;
|
||||
#endif
|
||||
virtual ~Config() {}
|
||||
};
|
||||
|
||||
struct Base : public DestructorBase
|
||||
{
|
||||
typedef RCPtr<Base> Ptr;
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull,
|
||||
Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) = 0;
|
||||
};
|
||||
|
||||
struct Factory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Factory> Ptr;
|
||||
|
||||
virtual Base::Ptr new_setup_obj() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,320 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_DHCP_CAPTURE_H
|
||||
#define OPENVPN_TUN_CLIENT_DHCP_CAPTURE_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/ip/ipcommon.hpp>
|
||||
#include <openvpn/ip/dhcp.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class DHCPCapture
|
||||
{
|
||||
public:
|
||||
// We take a TunBuilderCapture object with previously pushed
|
||||
// options and augment it with additional options sniffed
|
||||
// from the DHCP reply.
|
||||
DHCPCapture(const TunBuilderCapture::Ptr& props_arg)
|
||||
: props(props_arg)
|
||||
{
|
||||
if (props->vpn_ipv4() || props->vpn_ipv4())
|
||||
OPENVPN_LOG("NOTE: pushed ifconfig directive is ignored in layer 2 mode");
|
||||
if (!props->dns_servers.empty())
|
||||
OPENVPN_LOG("NOTE: pushed DNS servers are ignored in layer 2 mode");
|
||||
reset();
|
||||
}
|
||||
|
||||
// returns true when router addr and DNS servers are captured
|
||||
bool mod_reply(Buffer& buf)
|
||||
{
|
||||
if (buf.size() < sizeof(DHCPPacket))
|
||||
return false;
|
||||
|
||||
DHCPPacket* dhcp = (DHCPPacket*)buf.data();
|
||||
if (dhcp->ip.protocol == IPCommon::UDP
|
||||
&& dhcp->udp.source == htons(DHCP::BOOTPS_PORT)
|
||||
&& dhcp->udp.dest == htons(DHCP::BOOTPC_PORT)
|
||||
&& dhcp->dhcp.op == DHCP::BOOTREPLY)
|
||||
{
|
||||
const unsigned int optlen = buf.size() - sizeof(DHCPPacket);
|
||||
const int message_type = dhcp_message_type(dhcp, optlen);
|
||||
if (message_type == DHCP::DHCPACK || message_type == DHCP::DHCPOFFER)
|
||||
{
|
||||
/* get host IP address/netmask */
|
||||
const IPv4::Addr host = IPv4::Addr::from_uint32_net(dhcp->dhcp.yiaddr);
|
||||
const IPv4::Addr netmask = get_netmask(dhcp, optlen);
|
||||
const int prefix_len = netmask.prefix_len_nothrow();
|
||||
|
||||
/* get the router IP address while padding out all DHCP router options */
|
||||
const IPv4::Addr router = extract_router(dhcp, optlen);
|
||||
|
||||
/* get DNS server addresses */
|
||||
const std::vector<IPv4::Addr> dns_servers = get_dns(dhcp, optlen);
|
||||
|
||||
/* recompute the UDP checksum */
|
||||
dhcp->udp.check = 0;
|
||||
dhcp->udp.check = htons(udp_checksum((uint8_t *)&dhcp->udp,
|
||||
sizeof(UDPHeader) + sizeof(DHCP) + optlen,
|
||||
(uint8_t *)&dhcp->ip.saddr,
|
||||
(uint8_t *)&dhcp->ip.daddr));
|
||||
|
||||
/* only capture the extracted Router address if DHCPACK */
|
||||
if (message_type == DHCP::DHCPACK && !configured)
|
||||
{
|
||||
bool complete = true;
|
||||
if (host.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain host address via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (netmask.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain netmask via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (prefix_len < 0)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: bad netmask obtained via DHCP: " << netmask);
|
||||
complete = false;
|
||||
}
|
||||
if (router.unspecified())
|
||||
{
|
||||
OPENVPN_LOG("NOTE: failed to obtain router via DHCP");
|
||||
complete = false;
|
||||
}
|
||||
if (complete)
|
||||
{
|
||||
reset();
|
||||
props->tun_builder_add_address(host.to_string(), prefix_len, router.to_string(), false, false);
|
||||
if (dns_servers.empty())
|
||||
OPENVPN_LOG("NOTE: failed to obtain DNS servers via DHCP");
|
||||
else
|
||||
{
|
||||
for (const auto &a : dns_servers)
|
||||
props->tun_builder_add_dns_server(a.to_string(), false);
|
||||
}
|
||||
}
|
||||
return configured = complete;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const TunBuilderCapture& get_props() const
|
||||
{
|
||||
return *props;
|
||||
}
|
||||
|
||||
private:
|
||||
void reset()
|
||||
{
|
||||
props->reset_tunnel_addresses();
|
||||
props->reset_dns_servers();
|
||||
}
|
||||
|
||||
static int dhcp_message_type(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
for (unsigned int i = 0; i < optlen; ++i)
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END) /* didn't find what we were looking for */
|
||||
return -1;
|
||||
else if (type == DHCP::DHCP_PAD) /* no-operation */
|
||||
;
|
||||
else if (type == DHCP::DHCP_MSG_TYPE) /* what we are looking for */
|
||||
{
|
||||
if (room >= 3)
|
||||
{
|
||||
if (p[i+1] == 1) /* option length should be 1 */
|
||||
return p[i+2]; /* return message type */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 1); /* advance to next option */
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static IPv4::Addr extract_router(DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
std::uint8_t* p = dhcp->options;
|
||||
IPv4::Addr ret = IPv4::Addr::from_zero();
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_ROUTER)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2))
|
||||
{
|
||||
/* get router IP address */
|
||||
if (ret.unspecified() && len >= 4 && (len & 3) == 0)
|
||||
ret = IPv4::Addr::from_bytes_net(p + i + 2);
|
||||
|
||||
/* delete the router option */
|
||||
std::uint8_t *dest = p + i;
|
||||
const unsigned int owlen = len + 2; /* len of data to overwrite */
|
||||
std::uint8_t *src = dest + owlen;
|
||||
std::uint8_t *end = p + optlen;
|
||||
const int movlen = end - src;
|
||||
if (movlen > 0)
|
||||
std::memmove(dest, src, movlen); /* overwrite router option */
|
||||
std::memset(end - owlen, DHCP::DHCP_PAD, owlen); /* pad tail */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IPv4::Addr get_netmask(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
IPv4::Addr ret = IPv4::Addr::from_zero();
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_NETMASK)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2) && len == 4)
|
||||
return IPv4::Addr::from_bytes_net(p + i + 2);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<IPv4::Addr> get_dns(const DHCPPacket* dhcp, const unsigned int optlen)
|
||||
{
|
||||
const std::uint8_t* p = dhcp->options;
|
||||
std::vector<IPv4::Addr> ret;
|
||||
|
||||
for (unsigned int i = 0; i < optlen; )
|
||||
{
|
||||
const std::uint8_t type = p[i];
|
||||
const unsigned int room = optlen - i;
|
||||
|
||||
if (type == DHCP::DHCP_END)
|
||||
break;
|
||||
else if (type == DHCP::DHCP_PAD)
|
||||
++i;
|
||||
else if (type == DHCP::DHCP_DNS)
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
if (len <= (room-2) && (len & 3) == 0)
|
||||
{
|
||||
/* get DNS addresses */
|
||||
for (unsigned int j = 0; j < len; j += 4)
|
||||
ret.push_back(IPv4::Addr::from_bytes_net(p + i + j + 2));
|
||||
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* some other option */
|
||||
{
|
||||
if (room >= 2)
|
||||
{
|
||||
const unsigned int len = p[i+1]; /* get option length */
|
||||
i += (len + 2); /* advance to next option */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TunBuilderCapture::Ptr props;
|
||||
bool configured = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Base classes for Emulate Excluded Routes
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_EMUEXR_H
|
||||
#define OPENVPN_TUN_CLIENT_EMUEXR_H
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/client/ipverflags.hpp>
|
||||
#include <openvpn/tun/builder/base.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct EmulateExcludeRoute : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<EmulateExcludeRoute> Ptr;
|
||||
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len) = 0;
|
||||
virtual bool enabled(const IPVerFlags& ipv) const = 0;
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const = 0;
|
||||
virtual void add_default_routes(bool ipv4, bool ipv6) = 0;
|
||||
};
|
||||
|
||||
struct EmulateExcludeRouteFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<EmulateExcludeRouteFactory> Ptr;
|
||||
|
||||
virtual EmulateExcludeRoute::Ptr new_obj() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Abstract base classes for client tun interface objects.
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_TUNBASE_H
|
||||
#define OPENVPN_TUN_CLIENT_TUNBASE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Base class for objects that implement a client tun interface.
|
||||
struct TunClient : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TunClient> Ptr;
|
||||
|
||||
virtual void tun_start(const OptionList&, TransportClient&, CryptoDCSettings&) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void set_disconnect() = 0;
|
||||
virtual bool tun_send(BufferAllocated& buf) = 0; // return true if send succeeded
|
||||
|
||||
virtual std::string tun_name() const = 0;
|
||||
|
||||
virtual std::string vpn_ip4() const = 0; // VPN IP addresses
|
||||
virtual std::string vpn_ip6() const = 0;
|
||||
|
||||
virtual std::string vpn_gw4() const { return std::string(); } // VPN gateways
|
||||
virtual std::string vpn_gw6() const { return std::string(); }
|
||||
};
|
||||
|
||||
// Base class for parent of tun interface object, used to
|
||||
// communicate received data packets, exceptions, and progress
|
||||
// notifications.
|
||||
struct TunClientParent
|
||||
{
|
||||
virtual void tun_recv(BufferAllocated& buf) = 0;
|
||||
virtual void tun_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
|
||||
// progress notifications
|
||||
virtual void tun_pre_tun_config() = 0;
|
||||
virtual void tun_pre_route_config() = 0;
|
||||
virtual void tun_connected() = 0;
|
||||
};
|
||||
|
||||
// Factory for tun interface objects.
|
||||
struct TunClientFactory : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TunClientFactory> Ptr;
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli) = 0;
|
||||
|
||||
// return true if layer 2 tunnels are supported
|
||||
virtual bool layer_2_supported() const { return false; }
|
||||
|
||||
// Called on TunClient close, after TunClient::stop has been called.
|
||||
// disconnected ->
|
||||
// true: this is the final disconnect, or
|
||||
// false: we are in a pause/reconnecting state.
|
||||
virtual void finalize(const bool disconnected) {}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_CLIENT_TUNBASE_H
|
||||
@@ -0,0 +1,120 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Null tun interface object, intended for testing.
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_TUNNULL_H
|
||||
#define OPENVPN_TUN_CLIENT_TUNNULL_H
|
||||
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNull {
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
private:
|
||||
ClientConfig() {}
|
||||
};
|
||||
|
||||
class Client : public TunClient
|
||||
{
|
||||
friend class ClientConfig; // calls constructor
|
||||
|
||||
public:
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&)
|
||||
{
|
||||
#ifdef TUN_NULL_EXIT
|
||||
throw ErrorCode(Error::TUN_SETUP_FAILED, true, "TUN_NULL_EXIT");
|
||||
#else
|
||||
// signal that we are "connected"
|
||||
parent.tun_connected();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf)
|
||||
{
|
||||
config->stats->inc_stat(SessionStats::TUN_BYTES_OUT, buf.size());
|
||||
config->stats->inc_stat(SessionStats::TUN_PACKETS_OUT, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const
|
||||
{
|
||||
return "TUN_NULL";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stop() {}
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg)
|
||||
{
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli)
|
||||
{
|
||||
return TunClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_CLIENT_TUNNULL_H
|
||||
@@ -0,0 +1,654 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Process tun interface properties.
|
||||
|
||||
#ifndef OPENVPN_TUN_CLIENT_TUNPROP_H
|
||||
#define OPENVPN_TUN_CLIENT_TUNPROP_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/tun/builder/base.hpp>
|
||||
#include <openvpn/addr/addrpair.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/client/ipverflags.hpp>
|
||||
#include <openvpn/tun/client/emuexr.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TunProp {
|
||||
// add_dns flags
|
||||
enum {
|
||||
F_ADD_DNS=(1<<0),
|
||||
};
|
||||
|
||||
// render option flags
|
||||
enum {
|
||||
OPT_RENDER_FLAGS = Option::RENDER_TRUNC_64 | Option::RENDER_BRACKET
|
||||
};
|
||||
|
||||
// maximum route metric
|
||||
static constexpr int MAX_ROUTE_METRIC = 1000000;
|
||||
|
||||
public:
|
||||
OPENVPN_EXCEPTION(tun_prop_error);
|
||||
OPENVPN_EXCEPTION(tun_prop_route_error);
|
||||
OPENVPN_EXCEPTION(tun_prop_dhcp_option_error);
|
||||
|
||||
struct Config
|
||||
{
|
||||
std::string session_name;
|
||||
int mtu = 0;
|
||||
bool google_dns_fallback = false;
|
||||
bool allow_local_lan_access = false;
|
||||
Layer layer{Layer::OSI_LAYER_3};
|
||||
|
||||
// If remote_bypass is true, obtain cached remote IPs from
|
||||
// remote_list, and preconfigure exclude route rules for them.
|
||||
// Note that the primary remote IP is not included in the
|
||||
// exclusion list because existing pathways already exist
|
||||
// (i.e. redirect-gateway) for routing this particular address.
|
||||
// This feature is intended to work with tun_persist, so that
|
||||
// the client is not locked out of contacting subsequent
|
||||
// servers in the remote list after the routing configuration
|
||||
// for the initial connection has taken effect.
|
||||
RemoteList::Ptr remote_list;
|
||||
bool remote_bypass = false;
|
||||
};
|
||||
|
||||
struct State : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<State> Ptr;
|
||||
|
||||
std::string iface_name;
|
||||
IP::Addr vpn_ip4_addr;
|
||||
IP::Addr vpn_ip6_addr;
|
||||
IP::Addr vpn_ip4_gw;
|
||||
IP::Addr vpn_ip6_gw;
|
||||
bool tun_prefix = false;
|
||||
};
|
||||
|
||||
static void configure_builder(TunBuilderBase* tb,
|
||||
State* state,
|
||||
SessionStats* stats,
|
||||
const IP::Addr& server_addr,
|
||||
const Config& config,
|
||||
const OptionList& opt,
|
||||
const EmulateExcludeRouteFactory* eer_factory,
|
||||
const bool quiet)
|
||||
{
|
||||
// if eer_factory is defined, we must emulate exclude routes
|
||||
EmulateExcludeRoute::Ptr eer;
|
||||
if (eer_factory)
|
||||
eer = eer_factory->new_obj();
|
||||
|
||||
// do ifconfig
|
||||
IP::Addr::VersionMask ip_ver_flags = tun_ifconfig(tb, state, opt);
|
||||
|
||||
// with layer 2, either IPv4 or IPv6 might be supported
|
||||
if (config.layer() == Layer::OSI_LAYER_2)
|
||||
ip_ver_flags |= (IP::Addr::V4_MASK|IP::Addr::V6_MASK);
|
||||
|
||||
// verify IPv4/IPv6
|
||||
if (!ip_ver_flags)
|
||||
throw tun_prop_error("one of ifconfig or ifconfig-ipv6 must be specified");
|
||||
|
||||
// get IP version and redirect-gateway flags
|
||||
IPVerFlags ipv(opt, ip_ver_flags);
|
||||
|
||||
// add default route-metric
|
||||
add_route_metric_default(tb, opt, quiet);
|
||||
|
||||
// add remote bypass routes
|
||||
if (config.remote_list && config.remote_bypass)
|
||||
add_remote_bypass_routes(tb, *config.remote_list, server_addr, eer.get(), quiet);
|
||||
|
||||
// add routes
|
||||
if (config.allow_local_lan_access)
|
||||
{
|
||||
// query local lan exclude routes and then
|
||||
// copy option list to construct a copy with the excluded routes as route options
|
||||
OptionList excludedRoutesOptions = opt;
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(false))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(true))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route-ipv6", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
add_routes(tb, excludedRoutesOptions, ipv, eer.get(), quiet);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_routes(tb, opt, ipv, eer.get(), quiet);
|
||||
}
|
||||
|
||||
|
||||
if (eer)
|
||||
{
|
||||
// Route emulation needs to know if default routes are included
|
||||
// from redirect-gateway
|
||||
eer->add_default_routes(ipv.rgv4(), ipv.rgv6());
|
||||
// emulate exclude routes
|
||||
eer->emulate(tb, ipv, server_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// configure redirect-gateway
|
||||
if (!tb->tun_builder_reroute_gw(ipv.rgv4(), ipv.rgv6(), ipv.api_flags()))
|
||||
throw tun_prop_route_error("tun_builder_reroute_gw for redirect-gateway failed");
|
||||
}
|
||||
|
||||
// add DNS servers and domain prefixes
|
||||
const unsigned int dhcp_option_flags = add_dhcp_options(tb, opt, quiet);
|
||||
|
||||
// Block IPv6?
|
||||
tb->tun_builder_set_block_ipv6(opt.exists("block-ipv6"));
|
||||
|
||||
// DNS fallback
|
||||
if (ipv.rgv4() && !(dhcp_option_flags & F_ADD_DNS))
|
||||
{
|
||||
if (config.google_dns_fallback)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Google DNS fallback enabled");
|
||||
add_google_dns(tb);
|
||||
}
|
||||
else if (stats && (config.layer() != Layer::OSI_LAYER_2))
|
||||
stats->error(Error::REROUTE_GW_NO_DNS);
|
||||
}
|
||||
|
||||
// set remote server address
|
||||
if (!tb->tun_builder_set_remote_address(server_addr.to_string(),
|
||||
server_addr.version() == IP::Addr::V6))
|
||||
throw tun_prop_error("tun_builder_set_remote_address failed");
|
||||
|
||||
// set layer
|
||||
if (!tb->tun_builder_set_layer(config.layer.value()))
|
||||
throw tun_prop_error("tun_builder_set_layer failed");
|
||||
|
||||
// set MTU
|
||||
if (config.mtu)
|
||||
{
|
||||
if (!tb->tun_builder_set_mtu(config.mtu))
|
||||
throw tun_prop_error("tun_builder_set_mtu failed");
|
||||
}
|
||||
|
||||
// set session name
|
||||
if (!config.session_name.empty())
|
||||
{
|
||||
if (!tb->tun_builder_set_session_name(config.session_name))
|
||||
throw tun_prop_error("tun_builder_set_session_name failed");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static void add_route_metric_default(TunBuilderBase* tb,
|
||||
const OptionList& opt,
|
||||
const bool quiet)
|
||||
{
|
||||
try {
|
||||
const Option* o = opt.get_ptr("route-metric"); // DIRECTIVE
|
||||
if (o)
|
||||
{
|
||||
const int metric = o->get_num<int>(1);
|
||||
if (metric < 0 || metric > MAX_ROUTE_METRIC)
|
||||
throw tun_prop_error("route-metric is out of range");
|
||||
if (!tb->tun_builder_set_route_metric_default(metric))
|
||||
throw tun_prop_error("tun_builder_set_route_metric_default failed");
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception processing route-metric: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static IP::Addr route_gateway(const OptionList& opt)
|
||||
{
|
||||
IP::Addr gateway;
|
||||
const Option* o = opt.get_ptr("route-gateway"); // DIRECTIVE
|
||||
if (o)
|
||||
{
|
||||
gateway = IP::Addr::from_string(o->get(1, 256), "route-gateway");
|
||||
if (gateway.version() != IP::Addr::V4)
|
||||
throw tun_prop_error("route-gateway is not IPv4 (IPv6 route-gateway is passed with ifconfig-ipv6 directive)");
|
||||
}
|
||||
return gateway;
|
||||
}
|
||||
|
||||
static IP::Addr::VersionMask tun_ifconfig(TunBuilderBase* tb,
|
||||
State* state,
|
||||
const OptionList& opt)
|
||||
{
|
||||
enum Topology {
|
||||
NET30,
|
||||
SUBNET,
|
||||
};
|
||||
|
||||
IP::Addr::VersionMask ip_ver_flags = 0;
|
||||
|
||||
// get topology
|
||||
Topology top = NET30;
|
||||
{
|
||||
const Option* o = opt.get_ptr("topology"); // DIRECTIVE
|
||||
if (o)
|
||||
{
|
||||
const std::string& topstr = o->get(1, 16);
|
||||
if (topstr == "subnet")
|
||||
top = SUBNET;
|
||||
else if (topstr == "net30")
|
||||
top = NET30;
|
||||
else
|
||||
throw option_error("only topology 'subnet' and 'net30' supported");
|
||||
}
|
||||
}
|
||||
|
||||
// configure tun interface
|
||||
{
|
||||
const Option* o;
|
||||
o = opt.get_ptr("ifconfig"); // DIRECTIVE
|
||||
if (o)
|
||||
{
|
||||
if (top == SUBNET)
|
||||
{
|
||||
const IP::AddrMaskPair pair = IP::AddrMaskPair::from_string(o->get(1, 256), o->get_optional(2, 256), "ifconfig");
|
||||
const IP::Addr gateway = route_gateway(opt);
|
||||
if (pair.version() != IP::Addr::V4)
|
||||
throw tun_prop_error("ifconfig address is not IPv4 (topology subnet)");
|
||||
if (!tb->tun_builder_add_address(pair.addr.to_string(),
|
||||
pair.netmask.prefix_len(),
|
||||
gateway.to_string(),
|
||||
false, // IPv6
|
||||
false)) // net30
|
||||
throw tun_prop_error("tun_builder_add_address IPv4 failed (topology subnet)");
|
||||
if (state)
|
||||
{
|
||||
state->vpn_ip4_addr = pair.addr;
|
||||
state->vpn_ip4_gw = gateway;
|
||||
}
|
||||
ip_ver_flags |= IP::Addr::V4_MASK;
|
||||
}
|
||||
else if (top == NET30)
|
||||
{
|
||||
const IP::Addr remote = IP::Addr::from_string(o->get(2, 256));
|
||||
const IP::Addr local = IP::Addr::from_string(o->get(1, 256));
|
||||
const IP::Addr netmask = IP::Addr::from_string("255.255.255.252");
|
||||
if (local.version() != IP::Addr::V4 || remote.version() != IP::Addr::V4)
|
||||
throw tun_prop_error("ifconfig address is not IPv4 (topology net30)");
|
||||
if ((local & netmask) != (remote & netmask))
|
||||
throw tun_prop_error("ifconfig addresses are not in the same /30 subnet (topology net30)");
|
||||
if (!tb->tun_builder_add_address(local.to_string(),
|
||||
netmask.prefix_len(),
|
||||
remote.to_string(),
|
||||
false, // IPv6
|
||||
true)) // net30
|
||||
throw tun_prop_error("tun_builder_add_address IPv4 failed (topology net30)");
|
||||
if (state)
|
||||
{
|
||||
state->vpn_ip4_addr = local;
|
||||
state->vpn_ip4_gw = remote;
|
||||
}
|
||||
ip_ver_flags |= IP::Addr::V4_MASK;
|
||||
}
|
||||
else
|
||||
throw option_error("internal topology error");
|
||||
}
|
||||
|
||||
o = opt.get_ptr("ifconfig-ipv6"); // DIRECTIVE
|
||||
if (o)
|
||||
{
|
||||
// We don't check topology setting here since it doesn't really affect IPv6
|
||||
const IP::AddrMaskPair pair = IP::AddrMaskPair::from_string(o->get(1, 256), "ifconfig-ipv6");
|
||||
if (pair.version() != IP::Addr::V6)
|
||||
throw tun_prop_error("ifconfig-ipv6 address is not IPv6");
|
||||
std::string gateway_str;
|
||||
if (o->size() >= 3)
|
||||
{
|
||||
const IP::Addr gateway = IP::Addr::from_string(o->get(2, 256), "ifconfig-ipv6");
|
||||
if (gateway.version() != IP::Addr::V6)
|
||||
throw tun_prop_error("ifconfig-ipv6 gateway is not IPv6");
|
||||
gateway_str = gateway.to_string();
|
||||
if (state)
|
||||
state->vpn_ip6_gw = gateway;
|
||||
}
|
||||
if (!tb->tun_builder_add_address(pair.addr.to_string(),
|
||||
pair.netmask.prefix_len(),
|
||||
gateway_str,
|
||||
true, // IPv6
|
||||
false)) // net30
|
||||
throw tun_prop_error("tun_builder_add_address IPv6 failed");
|
||||
if (state)
|
||||
state->vpn_ip6_addr = pair.addr;
|
||||
ip_ver_flags |= IP::Addr::V6_MASK;
|
||||
}
|
||||
|
||||
return ip_ver_flags;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_exclude_route(TunBuilderBase* tb,
|
||||
bool add,
|
||||
const IP::Addr& addr,
|
||||
int prefix_length,
|
||||
int metric,
|
||||
bool ipv6,
|
||||
EmulateExcludeRoute* eer)
|
||||
{
|
||||
const std::string addr_str = addr.to_string();
|
||||
if (eer)
|
||||
eer->add_route(add, addr, prefix_length);
|
||||
else if (add)
|
||||
{
|
||||
if (!tb->tun_builder_add_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
else if (!eer)
|
||||
{
|
||||
if (!tb->tun_builder_exclude_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_exclude_route failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check the target of a route.
|
||||
// Return true if route should be added or false if route should be excluded.
|
||||
static bool route_target(const Option& o, const size_t target_index)
|
||||
{
|
||||
if (o.size() >= (target_index+1))
|
||||
{
|
||||
const std::string& target = o.ref(target_index);
|
||||
if (target == "vpn_gateway")
|
||||
return true;
|
||||
else if (target == "net_gateway")
|
||||
return false;
|
||||
else
|
||||
throw tun_prop_route_error("route destinations other than vpn_gateway or net_gateway are not supported");
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_routes(TunBuilderBase* tb,
|
||||
const OptionList& opt,
|
||||
const IPVerFlags& ipv,
|
||||
EmulateExcludeRoute* eer,
|
||||
const bool quiet)
|
||||
{
|
||||
// add IPv4 routes
|
||||
if (ipv.v4())
|
||||
{
|
||||
OptionList::IndexMap::const_iterator dopt = opt.map().find("route"); // DIRECTIVE
|
||||
if (dopt != opt.map().end())
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = dopt->second.begin(); i != dopt->second.end(); ++i)
|
||||
{
|
||||
const Option& o = opt[*i];
|
||||
try {
|
||||
const IP::AddrMaskPair pair = IP::AddrMaskPair::from_string(o.get(1, 256), o.get_optional(2, 256), "route");
|
||||
const int metric = o.get_num<int>(4, -1, 0, MAX_ROUTE_METRIC);
|
||||
if (!pair.is_canonical())
|
||||
throw tun_prop_error("route is not canonical");
|
||||
if (pair.version() != IP::Addr::V4)
|
||||
throw tun_prop_error("route is not IPv4");
|
||||
const bool add = route_target(o, 3);
|
||||
add_exclude_route(tb, add, pair.addr, pair.netmask.prefix_len(), metric, false, eer);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception parsing IPv4 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add IPv6 routes
|
||||
if (ipv.v6())
|
||||
{
|
||||
OptionList::IndexMap::const_iterator dopt = opt.map().find("route-ipv6"); // DIRECTIVE
|
||||
if (dopt != opt.map().end())
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = dopt->second.begin(); i != dopt->second.end(); ++i)
|
||||
{
|
||||
const Option& o = opt[*i];
|
||||
try {
|
||||
const IP::AddrMaskPair pair = IP::AddrMaskPair::from_string(o.get(1, 256), "route-ipv6");
|
||||
const int metric = o.get_num<int>(3, -1, 0, MAX_ROUTE_METRIC);
|
||||
if (!pair.is_canonical())
|
||||
throw tun_prop_error("route is not canonical");
|
||||
if (pair.version() != IP::Addr::V6)
|
||||
throw tun_prop_error("route is not IPv6");
|
||||
const bool add = route_target(o, 2);
|
||||
add_exclude_route(tb, add, pair.addr, pair.netmask.prefix_len(), metric, true, eer);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception parsing IPv6 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_remote_bypass_routes(TunBuilderBase* tb,
|
||||
const RemoteList& remote_list,
|
||||
const IP::Addr& server_addr,
|
||||
EmulateExcludeRoute* eer,
|
||||
const bool quiet)
|
||||
{
|
||||
IP::AddrList addrlist;
|
||||
remote_list.cached_ip_address_list(addrlist);
|
||||
for (IP::AddrList::const_iterator i = addrlist.begin(); i != addrlist.end(); ++i)
|
||||
{
|
||||
const IP::Addr& addr = *i;
|
||||
if (addr != server_addr)
|
||||
{
|
||||
try {
|
||||
const IP::Addr::Version ver = addr.version();
|
||||
add_exclude_route(tb, false, addr, IP::Addr::version_size(ver), -1, ver == IP::Addr::V6, eer);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception adding remote bypass route: " << addr.to_string() << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int add_dhcp_options(TunBuilderBase* tb, const OptionList& opt, const bool quiet)
|
||||
{
|
||||
// Example:
|
||||
// [dhcp-option] [DNS] [172.16.0.23]
|
||||
// [dhcp-option] [WINS] [172.16.0.23]
|
||||
// [dhcp-option] [DOMAIN] [openvpn.net]
|
||||
// [dhcp-option] [DOMAIN] [example.com]
|
||||
// [dhcp-option] [DOMAIN] [foo1.com foo2.com foo3.com ...]
|
||||
// [dhcp-option] [DOMAIN] [bar1.com] [bar2.com] [bar3.com] ...
|
||||
// [dhcp-option] [ADAPTER_DOMAIN_SUFFIX] [mycompany.com]
|
||||
// [dhcp-option] [PROXY_HTTP] [foo.bar.gov] [1234]
|
||||
// [dhcp-option] [PROXY_HTTPS] [foo.bar.gov] [1234]
|
||||
// [dhcp-option] [PROXY_BYPASS] [server1] [server2] ...
|
||||
// [dhcp-option] [PROXY_AUTO_CONFIG_URL] [http://...]
|
||||
unsigned int flags = 0;
|
||||
OptionList::IndexMap::const_iterator dopt = opt.map().find("dhcp-option"); // DIRECTIVE
|
||||
if (dopt != opt.map().end())
|
||||
{
|
||||
std::string auto_config_url;
|
||||
std::string http_host;
|
||||
unsigned int http_port = 0;
|
||||
std::string https_host;
|
||||
unsigned int https_port = 0;
|
||||
for (OptionList::IndexList::const_iterator i = dopt->second.begin(); i != dopt->second.end(); ++i)
|
||||
{
|
||||
const Option& o = opt[*i];
|
||||
try {
|
||||
const std::string& type = o.get(1, 64);
|
||||
if (type == "DNS" || type == "DNS6")
|
||||
{
|
||||
o.exact_args(3);
|
||||
const IP::Addr ip = IP::Addr::from_string(o.get(2, 256), "dns-server-ip");
|
||||
if (!tb->tun_builder_add_dns_server(ip.to_string(),
|
||||
ip.version() == IP::Addr::V6))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_add_dns_server failed");
|
||||
flags |= F_ADD_DNS;
|
||||
}
|
||||
else if (type == "DOMAIN")
|
||||
{
|
||||
o.min_args(3);
|
||||
for (size_t j = 2; j < o.size(); ++j)
|
||||
{
|
||||
typedef std::vector<std::string> strvec;
|
||||
strvec v = Split::by_space<strvec, StandardLex, SpaceMatch, Split::NullLimit>(o.get(j, 256));
|
||||
for (size_t k = 0; k < v.size(); ++k)
|
||||
{
|
||||
if (!tb->tun_builder_add_search_domain(v[k]))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_add_search_domain failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == "ADAPTER_DOMAIN_SUFFIX")
|
||||
{
|
||||
o.exact_args(3);
|
||||
const std::string& adapter_domain_suffix = o.get(2, 256);
|
||||
if (!tb->tun_builder_set_adapter_domain_suffix(adapter_domain_suffix))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_set_adapter_domain_suffix");
|
||||
}
|
||||
else if (type == "PROXY_BYPASS")
|
||||
{
|
||||
o.min_args(3);
|
||||
for (size_t j = 2; j < o.size(); ++j)
|
||||
{
|
||||
typedef std::vector<std::string> strvec;
|
||||
strvec v = Split::by_space<strvec, StandardLex, SpaceMatch, Split::NullLimit>(o.get(j, 256));
|
||||
for (size_t k = 0; k < v.size(); ++k)
|
||||
{
|
||||
if (!tb->tun_builder_add_proxy_bypass(v[k]))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_add_proxy_bypass");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == "PROXY_AUTO_CONFIG_URL")
|
||||
{
|
||||
o.exact_args(3);
|
||||
auto_config_url = o.get(2, 256);
|
||||
}
|
||||
else if (type == "PROXY_HTTP")
|
||||
{
|
||||
o.exact_args(4);
|
||||
http_host = o.get(2, 256);
|
||||
HostPort::validate_port(o.get(3, 256), "PROXY_HTTP", &http_port);
|
||||
}
|
||||
else if (type == "PROXY_HTTPS")
|
||||
{
|
||||
o.exact_args(4);
|
||||
https_host = o.get(2, 256);
|
||||
HostPort::validate_port(o.get(3, 256), "PROXY_HTTPS", &https_port);
|
||||
}
|
||||
else if (type == "WINS")
|
||||
{
|
||||
o.exact_args(3);
|
||||
const IP::Addr ip = IP::Addr::from_string(o.get(2, 256), "wins-server-ip");
|
||||
if (ip.version() != IP::Addr::V4)
|
||||
throw tun_prop_dhcp_option_error("WINS addresses must be IPv4");
|
||||
if (!tb->tun_builder_add_wins_server(ip.to_string()))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_add_wins_server failed");
|
||||
}
|
||||
else if (!quiet)
|
||||
OPENVPN_LOG("Unknown pushed DHCP option: " << o.render(OPT_RENDER_FLAGS));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!http_host.empty())
|
||||
{
|
||||
if (!tb->tun_builder_set_proxy_http(http_host, http_port))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_set_proxy_http");
|
||||
}
|
||||
if (!https_host.empty())
|
||||
{
|
||||
if (!tb->tun_builder_set_proxy_https(https_host, https_port))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_set_proxy_https");
|
||||
}
|
||||
if (!auto_config_url.empty())
|
||||
{
|
||||
if (!tb->tun_builder_set_proxy_auto_config_url(auto_config_url))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_set_proxy_auto_config_url");
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception setting dhcp-option for proxy: " << e.what());
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static bool search_domains_exist(const OptionList& opt, const bool quiet)
|
||||
{
|
||||
OptionList::IndexMap::const_iterator dopt = opt.map().find("dhcp-option"); // DIRECTIVE
|
||||
if (dopt != opt.map().end())
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = dopt->second.begin(); i != dopt->second.end(); ++i)
|
||||
{
|
||||
const Option& o = opt[*i];
|
||||
try {
|
||||
const std::string& type = o.get(1, 64);
|
||||
if (type == "DOMAIN")
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("exception parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_google_dns(TunBuilderBase* tb)
|
||||
{
|
||||
if (!tb->tun_builder_add_dns_server("8.8.8.8", false)
|
||||
|| !tb->tun_builder_add_dns_server("8.8.4.4", false))
|
||||
throw tun_prop_dhcp_option_error("tun_builder_add_dns_server failed for Google DNS");
|
||||
}
|
||||
};
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_CLIENT_TUNPROP_H
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_EXTERN_CONFIG_H
|
||||
#define OPENVPN_TUN_EXTERN_CONFIG_H
|
||||
|
||||
// These includes are also intended to resolve forward references in fw.hpp
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace ExternalTun {
|
||||
struct Config
|
||||
{
|
||||
TunProp::Config tun_prop;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
Stop* stop = nullptr;
|
||||
bool tun_persist = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_EXTERN_FW_H
|
||||
#define OPENVPN_TUN_EXTERN_FW_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
|
||||
|
||||
struct TunClientFactory;
|
||||
class OptionList;
|
||||
|
||||
namespace ExternalTun {
|
||||
struct Config; // defined in config.hpp
|
||||
struct Factory
|
||||
{
|
||||
virtual TunClientFactory* new_tun_factory(const Config& conf, const OptionList& opt) = 0;
|
||||
virtual ~Factory() {}
|
||||
};
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace ExternalTun {
|
||||
struct Factory {};
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_IPv6_SETTING_H
|
||||
#define OPENVPN_TUN_IPv6_SETTING_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class IPv6Setting
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
No,
|
||||
Yes,
|
||||
Default,
|
||||
};
|
||||
|
||||
IPv6Setting()
|
||||
{
|
||||
}
|
||||
|
||||
explicit IPv6Setting(const Type t)
|
||||
: type_(t)
|
||||
{
|
||||
}
|
||||
|
||||
Type operator()() const { return type_; }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case No:
|
||||
return "no";
|
||||
case Yes:
|
||||
return "yes";
|
||||
case Default:
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
|
||||
static IPv6Setting parse(const std::string& str)
|
||||
{
|
||||
if (str == "no")
|
||||
return IPv6Setting(No);
|
||||
else if (str == "yes")
|
||||
return IPv6Setting(Yes);
|
||||
else if (str == "default")
|
||||
return IPv6Setting(Default);
|
||||
else
|
||||
throw Exception("IPv6Setting: unrecognized setting: '" + str + '\'');
|
||||
}
|
||||
|
||||
bool operator==(const IPv6Setting& other) const
|
||||
{
|
||||
return type_ == other.type_;
|
||||
}
|
||||
|
||||
bool operator!=(const IPv6Setting& other) const
|
||||
{
|
||||
return type_ != other.type_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_ = Default;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Class that encapsulates the definition of an OSI layer.
|
||||
|
||||
#ifndef OPENVPN_TUN_LAYER_H
|
||||
#define OPENVPN_TUN_LAYER_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class Layer
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
NONE,
|
||||
OSI_LAYER_2,
|
||||
OSI_LAYER_3,
|
||||
};
|
||||
|
||||
Layer() : type_(NONE) {}
|
||||
explicit Layer(const Type t) : type_(t) {}
|
||||
Type operator()() const { return type_; }
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return type_ != NONE;
|
||||
}
|
||||
|
||||
const char *dev_type() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case OSI_LAYER_2:
|
||||
return "tap";
|
||||
case OSI_LAYER_3:
|
||||
return "tun";
|
||||
default:
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
const char *str() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return "UNDEF_LAYER";
|
||||
case OSI_LAYER_2:
|
||||
return "OSI_LAYER_2";
|
||||
case OSI_LAYER_3:
|
||||
return "OSI_LAYER_3";
|
||||
default:
|
||||
throw Exception("Layer: unrecognized layer type");
|
||||
}
|
||||
}
|
||||
|
||||
int value() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return 0;
|
||||
case OSI_LAYER_2:
|
||||
return 2;
|
||||
case OSI_LAYER_3:
|
||||
return 3;
|
||||
default:
|
||||
throw Exception("Layer: unrecognized layer type");
|
||||
}
|
||||
}
|
||||
|
||||
static Layer from_str(const std::string& str)
|
||||
{
|
||||
if (str == "OSI_LAYER_3")
|
||||
return Layer(OSI_LAYER_3);
|
||||
else if (str == "OSI_LAYER_2")
|
||||
return Layer(OSI_LAYER_2);
|
||||
else if (str == "UNDEF_LAYER")
|
||||
return Layer(NONE);
|
||||
else
|
||||
throw Exception("Layer: unrecognized layer string");
|
||||
}
|
||||
|
||||
static Layer from_value(const int value)
|
||||
{
|
||||
if (value == 3)
|
||||
return Layer(OSI_LAYER_3);
|
||||
else if (value == 2)
|
||||
return Layer(OSI_LAYER_2);
|
||||
else if (value == 0)
|
||||
return Layer(NONE);
|
||||
else
|
||||
throw Exception("Layer: unrecognized layer value");
|
||||
}
|
||||
|
||||
bool operator==(const Layer& other) const
|
||||
{
|
||||
return type_ == other.type_;
|
||||
}
|
||||
|
||||
bool operator!=(const Layer& other) const
|
||||
{
|
||||
return type_ != other.type_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
};
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_LAYER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,356 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun interface for Linux.
|
||||
|
||||
#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
#define OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/common/cleanup.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
|
||||
// check if Netlink has been selected at compile time
|
||||
#ifdef OPENVPN_USE_SITNL
|
||||
#include <openvpn/tun/linux/client/tunnetlink.hpp>
|
||||
#define TUN_LINUX openvpn::TunNetlink::TunMethods
|
||||
#else
|
||||
#include <openvpn/tun/linux/client/tuniproute.hpp>
|
||||
#define TUN_LINUX openvpn::TunIPRoute::TunMethods
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
template <typename ReadHandler>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor>
|
||||
{
|
||||
typedef TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor> Base;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
Tun(openvpn_io::io_context& io_context,
|
||||
ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const int socket,
|
||||
const std::string& name)
|
||||
: Base(read_handler_arg, frame_arg, stats_arg)
|
||||
{
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = true;
|
||||
Base::stream = new openvpn_io::posix::stream_descriptor(io_context, socket);
|
||||
OPENVPN_LOG_TUN(Base::name_ << " opened");
|
||||
}
|
||||
|
||||
~Tun() { Base::stop(); }
|
||||
};
|
||||
|
||||
typedef TunPersistTemplate<ScopedFD> TunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
std::string dev_name;
|
||||
int txqueuelen = 200;
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
|
||||
int n_parallel = 8;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
TunBuilderSetup::Factory::Ptr tun_setup_factory;
|
||||
TunPersist::Ptr tun_persist;
|
||||
|
||||
void load(const OptionList& opt)
|
||||
{
|
||||
// set a default MTU
|
||||
if (!tun_prop.mtu)
|
||||
tun_prop.mtu = 1500;
|
||||
|
||||
// parse "dev" option
|
||||
if (dev_name.empty())
|
||||
{
|
||||
const Option* dev = opt.get_ptr("dev");
|
||||
if (dev)
|
||||
dev_name = dev->get(1, 64);
|
||||
}
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
|
||||
TunBuilderSetup::Base::Ptr new_setup_obj()
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TunLinuxSetup::Setup<TUN_LINUX>();
|
||||
}
|
||||
|
||||
private:
|
||||
ClientConfig() {}
|
||||
};
|
||||
|
||||
class Client : public TunClient
|
||||
{
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend class TunIO<Client*, PacketFrom, openvpn_io::posix::stream_descriptor>; // calls tun_read_handler
|
||||
|
||||
typedef Tun<Client*> TunImpl;
|
||||
|
||||
public:
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
|
||||
if (config->tun_persist)
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: long-term session scope");
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: short-term connection scope");
|
||||
tun_persist.reset(new TunPersist(true, false, nullptr)); // short-term
|
||||
}
|
||||
|
||||
try {
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
int sd = -1;
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state();
|
||||
sd = tun_persist->obj();
|
||||
state = tun_persist->state();
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// close old tun handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
TunLinuxSetup::Setup<TUN_LINUX>::Config tsconf;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
tsconf.dev_name = config->dev_name;
|
||||
tsconf.txqueuelen = config->txqueuelen;
|
||||
tsconf.add_bypass_routes_on_establish = true;
|
||||
|
||||
// open/config tun
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
sd = tun_setup->establish(*po, &tsconf, nullptr, os);
|
||||
}
|
||||
|
||||
// persist tun settings state
|
||||
state->iface_name = tsconf.iface_name;
|
||||
tun_persist->persist_tun_state(sd, state);
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
}
|
||||
|
||||
// start tun
|
||||
impl.reset(new TunImpl(io_context,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats,
|
||||
sd,
|
||||
state->iface_name
|
||||
));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
// signal that we are connected
|
||||
parent.tun_connected();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->close();
|
||||
|
||||
stop();
|
||||
parent.tun_error(Error::TUN_SETUP_FAILED, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const override
|
||||
{
|
||||
if (impl)
|
||||
return impl->name();
|
||||
else
|
||||
return "UNDEF_TUN";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
halt(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool send(Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
return impl->write(buf);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
}
|
||||
|
||||
void tun_error_handler(const Error::Type errtype, // called by TunImpl
|
||||
const openvpn_io::error_code* error)
|
||||
{
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist;
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
TunProp::State::Ptr state;
|
||||
TunBuilderSetup::Base::Ptr tun_setup;
|
||||
bool halt;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli)
|
||||
{
|
||||
return TunClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
@@ -0,0 +1,351 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunIPRoute {
|
||||
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
struct TunMethods
|
||||
{
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes = true)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
LinuxGW46 gw(true);
|
||||
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/netconf/linux/gwnetlink.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNetlink {
|
||||
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
struct NetlinkLinkSet : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkLinkSet> Ptr;
|
||||
|
||||
NetlinkLinkSet() {}
|
||||
|
||||
NetlinkLinkSet(std::string dev_arg, bool up_arg, int mtu_arg)
|
||||
: dev(dev_arg),
|
||||
up(up_arg),
|
||||
mtu(mtu_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkLinkSet* copy() const
|
||||
{
|
||||
NetlinkLinkSet *ret = new NetlinkLinkSet;
|
||||
ret->dev = dev;
|
||||
ret->up = up;
|
||||
ret->mtu = mtu;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkLinkSet with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = SITNL::net_iface_mtu_set(dev, mtu);
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkLinkSet " << dev << " mtu " << mtu
|
||||
<< ": " << ret << std::endl;
|
||||
}
|
||||
|
||||
ret = SITNL::net_iface_up(dev, up);
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkLinkSet " << dev << " up " << up
|
||||
<< ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " link set " << up << " mtu " << mtu;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
bool up;
|
||||
int mtu;
|
||||
};
|
||||
|
||||
struct NetlinkAddr4 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr4> Ptr;
|
||||
|
||||
NetlinkAddr4() {}
|
||||
|
||||
NetlinkAddr4(std::string dev_arg, IPv4::Addr& addr_arg, int prefixlen_arg,
|
||||
IPv4::Addr& broadcast_arg, bool add_arg)
|
||||
: dev(dev_arg),
|
||||
addr(addr_arg),
|
||||
prefixlen(prefixlen_arg),
|
||||
broadcast(broadcast_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr4* copy() const
|
||||
{
|
||||
NetlinkAddr4 *ret = new NetlinkAddr4;
|
||||
ret->dev = dev;
|
||||
ret->addr = addr;
|
||||
ret->prefixlen = prefixlen;
|
||||
ret->broadcast = broadcast;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr4 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_add(dev, addr, prefixlen, broadcast);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_del(dev, addr, prefixlen);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr4(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " " << (add ? "add" : "del") << " "
|
||||
<< addr.to_string() << "/" << prefixlen << " broadcast "
|
||||
<< broadcast.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv4::Addr addr;
|
||||
int prefixlen;
|
||||
IPv4::Addr broadcast;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkAddr6 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr6> Ptr;
|
||||
|
||||
NetlinkAddr6() {}
|
||||
|
||||
NetlinkAddr6(std::string dev_arg, IPv6::Addr& addr_arg, int prefixlen_arg,
|
||||
bool add_arg)
|
||||
: dev(dev_arg),
|
||||
addr(addr_arg),
|
||||
prefixlen(prefixlen_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr6* copy() const
|
||||
{
|
||||
NetlinkAddr6 *ret = new NetlinkAddr6;
|
||||
ret->dev = dev;
|
||||
ret->addr = addr;
|
||||
ret->prefixlen = prefixlen;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr6 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_add(dev, addr, prefixlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_del(dev, addr, prefixlen);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr6(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink iface " << dev << " " << (add ? "add" : "del") << " "
|
||||
<< addr.to_string() << "/" << prefixlen;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv6::Addr addr;
|
||||
int prefixlen;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkAddr4PtP : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkAddr4PtP> Ptr;
|
||||
|
||||
NetlinkAddr4PtP() {}
|
||||
|
||||
NetlinkAddr4PtP(std::string dev_arg, IPv4::Addr local_arg,
|
||||
IPv4::Addr remote_arg, bool add_arg)
|
||||
: dev(dev_arg),
|
||||
local(local_arg),
|
||||
remote(remote_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkAddr4PtP* copy() const
|
||||
{
|
||||
NetlinkAddr4PtP *ret = new NetlinkAddr4PtP;
|
||||
ret->dev = dev;
|
||||
ret->local = local;
|
||||
ret->remote = remote;
|
||||
ret->add = add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkAddr4PtP with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_addr_ptp_add(dev, local, remote);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_addr_ptp_del(dev, local, remote);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkAddr4PtP(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
return "netlink iface " + dev + " " + (add ? "add" : "del") + " ptp "
|
||||
+ local.to_string() + " remote " + remote.to_string();
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
IPv4::Addr local;
|
||||
IPv4::Addr remote;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkRoute4 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkRoute4> Ptr;
|
||||
|
||||
NetlinkRoute4() {}
|
||||
|
||||
NetlinkRoute4(IPv4::Addr& dst_arg, int prefixlen_arg, IPv4::Addr& gw_arg,
|
||||
std::string dev_arg, bool add_arg)
|
||||
: route(dst_arg, prefixlen_arg),
|
||||
gw(gw_arg),
|
||||
dev(dev_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkRoute4* copy() const
|
||||
{
|
||||
NetlinkRoute4 *ret = new NetlinkRoute4;
|
||||
ret->route = route;
|
||||
ret->gw = gw;
|
||||
ret->dev = dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkRoute4 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_route_add(route, gw, dev, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_route_del(route, gw, dev, 0, 0);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkRoute4(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " "
|
||||
<< route << " via " << gw.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
IP::Route4 route;
|
||||
IPv4::Addr gw;
|
||||
std::string dev;
|
||||
bool add;
|
||||
};
|
||||
|
||||
struct NetlinkRoute6 : public Action
|
||||
{
|
||||
typedef RCPtr<NetlinkRoute6> Ptr;
|
||||
|
||||
NetlinkRoute6() {}
|
||||
|
||||
NetlinkRoute6(IPv6::Addr& dst_arg, int prefixlen_arg, IPv6::Addr& gw_arg,
|
||||
std::string dev_arg, bool add_arg)
|
||||
: route(dst_arg, prefixlen_arg),
|
||||
gw(gw_arg),
|
||||
dev(dev_arg),
|
||||
add(add_arg)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkRoute6* copy() const
|
||||
{
|
||||
NetlinkRoute6 *ret = new NetlinkRoute6;
|
||||
ret->route = route;
|
||||
ret->gw = gw;
|
||||
ret->dev = dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev.empty())
|
||||
{
|
||||
os << "Error: can't call NetlinkRoute6 with no interface" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
ret = SITNL::net_route_add(route, gw, dev, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SITNL::net_route_del(route, gw, dev, 0, 0);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
os << "Error while executing NetlinkRoute6(add: " << add << ") "
|
||||
<< dev << ": " << ret << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " "
|
||||
<< route << " via " << gw.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
IP::Route6 route;
|
||||
IPv6::Addr gw;
|
||||
std::string dev;
|
||||
bool add;
|
||||
};
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
/*inline IPv4::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}*/
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
NetlinkRoute6::Ptr add(new NetlinkRoute6);
|
||||
add->route.addr = net;
|
||||
add->route.prefix_len = prefix_len;
|
||||
add->gw = IPv6::Addr::from_string(gateway_str);
|
||||
add->dev = dev;
|
||||
add->add = true;
|
||||
|
||||
create = add;
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkRoute6::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
NetlinkRoute4::Ptr add(new NetlinkRoute4);
|
||||
add->route.addr = net;
|
||||
add->route.prefix_len = prefix_len;
|
||||
add->gw = IPv4::Addr::from_string(gateway_str);
|
||||
add->dev = dev;
|
||||
add->add = true;
|
||||
|
||||
create = add;
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkRoute4::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
NetlinkLinkSet::Ptr add(new NetlinkLinkSet);
|
||||
add->dev = iface_name;
|
||||
add->up = true;
|
||||
add->mtu = mtu;
|
||||
|
||||
create.add(add);
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
NetlinkLinkSet::Ptr del(add->copy());
|
||||
del->up = false;
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
NetlinkAddr4::Ptr add(new NetlinkAddr4);
|
||||
add->addr = IPv4::Addr::from_string(local4->address);
|
||||
add->prefixlen = local4->prefix_length;
|
||||
add->broadcast = IPv4::Addr::from_string(local4->address)
|
||||
| ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length);
|
||||
add->dev = iface_name;
|
||||
add->add = true;
|
||||
// if (unit >= 0)
|
||||
// {
|
||||
// add->argv.push_back("label");
|
||||
// add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
// }
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkAddr4::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
NetlinkAddr6::Ptr add(new NetlinkAddr6);
|
||||
add->addr = IPv6::Addr::from_string(local6->address);
|
||||
add->prefixlen = local6->prefix_length;
|
||||
add->dev = iface_name;
|
||||
add->add = true;
|
||||
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
NetlinkAddr6::Ptr del(add->copy());
|
||||
del->add = false;
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
struct TunMethods
|
||||
{
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
LinuxGW46Netlink gw(iface_name);
|
||||
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, false, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, true, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
LinuxGW46Netlink gw(tun_iface_name, address);
|
||||
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
@@ -0,0 +1,244 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun interface for Linux.
|
||||
|
||||
#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
|
||||
#define OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinuxSetup {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
template <class TUNMETHODS>
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
// This empty constructor shouldn't be needed, but due to a
|
||||
// plausible compiler bug in GCC 4.8.5 (RHEL 7), this empty
|
||||
// constructor is required to be able to build. This is
|
||||
// related to the member initialization of the private
|
||||
// remove_cmds_bypass_gw and remove_cmds class members.
|
||||
Setup() {}
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
bool add_bypass_routes_on_establish; // required when not using tunbuilder
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["dev_name"] = Json::Value(dev_name);
|
||||
root["txqueuelen"] = Json::Value(txqueuelen);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_string(root, dev_name, "dev_name", title);
|
||||
json::to_int(root, txqueuelen, "txqueuelen", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
remove_cmds->execute(os);
|
||||
|
||||
// remove bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
}
|
||||
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// nothing to do if we reconnect to the same gateway
|
||||
if (connected_gw == address)
|
||||
return true;
|
||||
|
||||
// remove previous bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
remove_cmds_bypass_gw->clear();
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw);
|
||||
|
||||
// add gateway bypass route
|
||||
add_cmds->execute(os);
|
||||
return true;
|
||||
}
|
||||
|
||||
int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_linux_error("missing config");
|
||||
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (conf->layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(conf->dev_name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (conf->txqueuelen)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = conf->txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
tun_iface_name = ifr.ifr_name;
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
ActionList::Ptr remove_cmds_new = new ActionListReversed();
|
||||
|
||||
// configure tun properties
|
||||
TUNMETHODS::tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds_new, conf->add_bypass_routes_on_establish);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(os);
|
||||
|
||||
// tear down old routes
|
||||
remove_cmds->execute(os);
|
||||
std::swap(remove_cmds, remove_cmds_new);
|
||||
|
||||
connected_gw = pull.remote_address.to_string();
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
private:
|
||||
void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionList::Ptr remove_cmds_bypass_gw = new ActionList();
|
||||
ActionListReversed::Ptr remove_cmds = new ActionListReversed();
|
||||
|
||||
std::string connected_gw;
|
||||
|
||||
std::string tun_iface_name; // used to skip tun-based default gw when add bypass route
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
@@ -0,0 +1,379 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun interface for Mac OS X
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_CLIENT_TUNCLI_H
|
||||
#define OPENVPN_TUN_MAC_CLIENT_TUNCLI_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/asio/scoped_asio_stream.hpp>
|
||||
#include <openvpn/common/cleanup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/persist/tunwrapasio.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/mac/client/tunsetup.hpp>
|
||||
|
||||
#ifdef TEST_EER // test emulated exclude routes
|
||||
#include <openvpn/client/cliemuexr.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunMac {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_mac_error);
|
||||
|
||||
// struct used to pass received tun packets
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
// tun interface wrapper for Mac OS X
|
||||
template <typename ReadHandler, typename TunPersist>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, TunWrapAsioStream<TunPersist> >
|
||||
{
|
||||
typedef TunIO<ReadHandler, PacketFrom, TunWrapAsioStream<TunPersist> > Base;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
Tun(const typename TunPersist::Ptr& tun_persist,
|
||||
const std::string& name,
|
||||
const bool retain_stream,
|
||||
const bool tun_prefix,
|
||||
ReadHandler read_handler,
|
||||
const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats)
|
||||
: Base(read_handler, frame, stats)
|
||||
{
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = retain_stream;
|
||||
Base::tun_prefix = tun_prefix;
|
||||
Base::stream = new TunWrapAsioStream<TunPersist>(tun_persist);
|
||||
}
|
||||
};
|
||||
|
||||
// These types manage the underlying tun driver fd
|
||||
typedef openvpn_io::posix::stream_descriptor TUNStream;
|
||||
typedef ScopedAsioStream<TUNStream> ScopedTUNStream;
|
||||
typedef TunPersistTemplate<ScopedTUNStream> TunPersist;
|
||||
|
||||
class Client;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
int n_parallel = 8; // number of parallel async reads on tun socket
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
TunPersist::Ptr tun_persist;
|
||||
|
||||
Stop* stop = nullptr;
|
||||
|
||||
TunBuilderSetup::Factory::Ptr tun_setup_factory;
|
||||
|
||||
TunBuilderSetup::Base::Ptr new_setup_obj()
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TunMac::Setup();
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
|
||||
// return true if layer 2 tunnels are supported
|
||||
virtual bool layer_2_supported() const
|
||||
{
|
||||
# if defined(MAC_TUNTAP_FALLBACK)
|
||||
return false; // change to true after TAP support is added
|
||||
# else
|
||||
return false; // utun device doesn't support TAP
|
||||
# endif
|
||||
}
|
||||
|
||||
// called just prior to transmission of Disconnect event
|
||||
virtual void finalize(const bool disconnected)
|
||||
{
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
}
|
||||
};
|
||||
|
||||
class Client : public TunClient
|
||||
{
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend class TunIO<Client*, PacketFrom, TunWrapAsioStream<TunPersist> >; // calls tun_read_handler
|
||||
|
||||
typedef Tun<Client*, TunPersist> TunImpl;
|
||||
|
||||
public:
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
if (config->tun_persist)
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: long-term session scope");
|
||||
tun_persist = config->tun_persist;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: short-term connection scope");
|
||||
tun_persist.reset(new TunPersist(false, false, NULL));
|
||||
}
|
||||
|
||||
try {
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state();
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: new tun context");
|
||||
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// close old tun handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// emulated exclude routes
|
||||
EmulateExcludeRouteFactory::Ptr eer_factory;
|
||||
#ifdef TEST_EER
|
||||
eer_factory.reset(new EmulateExcludeRouteFactoryImpl(true));
|
||||
#endif
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
eer_factory.get(),
|
||||
false);
|
||||
|
||||
// handle MTU default
|
||||
if (!po->mtu)
|
||||
po->mtu = 1500;
|
||||
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
Setup::Config tsconf;
|
||||
tsconf.iface_name = state->iface_name;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
|
||||
// open/config tun
|
||||
int fd = -1;
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
fd = tun_setup->establish(*po, &tsconf, config->stop, os);
|
||||
}
|
||||
|
||||
// create ASIO wrapper for tun fd
|
||||
TUNStream* ts = new TUNStream(io_context, fd);
|
||||
|
||||
// persist tun settings state
|
||||
state->iface_name = tsconf.iface_name;
|
||||
state->tun_prefix = tsconf.tun_prefix;
|
||||
if (tun_persist->persist_tun_state(ts, state))
|
||||
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
}
|
||||
|
||||
// configure tun interface packet forwarding
|
||||
impl.reset(new TunImpl(tun_persist,
|
||||
state->iface_name,
|
||||
true,
|
||||
state->tun_prefix,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats
|
||||
));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
// signal that we are connected
|
||||
parent.tun_connected();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->close();
|
||||
stop();
|
||||
Error::Type err = Error::TUN_SETUP_FAILED;
|
||||
const ExceptionCode *ec = dynamic_cast<const ExceptionCode *>(&e);
|
||||
if (ec && ec->code_defined())
|
||||
err = ec->code();
|
||||
parent.tun_error(err, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const override
|
||||
{
|
||||
if (impl)
|
||||
return impl->name();
|
||||
else
|
||||
return "UNDEF_TUN";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
halt(false),
|
||||
state(new TunProp::State())
|
||||
{
|
||||
}
|
||||
|
||||
bool send(Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
return impl->write(buf);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
}
|
||||
|
||||
void tun_error_handler(const Error::Type errtype, // called by TunImpl
|
||||
const openvpn_io::error_code* error)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "TUN I/O error");
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // contains the tun device fd
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
bool halt;
|
||||
TunProp::State::Ptr state;
|
||||
TunBuilderSetup::Base::Ptr tun_setup;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli)
|
||||
{
|
||||
return TunClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,507 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun setup for Mac
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_CLIENT_TUNSETUP_H
|
||||
#define OPENVPN_TUN_MAC_CLIENT_TUNSETUP_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/arraysize.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/jsonlib.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
#include <openvpn/tun/mac/tunutil.hpp>
|
||||
#include <openvpn/tun/mac/utun.hpp>
|
||||
#include <openvpn/tun/mac/macgw.hpp>
|
||||
#include <openvpn/tun/mac/macdns_watchdog.hpp>
|
||||
#include <openvpn/tun/proxy.hpp>
|
||||
#include <openvpn/tun/mac/macproxy.hpp>
|
||||
#include <openvpn/tun/builder/rgwflags.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
#include <openvpn/common/jsonhelper.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunMac {
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(tun_mac_setup);
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
bool tun_prefix = false;
|
||||
bool add_bypass_routes_on_establish = false;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["tun_prefix"] = Json::Value(tun_prefix);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_bool(root, tun_prefix, "tun_prefix", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// not yet implemented
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_mac_setup("missing config");
|
||||
|
||||
// close out old remove cmds, if they exist
|
||||
destroy(os);
|
||||
|
||||
// Open tun device. Try Mac OS X integrated utun device first
|
||||
// (layer 3 only). If utun fails and MAC_TUNTAP_FALLBACK is defined,
|
||||
// then fall back to TunTap third-party device.
|
||||
// If successful, conf->iface_name will be set to tun iface name.
|
||||
int fd = -1;
|
||||
conf->tun_prefix = false;
|
||||
try {
|
||||
# if defined(MAC_TUNTAP_FALLBACK)
|
||||
# if !defined(ASIO_DISABLE_KQUEUE)
|
||||
# error Mac OS X TunTap adapter is incompatible with kqueue; rebuild with ASIO_DISABLE_KQUEUE
|
||||
# endif
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
{
|
||||
try {
|
||||
fd = UTun::utun_open(conf->iface_name);
|
||||
conf->tun_prefix = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
os << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
if (fd == -1)
|
||||
fd = Util::tuntap_open(conf->layer, conf->iface_name);
|
||||
# else
|
||||
fd = UTun::utun_open(conf->iface_name);
|
||||
conf->tun_prefix = true;
|
||||
# endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw ErrorCode(Error::TUN_IFACE_CREATE, true, e.what());
|
||||
}
|
||||
|
||||
// create ActionLists for setting up and removing adapter properties
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
remove_cmds.reset(new ActionList());
|
||||
|
||||
// populate add/remove lists with actions
|
||||
tun_config(conf->iface_name, pull, *add_cmds, *remove_cmds, os);
|
||||
|
||||
// execute the add actions
|
||||
add_cmds->execute(os);
|
||||
|
||||
// now that the add actions have succeeded,
|
||||
// enable the remove actions
|
||||
remove_cmds->enable_destroy(true);
|
||||
|
||||
os << "open " << conf->iface_name << " SUCCEEDED" << std::endl;
|
||||
return fd;
|
||||
}
|
||||
|
||||
virtual void destroy(std::ostream& os) override // defined by DestructorBase
|
||||
{
|
||||
if (remove_cmds)
|
||||
{
|
||||
remove_cmds->destroy(os);
|
||||
remove_cmds.reset();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Setup()
|
||||
{
|
||||
std::ostringstream os;
|
||||
destroy(os);
|
||||
}
|
||||
|
||||
private:
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_IFACE=(1<<1),
|
||||
R_IFACE_HINT=(1<<2),
|
||||
R_ONLINK=(1<<3),
|
||||
R_REJECT=(1<<4),
|
||||
R_BLACKHOLE=(1<<5),
|
||||
};
|
||||
|
||||
static void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& iface,
|
||||
const unsigned int flags,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back("-net");
|
||||
add->argv.push_back("-inet6");
|
||||
add->argv.push_back(net.to_string());
|
||||
add->argv.push_back("-prefixlen");
|
||||
add->argv.push_back(to_string(prefix_len));
|
||||
if (flags & R_REJECT)
|
||||
add->argv.push_back("-reject");
|
||||
if (flags & R_BLACKHOLE)
|
||||
add->argv.push_back("-blackhole");
|
||||
if (!iface.empty())
|
||||
{
|
||||
if (flags & R_IFACE)
|
||||
{
|
||||
add->argv.push_back("-iface");
|
||||
add->argv.push_back(iface);
|
||||
}
|
||||
}
|
||||
if (!gateway_str.empty() && !(flags & R_IFACE))
|
||||
{
|
||||
std::string g = gateway_str;
|
||||
if (flags & R_IFACE_HINT)
|
||||
g += '%' + iface;
|
||||
add->argv.push_back(g);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[1] = "delete";
|
||||
destroy = del;
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/route");
|
||||
add->argv.push_back("add");
|
||||
if (flags & R_ONLINK)
|
||||
{
|
||||
add->argv.push_back("-cloning");
|
||||
add->argv.push_back("-net");
|
||||
add->argv.push_back(net.to_string());
|
||||
add->argv.push_back("-netmask");
|
||||
add->argv.push_back(netmask.to_string());
|
||||
add->argv.push_back("-interface");
|
||||
add->argv.push_back(iface);
|
||||
}
|
||||
else
|
||||
{
|
||||
add->argv.push_back("-net");
|
||||
add->argv.push_back(net.to_string());
|
||||
add->argv.push_back("-netmask");
|
||||
add->argv.push_back(netmask.to_string());
|
||||
if (flags & R_REJECT)
|
||||
add->argv.push_back("-reject");
|
||||
if (flags & R_BLACKHOLE)
|
||||
add->argv.push_back("-blackhole");
|
||||
if (!iface.empty())
|
||||
{
|
||||
if (flags & R_IFACE)
|
||||
{
|
||||
add->argv.push_back("-iface");
|
||||
add->argv.push_back(iface);
|
||||
}
|
||||
}
|
||||
add->argv.push_back(gateway_str);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[1] = "delete";
|
||||
destroy = del;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& iface,
|
||||
const unsigned int flags,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, iface, flags, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
static void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
{
|
||||
// get default gateway
|
||||
MacGWInfo gw;
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = nullptr;
|
||||
const TunBuilderCapture::RouteAddress* local6 = nullptr;
|
||||
if (pull.tunnel_address_index_ipv4 >= 0)
|
||||
local4 = &pull.tunnel_addresses[pull.tunnel_address_index_ipv4];
|
||||
if (pull.tunnel_address_index_ipv6 >= 0)
|
||||
local6 = &pull.tunnel_addresses[pull.tunnel_address_index_ipv6];
|
||||
|
||||
// Interface down
|
||||
Command::Ptr iface_down(new Command);
|
||||
iface_down->argv.push_back("/sbin/ifconfig");
|
||||
iface_down->argv.push_back(iface_name);
|
||||
iface_down->argv.push_back("down");
|
||||
create.add(iface_down);
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
// Process ifconfig
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length);
|
||||
{
|
||||
Command::Ptr cmd(new Command);
|
||||
cmd->argv.push_back("/sbin/ifconfig");
|
||||
cmd->argv.push_back(iface_name);
|
||||
cmd->argv.push_back(local4->address);
|
||||
cmd->argv.push_back(local4->gateway);
|
||||
cmd->argv.push_back("netmask");
|
||||
cmd->argv.push_back(netmask.to_string());
|
||||
cmd->argv.push_back("mtu");
|
||||
cmd->argv.push_back(to_string(pull.mtu));
|
||||
cmd->argv.push_back("up");
|
||||
create.add(cmd);
|
||||
}
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, 0, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
{
|
||||
Command::Ptr cmd(new Command);
|
||||
cmd->argv.push_back("/sbin/ifconfig");
|
||||
cmd->argv.push_back(iface_name);
|
||||
cmd->argv.push_back("inet6");
|
||||
cmd->argv.push_back(local6->address + '/' + to_string(local6->prefix_length));
|
||||
cmd->argv.push_back("up");
|
||||
create.add(cmd);
|
||||
}
|
||||
add_del_route(local6->address, local6->prefix_length, "", iface_name, R_IPv6|R_IFACE, create, destroy);
|
||||
}
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (std::vector<TunBuilderCapture::Route>::const_iterator i = pull.add_routes.begin(); i != pull.add_routes.end(); ++i)
|
||||
{
|
||||
const TunBuilderCapture::Route& route = *i;
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_IPv6|R_IFACE, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, 0, create, destroy);
|
||||
else
|
||||
os << "ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
for (std::vector<TunBuilderCapture::Route>::const_iterator i = pull.exclude_routes.begin(); i != pull.exclude_routes.end(); ++i)
|
||||
{
|
||||
const TunBuilderCapture::Route& route = *i;
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
{
|
||||
if (gw.v6.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v6.router.to_string(), gw.v6.iface, R_IPv6|R_IFACE_HINT, create, destroy);
|
||||
else
|
||||
os << "NOTE: cannot determine gateway for exclude IPv6 routes" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.router.to_string(), gw.v4.iface, 0, create, destroy);
|
||||
else
|
||||
os << "NOTE: cannot determine gateway for exclude IPv4 routes" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add server bypass route
|
||||
if (gw.v4.defined())
|
||||
{
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.router.to_string(), gw.v4.iface, 0, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
//add_del_route(gw.v4.router.to_string(), 32, "", gw.v4.iface, R_ONLINK, create, destroy); // fixme -- needed for block-local
|
||||
}
|
||||
}
|
||||
else
|
||||
os << "ERROR: cannot detect IPv4 default gateway" << std::endl;
|
||||
|
||||
if (!(pull.reroute_gw.flags & RGWFlags::EmulateExcludeRoutes))
|
||||
{
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, 0, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, 0, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add server bypass route
|
||||
if (gw.v6.defined())
|
||||
{
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.router.to_string(), gw.v6.iface, R_IPv6|R_IFACE_HINT, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
//add_del_route(gw.v6.router.to_string(), 128, "", gw.v6.iface, R_IPv6|R_ONLINK, create, destroy); // fixme -- needed for block-local
|
||||
}
|
||||
}
|
||||
else
|
||||
os << "ERROR: cannot detect IPv6 default gateway" << std::endl;
|
||||
|
||||
if (!(pull.reroute_gw.flags & RGWFlags::EmulateExcludeRoutes))
|
||||
{
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_IPv6|R_IFACE, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_IPv6|R_IFACE, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
// Process block-ipv6
|
||||
if (pull.block_ipv6)
|
||||
{
|
||||
add_del_route("2000::", 4, "::1", "lo0", R_IPv6|R_REJECT|R_IFACE_HINT, create, destroy);
|
||||
add_del_route("3000::", 4, "::1", "lo0", R_IPv6|R_REJECT|R_IFACE_HINT, create, destroy);
|
||||
add_del_route("fc00::", 7, "::1", "lo0", R_IPv6|R_REJECT|R_IFACE_HINT, create, destroy);
|
||||
}
|
||||
|
||||
// Interface down
|
||||
destroy.add(iface_down);
|
||||
|
||||
// configure DNS
|
||||
{
|
||||
MacDNS::Config::Ptr dns(new MacDNS::Config(pull));
|
||||
MacDNSWatchdog::add_actions(dns,
|
||||
MacDNSWatchdog::FLUSH_RECONFIG
|
||||
#ifdef ENABLE_DNS_WATCHDOG
|
||||
| MacDNSWatchdog::SYNCHRONOUS
|
||||
| MacDNSWatchdog::ENABLE_WATCHDOG
|
||||
#endif
|
||||
,
|
||||
create,
|
||||
destroy);
|
||||
}
|
||||
|
||||
if (pull.proxy_auto_config_url.defined())
|
||||
ProxySettings::add_actions<MacProxySettings>(pull, create, destroy);
|
||||
}
|
||||
|
||||
ActionList::Ptr remove_cmds;
|
||||
|
||||
public:
|
||||
static void add_bypass_route(const std::string& route,
|
||||
bool ipv6,
|
||||
ActionList& add_cmds,
|
||||
ActionList& remove_cmds_bypass_gw)
|
||||
{
|
||||
MacGWInfo gw;
|
||||
|
||||
if (!ipv6)
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route, 32, gw.v4.router.to_string(), gw.v4.iface, 0, add_cmds, remove_cmds_bypass_gw);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v6.defined())
|
||||
add_del_route(route, 128, gw.v6.router.to_string(), gw.v6.iface, R_IPv6|R_IFACE_HINT, add_cmds, remove_cmds_bypass_gw);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,184 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace openvpn {
|
||||
class DSDict {
|
||||
public:
|
||||
OPENVPN_EXCEPTION(dsdict_error);
|
||||
|
||||
DSDict(CF::DynamicStore& sc_arg, const std::string& sname_arg, const std::string& dskey_arg)
|
||||
: sc(sc_arg),
|
||||
sname(sname_arg),
|
||||
dskey(dskey_arg),
|
||||
dict(CF::DynamicStoreCopyDict(sc_arg, dskey)) { }
|
||||
|
||||
bool dirty() const
|
||||
{
|
||||
return mod.defined() ? !CFEqual(dict(), mod()) : false;
|
||||
}
|
||||
|
||||
bool push_to_store()
|
||||
{
|
||||
if (dirty())
|
||||
{
|
||||
const CF::String keystr = CF::string(dskey);
|
||||
if (SCDynamicStoreSetValue(sc(), keystr(), mod()))
|
||||
{
|
||||
OPENVPN_LOG("DSDict: updated " << dskey);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
OPENVPN_LOG("DSDict: ERROR updating " << dskey);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool remove_from_store()
|
||||
{
|
||||
if (dirty())
|
||||
throw dsdict_error("internal error: remove_from_store called on modified dict");
|
||||
const CF::String keystr = CF::string(dskey);
|
||||
if (SCDynamicStoreRemoveValue(sc(), keystr()))
|
||||
{
|
||||
OPENVPN_LOG("DSDict: removed " << dskey);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("DSDict: ERROR removing " << dskey);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void will_modify()
|
||||
{
|
||||
if (!mod.defined())
|
||||
mod = CF::mutable_dict_copy(dict);
|
||||
}
|
||||
|
||||
void mod_reset()
|
||||
{
|
||||
mod = CF::mutable_dict();
|
||||
}
|
||||
|
||||
void backup_orig(const std::string& key, const bool wipe_orig=true)
|
||||
{
|
||||
const CF::String k = CF::string(key);
|
||||
const CF::String orig = orig_key(key);
|
||||
if (!CFDictionaryContainsKey(dict(), orig()))
|
||||
{
|
||||
const CF::String delval = delete_value();
|
||||
CFTypeRef v = CFDictionaryGetValue(dict(), k());
|
||||
if (!v)
|
||||
v = delval();
|
||||
will_modify();
|
||||
CFDictionarySetValue(mod(), orig(), v);
|
||||
}
|
||||
if (wipe_orig)
|
||||
{
|
||||
will_modify();
|
||||
CFDictionaryRemoveValue(mod(), k());
|
||||
}
|
||||
}
|
||||
|
||||
void restore_orig()
|
||||
{
|
||||
const CFIndex size = CFDictionaryGetCount(dict());
|
||||
std::unique_ptr<const void *[]> keys(new const void *[size]);
|
||||
std::unique_ptr<const void *[]> values(new const void *[size]);
|
||||
CFDictionaryGetKeysAndValues(dict(), keys.get(), values.get());
|
||||
const CF::String orig_prefix = orig_key("");
|
||||
const CFIndex orig_prefix_len = CFStringGetLength(orig_prefix());
|
||||
const CF::String delval = delete_value();
|
||||
for (CFIndex i = 0; i < size; ++i)
|
||||
{
|
||||
const CF::String key = CF::string_cast(keys[i]);
|
||||
if (CFStringHasPrefix(key(), orig_prefix()))
|
||||
{
|
||||
const CFIndex key_len = CFStringGetLength(key());
|
||||
if (key_len > orig_prefix_len)
|
||||
{
|
||||
const CFRange r = CFRangeMake(orig_prefix_len, key_len - orig_prefix_len);
|
||||
const CF::String k(CFStringCreateWithSubstring(kCFAllocatorDefault, key(), r));
|
||||
const CFTypeRef v = values[i];
|
||||
const CF::String vstr = CF::string_cast(v);
|
||||
will_modify();
|
||||
if (vstr.defined() && CFStringCompare(vstr(), delval(), 0) == kCFCompareEqualTo)
|
||||
CFDictionaryRemoveValue(mod(), k());
|
||||
else
|
||||
CFDictionaryReplaceValue(mod(), k(), v);
|
||||
CFDictionaryRemoveValue(mod(), key());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "*** DSDict " << dskey << std::endl;
|
||||
std::string orig = CF::description(dict());
|
||||
string::trim_crlf(orig);
|
||||
os << "ORIG " << orig << std::endl;
|
||||
if (dirty())
|
||||
{
|
||||
std::string modstr = CF::description(mod());
|
||||
string::trim_crlf(modstr);
|
||||
os << "MODIFIED " << modstr << std::endl;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static CF::DynamicStore ds_create(const std::string& sname)
|
||||
{
|
||||
CF::String sn = CF::string(sname);
|
||||
return CF::DynamicStore(SCDynamicStoreCreate(kCFAllocatorDefault, sn(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
static bool signal_network_reconfiguration(const std::string& sname)
|
||||
{
|
||||
const char *key = "Setup:/Network/Global/IPv4";
|
||||
CF::DynamicStore sc = ds_create(sname);
|
||||
const CF::String cfkey = CF::string(key);
|
||||
OPENVPN_LOG("DSDict: SCDynamicStoreNotifyValue " << key);
|
||||
return bool(SCDynamicStoreNotifyValue(sc(), cfkey()));
|
||||
}
|
||||
|
||||
CF::DynamicStore sc;
|
||||
const std::string sname;
|
||||
const std::string dskey;
|
||||
const CF::Dict dict;
|
||||
CF::MutableDict mod;
|
||||
|
||||
private:
|
||||
CF::String orig_key(const std::string& key) const
|
||||
{
|
||||
return CF::string(sname + "Orig" + key);
|
||||
}
|
||||
|
||||
CF::String delete_value() const
|
||||
{
|
||||
return CF::string(sname + "DeleteValue");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Get IPv4 gateway info on Mac OS X.
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_GWV4_H
|
||||
#define OPENVPN_TUN_MAC_GWV4_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm> // for std::max
|
||||
#include <cstdint> // for std::uint32_t
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/addrpair.hpp>
|
||||
#include <openvpn/addr/macaddr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class MacGatewayInfoV4
|
||||
{
|
||||
struct rtmsg {
|
||||
struct rt_msghdr m_rtm;
|
||||
char m_space[512];
|
||||
};
|
||||
|
||||
# define OPENVPN_ROUNDUP(a) \
|
||||
((a) > 0 ? (1 + (((a) - 1) | (sizeof(std::uint32_t) - 1))) : sizeof(std::uint32_t))
|
||||
|
||||
# define OPENVPN_NEXTADDR(w, u) \
|
||||
if (rtm_addrs & (w)) { \
|
||||
l = OPENVPN_ROUNDUP(u.sa_len); \
|
||||
std::memmove(cp, &(u), l); \
|
||||
cp += l; \
|
||||
}
|
||||
|
||||
# define OPENVPN_ADVANCE(x, n) \
|
||||
(x += OPENVPN_ROUNDUP((n)->sa_len))
|
||||
|
||||
public:
|
||||
OPENVPN_EXCEPTION(route_gateway_error);
|
||||
|
||||
enum {
|
||||
ADDR_DEFINED = (1<<0), /* set if gateway.addr defined */
|
||||
NETMASK_DEFINED = (1<<1), /* set if gateway.netmask defined */
|
||||
HWADDR_DEFINED = (1<<2), /* set if hwaddr is defined */
|
||||
IFACE_DEFINED = (1<<3), /* set if iface is defined */
|
||||
};
|
||||
|
||||
MacGatewayInfoV4()
|
||||
: flags_(0)
|
||||
{
|
||||
struct rtmsg m_rtmsg;
|
||||
ScopedFD sockfd;
|
||||
int seq, l, pid, rtm_addrs, i;
|
||||
struct sockaddr so_dst, so_mask;
|
||||
char *cp = m_rtmsg.m_space;
|
||||
struct sockaddr *gate = nullptr, *ifp = nullptr, *sa;
|
||||
struct rt_msghdr *rtm_aux;
|
||||
|
||||
/* setup data to send to routing socket */
|
||||
pid = ::getpid();
|
||||
seq = 0;
|
||||
rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
|
||||
|
||||
std::memset(&m_rtmsg, 0, sizeof(m_rtmsg));
|
||||
std::memset(&so_dst, 0, sizeof(so_dst));
|
||||
std::memset(&so_mask, 0, sizeof(so_mask));
|
||||
std::memset(&m_rtmsg.m_rtm, 0, sizeof(struct rt_msghdr));
|
||||
|
||||
m_rtmsg.m_rtm.rtm_type = RTM_GET;
|
||||
m_rtmsg.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
|
||||
m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
|
||||
m_rtmsg.m_rtm.rtm_seq = ++seq;
|
||||
m_rtmsg.m_rtm.rtm_addrs = rtm_addrs;
|
||||
|
||||
so_dst.sa_family = AF_INET;
|
||||
so_dst.sa_len = sizeof(struct sockaddr_in);
|
||||
so_mask.sa_family = AF_INET;
|
||||
so_mask.sa_len = sizeof(struct sockaddr_in);
|
||||
|
||||
OPENVPN_NEXTADDR(RTA_DST, so_dst);
|
||||
OPENVPN_NEXTADDR(RTA_NETMASK, so_mask);
|
||||
|
||||
m_rtmsg.m_rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
|
||||
|
||||
/* transact with routing socket */
|
||||
sockfd.reset(socket(PF_ROUTE, SOCK_RAW, 0));
|
||||
if (!sockfd.defined())
|
||||
throw route_gateway_error("GDG: socket #1 failed");
|
||||
if (::write(sockfd(), (char *)&m_rtmsg, l) < 0)
|
||||
throw route_gateway_error("GDG: problem writing to routing socket");
|
||||
do {
|
||||
l = ::read(sockfd(), (char *)&m_rtmsg, sizeof(m_rtmsg));
|
||||
} while (l > 0 && (m_rtmsg.m_rtm.rtm_seq != seq || m_rtmsg.m_rtm.rtm_pid != pid));
|
||||
sockfd.close();
|
||||
|
||||
/* extract return data from routing socket */
|
||||
rtm_aux = &m_rtmsg.m_rtm;
|
||||
cp = ((char *)(rtm_aux + 1));
|
||||
if (rtm_aux->rtm_addrs)
|
||||
{
|
||||
for (i = 1; i; i <<= 1)
|
||||
{
|
||||
if (i & rtm_aux->rtm_addrs)
|
||||
{
|
||||
sa = (struct sockaddr *)cp;
|
||||
if (i == RTA_GATEWAY )
|
||||
gate = sa;
|
||||
else if (i == RTA_IFP)
|
||||
ifp = sa;
|
||||
OPENVPN_ADVANCE(cp, sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
/* get gateway addr and interface name */
|
||||
if (gate != nullptr )
|
||||
{
|
||||
/* get default gateway addr */
|
||||
gateway_.addr.reset_ipv4_from_uint32(ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr));
|
||||
if (!gateway_.addr.unspecified())
|
||||
flags_ |= ADDR_DEFINED;
|
||||
|
||||
if (ifp)
|
||||
{
|
||||
/* get interface name */
|
||||
const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
|
||||
const size_t len = adl->sdl_nlen;
|
||||
if (len && len < sizeof(iface_))
|
||||
{
|
||||
std::memcpy (iface_, adl->sdl_data, len);
|
||||
iface_[len] = '\0';
|
||||
flags_ |= IFACE_DEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get netmask of interface that owns default gateway */
|
||||
if (flags_ & IFACE_DEFINED) {
|
||||
struct ifreq ifr;
|
||||
|
||||
sockfd.reset(socket(AF_INET, SOCK_DGRAM, 0));
|
||||
if (!sockfd.defined())
|
||||
throw route_gateway_error("GDG: socket #2 failed");
|
||||
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
string::strncpynt(ifr.ifr_name, iface_, IFNAMSIZ);
|
||||
|
||||
if (::ioctl(sockfd(), SIOCGIFNETMASK, (char *)&ifr) < 0)
|
||||
throw route_gateway_error("GDG: ioctl #1 failed");
|
||||
sockfd.close();
|
||||
|
||||
gateway_.netmask.reset_ipv4_from_uint32(ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr));
|
||||
flags_ |= NETMASK_DEFINED;
|
||||
}
|
||||
|
||||
/* try to read MAC addr associated with interface that owns default gateway */
|
||||
if (flags_ & IFACE_DEFINED)
|
||||
{
|
||||
struct ifconf ifc;
|
||||
struct ifreq *ifr;
|
||||
const int bufsize = 4096;
|
||||
|
||||
std::unique_ptr<char[]> buffer(new char[bufsize]);
|
||||
std::memset(buffer.get(), 0, bufsize);
|
||||
sockfd.reset(socket(AF_INET, SOCK_DGRAM, 0));
|
||||
if (!sockfd.defined())
|
||||
throw route_gateway_error("GDG: socket #3 failed");
|
||||
|
||||
ifc.ifc_len = bufsize;
|
||||
ifc.ifc_buf = buffer.get();
|
||||
|
||||
if (::ioctl(sockfd(), SIOCGIFCONF, (char *)&ifc) < 0)
|
||||
throw route_gateway_error("GDG: ioctl #2 failed");
|
||||
sockfd.close();
|
||||
|
||||
for (cp = buffer.get(); cp <= buffer.get() + ifc.ifc_len - sizeof(struct ifreq); )
|
||||
{
|
||||
ifr = (struct ifreq *)cp;
|
||||
const size_t len = sizeof(ifr->ifr_name) + std::max(sizeof(ifr->ifr_addr), size_t(ifr->ifr_addr.sa_len));
|
||||
if (!ifr->ifr_addr.sa_family)
|
||||
break;
|
||||
if (!::strncmp(ifr->ifr_name, iface_, IFNAMSIZ))
|
||||
{
|
||||
if (ifr->ifr_addr.sa_family == AF_LINK)
|
||||
{
|
||||
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
|
||||
hwaddr_.reset((const unsigned char *)LLADDR(sdl));
|
||||
flags_ |= HWADDR_DEFINED;
|
||||
}
|
||||
}
|
||||
cp += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
# undef OPENVPN_ROUNDUP
|
||||
# undef OPENVPN_NEXTADDR
|
||||
# undef OPENVPN_ADVANCE
|
||||
|
||||
std::string info() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "GATEWAY";
|
||||
if (flags_ & ADDR_DEFINED)
|
||||
{
|
||||
os << " ADDR=" << gateway_.addr;
|
||||
if (flags_ & NETMASK_DEFINED)
|
||||
{
|
||||
os << '/' << gateway_.netmask;
|
||||
}
|
||||
}
|
||||
if (flags_ & IFACE_DEFINED)
|
||||
os << " IFACE=" << iface_;
|
||||
if (flags_ & HWADDR_DEFINED)
|
||||
os << " HWADDR=" << hwaddr_;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
unsigned int flags() const { return flags_; }
|
||||
const IP::Addr& gateway_addr() const { return gateway_.addr; }
|
||||
std::string gateway_addr_str() const { return gateway_addr().to_string(); }
|
||||
const IP::Addr& gateway_netmask() const { return gateway_.netmask; }
|
||||
std::string gateway_netmask_str() const { return gateway_netmask().to_string(); }
|
||||
std::string iface() const { return iface_; }
|
||||
const MACAddr& hwaddr() const { return hwaddr_; }
|
||||
|
||||
bool iface_addr_defined() const
|
||||
{
|
||||
return (flags_ & (ADDR_DEFINED|IFACE_DEFINED)) == (ADDR_DEFINED|IFACE_DEFINED);
|
||||
}
|
||||
|
||||
bool hwaddr_defined() const
|
||||
{
|
||||
return flags_ & HWADDR_DEFINED;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int flags_;
|
||||
IP::AddrMaskPair gateway_;
|
||||
char iface_[16];
|
||||
MACAddr hwaddr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,374 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// DNS utilities for Mac OS X.
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_MACDNS_H
|
||||
#define OPENVPN_TUN_MAC_MACDNS_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/apple/macver.hpp>
|
||||
#include <openvpn/apple/scdynstore.hpp>
|
||||
#include <openvpn/apple/cf/cfhelper.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/mac/dsdict.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class MacDNS : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
class Info;
|
||||
|
||||
public:
|
||||
typedef RCPtr<MacDNS> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(macdns_error);
|
||||
|
||||
class Config : public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Config> Ptr;
|
||||
|
||||
Config()
|
||||
{
|
||||
}
|
||||
|
||||
Config(const TunBuilderCapture& settings)
|
||||
: dns_servers(get_dns_servers(settings)),
|
||||
search_domains(get_search_domains(settings)),
|
||||
adapter_domain_suffix(settings.adapter_domain_suffix)
|
||||
{
|
||||
// We redirect DNS if either of the following is true:
|
||||
// 1. redirect-gateway (IPv4) is pushed, or
|
||||
// 2. DNS servers are pushed but no search domains are pushed
|
||||
redirect_dns = settings.reroute_gw.ipv4 || (CF::array_len(dns_servers) && !CF::array_len(search_domains));
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "RD=" << redirect_dns;
|
||||
os << " SO=" << search_order;
|
||||
os << " DNS=" << CF::array_to_string(dns_servers);
|
||||
os << " DOM=" << CF::array_to_string(search_domains);
|
||||
os << " ADS=" << adapter_domain_suffix;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool redirect_dns = false;
|
||||
int search_order = 5000;
|
||||
CF::Array dns_servers;
|
||||
CF::Array search_domains;
|
||||
std::string adapter_domain_suffix;
|
||||
|
||||
private:
|
||||
static CF::Array get_dns_servers(const TunBuilderCapture& settings)
|
||||
{
|
||||
CF::MutableArray ret(CF::mutable_array());
|
||||
for (std::vector<TunBuilderCapture::DNSServer>::const_iterator i = settings.dns_servers.begin();
|
||||
i != settings.dns_servers.end(); ++i)
|
||||
{
|
||||
const TunBuilderCapture::DNSServer& ds = *i;
|
||||
CF::array_append_str(ret, ds.address);
|
||||
}
|
||||
return CF::const_array(ret);
|
||||
}
|
||||
|
||||
static CF::Array get_search_domains(const TunBuilderCapture& settings)
|
||||
{
|
||||
CF::MutableArray ret(CF::mutable_array());
|
||||
for (std::vector<TunBuilderCapture::SearchDomain>::const_iterator i = settings.search_domains.begin();
|
||||
i != settings.search_domains.end(); ++i)
|
||||
{
|
||||
const TunBuilderCapture::SearchDomain& sd = *i;
|
||||
CF::array_append_str(ret, sd.domain);
|
||||
}
|
||||
return CF::const_array(ret);
|
||||
}
|
||||
};
|
||||
|
||||
MacDNS(const std::string& sname_arg)
|
||||
: sname(sname_arg)
|
||||
{
|
||||
}
|
||||
|
||||
void flush_cache()
|
||||
{
|
||||
const int v = ver.major();
|
||||
if (v < Mac::Version::OSX_10_6)
|
||||
OPENVPN_LOG("MacDNS: Error: No support for Mac OS X versions earlier than 10.6");
|
||||
if (v == Mac::Version::OSX_10_6 || v >= Mac::Version::OSX_10_9)
|
||||
{
|
||||
Argv args;
|
||||
args.push_back("/usr/bin/dscacheutil");
|
||||
args.push_back("-flushcache");
|
||||
OPENVPN_LOG(args.to_string());
|
||||
system_cmd(args);
|
||||
}
|
||||
if (v >= Mac::Version::OSX_10_7)
|
||||
{
|
||||
Argv args;
|
||||
args.push_back("/usr/bin/killall");
|
||||
args.push_back("-HUP");
|
||||
args.push_back("mDNSResponder");
|
||||
OPENVPN_LOG(args.to_string());
|
||||
system_cmd(args);
|
||||
}
|
||||
}
|
||||
|
||||
bool signal_network_reconfiguration()
|
||||
{
|
||||
return DSDict::signal_network_reconfiguration(sname);
|
||||
}
|
||||
|
||||
bool setdns(const Config& config)
|
||||
{
|
||||
bool mod = false;
|
||||
|
||||
try {
|
||||
CF::DynamicStore sc = ds_create();
|
||||
Info::Ptr info(new Info(sc, sname));
|
||||
|
||||
// cleanup settings applied to previous interface
|
||||
interface_change_cleanup(info.get());
|
||||
|
||||
if (config.redirect_dns)
|
||||
{
|
||||
// redirect all DNS
|
||||
info->dns.will_modify();
|
||||
|
||||
// set DNS servers
|
||||
if (CF::array_len(config.dns_servers))
|
||||
{
|
||||
info->dns.backup_orig("ServerAddresses");
|
||||
CF::dict_set_obj(info->dns.mod, "ServerAddresses", config.dns_servers());
|
||||
}
|
||||
|
||||
// set search domains
|
||||
info->dns.backup_orig("SearchDomains");
|
||||
CF::MutableArray search_domains(CF::mutable_array());
|
||||
|
||||
// add adapter_domain_suffix to SearchDomains for domain autocompletion
|
||||
if (config.adapter_domain_suffix.length() > 0)
|
||||
CF::array_append_str(search_domains, config.adapter_domain_suffix);
|
||||
|
||||
if (CF::array_len(search_domains))
|
||||
CF::dict_set_obj(info->dns.mod, "SearchDomains", search_domains());
|
||||
|
||||
// set search order
|
||||
info->dns.backup_orig("SearchOrder");
|
||||
CF::dict_set_int(info->dns.mod, "SearchOrder", config.search_order);
|
||||
|
||||
// push it
|
||||
mod |= info->dns.push_to_store();
|
||||
}
|
||||
else
|
||||
{
|
||||
// split-DNS - resolve only specific domains
|
||||
info->ovpn.mod_reset();
|
||||
if (CF::array_len(config.dns_servers) && CF::array_len(config.search_domains))
|
||||
{
|
||||
// set DNS servers
|
||||
CF::dict_set_obj(info->ovpn.mod, "ServerAddresses", config.dns_servers());
|
||||
|
||||
// DNS will be used only for those domains
|
||||
CF::dict_set_obj(info->ovpn.mod, "SupplementalMatchDomains", config.search_domains());
|
||||
|
||||
// do not use those domains in autocompletion
|
||||
CF::dict_set_int(info->ovpn.mod, "SupplementalMatchDomainsNoSearch", 1);
|
||||
}
|
||||
|
||||
// in case of split-DNS macOS uses domain suffix of network adapter,
|
||||
// not the one provided by VPN (which we put to SearchDomains)
|
||||
|
||||
// push it
|
||||
mod |= info->ovpn.push_to_store();
|
||||
}
|
||||
|
||||
if (mod)
|
||||
{
|
||||
// As a backup, save PrimaryService in private dict (if network goes down while
|
||||
// we are set, we can lose info about PrimaryService in State:/Network/Global/IPv4
|
||||
// and be unable to reset ourselves).
|
||||
const CFTypeRef ps = CF::dict_get_obj(info->ipv4.dict, "PrimaryService");
|
||||
if (ps)
|
||||
{
|
||||
info->info.mod_reset();
|
||||
CF::dict_set_obj(info->info.mod, "PrimaryService", ps);
|
||||
info->info.push_to_store();
|
||||
}
|
||||
}
|
||||
|
||||
prev = info;
|
||||
if (mod)
|
||||
OPENVPN_LOG("MacDNS: SETDNS " << ver.to_string() << std::endl << info->to_string());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNS: setdns exception: " << e.what());
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
bool resetdns()
|
||||
{
|
||||
bool mod = false;
|
||||
try {
|
||||
CF::DynamicStore sc = ds_create();
|
||||
Info::Ptr info(new Info(sc, sname));
|
||||
|
||||
// cleanup settings applied to previous interface
|
||||
interface_change_cleanup(info.get());
|
||||
|
||||
// undo primary dns changes
|
||||
mod |= reset_primary_dns(info.get());
|
||||
|
||||
// undo non-redirect-gateway changes
|
||||
if (CF::dict_len(info->ovpn.dict))
|
||||
mod |= info->ovpn.remove_from_store();
|
||||
|
||||
// remove private info dict
|
||||
if (CF::dict_len(info->info.dict))
|
||||
mod |= info->info.remove_from_store();
|
||||
|
||||
if (mod)
|
||||
OPENVPN_LOG("MacDNS: RESETDNS " << ver.to_string() << std::endl << info->to_string());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNS: resetdns exception: " << e.what());
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
CF::DynamicStore sc = ds_create();
|
||||
Info::Ptr info(new Info(sc, sname));
|
||||
return info->to_string();
|
||||
}
|
||||
|
||||
CF::Array dskey_array() const
|
||||
{
|
||||
CF::DynamicStore sc = ds_create();
|
||||
Info::Ptr info(new Info(sc, sname));
|
||||
CF::MutableArray ret(CF::mutable_array());
|
||||
CF::array_append_str(ret, info->ipv4.dskey);
|
||||
CF::array_append_str(ret, info->info.dskey);
|
||||
CF::array_append_str(ret, info->ovpn.dskey);
|
||||
CF::array_append_str(ret, info->dns.dskey);
|
||||
return CF::const_array(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
void interface_change_cleanup(Info* info)
|
||||
{
|
||||
if (info->interface_change(prev.get()))
|
||||
{
|
||||
reset_primary_dns(prev.get());
|
||||
prev.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool reset_primary_dns(Info* info)
|
||||
{
|
||||
bool mod = false;
|
||||
if (info)
|
||||
{
|
||||
#if 1
|
||||
// Restore previous DNS settings.
|
||||
// Recommended for production.
|
||||
info->dns.will_modify();
|
||||
info->dns.restore_orig();
|
||||
mod |= info->dns.push_to_store();
|
||||
#else
|
||||
// Wipe DNS settings without restore.
|
||||
// This can potentially wipe static IP/DNS settings.
|
||||
info->dns.mod_reset();
|
||||
mod |= info->dns.push_to_store();
|
||||
#endif
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
class Info : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Info> Ptr;
|
||||
|
||||
Info(CF::DynamicStore& sc, const std::string& sname)
|
||||
: ipv4(sc, sname, "State:/Network/Global/IPv4"),
|
||||
info(sc, sname, "State:/Network/Service/" + sname + "/Info"),
|
||||
ovpn(sc, sname, "State:/Network/Service/" + sname + "/DNS"),
|
||||
dns(sc, sname, primary_dns(ipv4.dict, info.dict))
|
||||
{
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << ipv4.to_string();
|
||||
os << info.to_string();
|
||||
os << ovpn.to_string();
|
||||
os << dns.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool interface_change(Info* other) const
|
||||
{
|
||||
return other && dns.dskey != other->dns.dskey;
|
||||
}
|
||||
|
||||
DSDict ipv4;
|
||||
DSDict info; // we may modify
|
||||
DSDict ovpn; // we may modify
|
||||
DSDict dns; // we may modify
|
||||
|
||||
private:
|
||||
static std::string primary_dns(const CF::Dict& ipv4, const CF::Dict& info)
|
||||
{
|
||||
std::string serv = CF::dict_get_str(ipv4, "PrimaryService");
|
||||
if (serv.empty())
|
||||
serv = CF::dict_get_str(info, "PrimaryService");
|
||||
if (serv.empty())
|
||||
throw macdns_error("no primary service");
|
||||
return "Setup:/Network/Service/" + serv + "/DNS";
|
||||
}
|
||||
};
|
||||
|
||||
CF::DynamicStore ds_create() const
|
||||
{
|
||||
return DSDict::ds_create(sname);
|
||||
}
|
||||
|
||||
const std::string sname;
|
||||
Mac::Version ver;
|
||||
Info::Ptr prev;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,291 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// DNS utilities for Mac
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_MACDNS_WATCHDOG_H
|
||||
#define OPENVPN_TUN_MAC_MACDNS_WATCHDOG_H
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include <openvpn/log/logthread.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/apple/cf/cftimer.hpp>
|
||||
#include <openvpn/apple/cf/cfrunloop.hpp>
|
||||
#include <openvpn/tun/mac/macdns.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(macdns_watchdog_error);
|
||||
|
||||
class MacDNSWatchdog : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<MacDNSWatchdog> Ptr;
|
||||
|
||||
// flags
|
||||
enum {
|
||||
ENABLE_WATCHDOG = (1<<0),
|
||||
SYNCHRONOUS = (1<<1),
|
||||
FLUSH_RECONFIG = (1<<2),
|
||||
};
|
||||
|
||||
class DNSAction : public Action
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<DNSAction> Ptr;
|
||||
|
||||
DNSAction(const MacDNSWatchdog::Ptr& parent_arg,
|
||||
const MacDNS::Config::Ptr& config_arg,
|
||||
const unsigned int flags_arg)
|
||||
: parent(parent_arg),
|
||||
config(config_arg),
|
||||
flags(flags_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os)
|
||||
{
|
||||
os << to_string() << std::endl;
|
||||
if (parent)
|
||||
parent->setdns(config, flags);
|
||||
}
|
||||
|
||||
virtual std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "MacDNSAction: FLAGS=";
|
||||
if (flags & ENABLE_WATCHDOG)
|
||||
os << 'E';
|
||||
if (flags & SYNCHRONOUS)
|
||||
os << 'S';
|
||||
if (flags & FLUSH_RECONFIG)
|
||||
os << 'F';
|
||||
if (config)
|
||||
os << ' ' << config->to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
const MacDNSWatchdog::Ptr parent;
|
||||
const MacDNS::Config::Ptr config;
|
||||
const unsigned int flags;
|
||||
};
|
||||
|
||||
MacDNSWatchdog()
|
||||
: macdns(new MacDNS("OpenVPNConnect")),
|
||||
thread(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MacDNSWatchdog()
|
||||
{
|
||||
stop_thread();
|
||||
}
|
||||
|
||||
static void add_actions(const MacDNS::Config::Ptr& dns,
|
||||
const unsigned int flags,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
MacDNSWatchdog::Ptr watchdog(new MacDNSWatchdog);
|
||||
MacDNS::Config::Ptr dns_remove;
|
||||
DNSAction::Ptr create_action(new DNSAction(watchdog, dns, flags));
|
||||
DNSAction::Ptr destroy_action(new DNSAction(watchdog, dns_remove, flags));
|
||||
create.add(create_action);
|
||||
destroy.add(destroy_action);
|
||||
}
|
||||
|
||||
private:
|
||||
bool setdns(const MacDNS::Config::Ptr& config, const unsigned int flags)
|
||||
{
|
||||
bool mod = false;
|
||||
if (config)
|
||||
{
|
||||
if ((flags & SYNCHRONOUS) || !(flags & ENABLE_WATCHDOG))
|
||||
stop_thread();
|
||||
config_ = config;
|
||||
if (flags & ENABLE_WATCHDOG)
|
||||
{
|
||||
if (!thread)
|
||||
{
|
||||
mod = macdns->setdns(*config_);
|
||||
thread = new std::thread(&MacDNSWatchdog::thread_func, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (runloop.defined())
|
||||
schedule_push_timer(0);
|
||||
else
|
||||
OPENVPN_LOG("MacDNSWatchdog::setdns: runloop undefined");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = macdns->setdns(*config_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stop_thread();
|
||||
config_.reset();
|
||||
mod = macdns->resetdns();
|
||||
}
|
||||
if (mod && (flags & FLUSH_RECONFIG))
|
||||
{
|
||||
macdns->flush_cache();
|
||||
macdns->signal_network_reconfiguration();
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const MacDNS::Config::Ptr config(config_);
|
||||
if (config)
|
||||
return config->to_string();
|
||||
else
|
||||
return std::string("UNDEF");
|
||||
}
|
||||
|
||||
void stop_thread()
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
if (runloop.defined())
|
||||
CFRunLoopStop(runloop());
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// All methods below this point called in the context of watchdog thread
|
||||
// except for schedule_push_timer which may be called from parent thread
|
||||
// as well.
|
||||
void thread_func()
|
||||
{
|
||||
runloop.reset(CFRunLoopGetCurrent(), CF::GET);
|
||||
Log::Context logctx(logwrap);
|
||||
|
||||
try {
|
||||
SCDynamicStoreContext context = {0, this, nullptr, nullptr, nullptr};
|
||||
CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
|
||||
CFSTR("OpenVPN_MacDNSWatchdog"),
|
||||
callback_static,
|
||||
&context));
|
||||
if (!ds.defined())
|
||||
throw macdns_watchdog_error("SCDynamicStoreCreate");
|
||||
const CF::Array watched_keys(macdns->dskey_array());
|
||||
if (!watched_keys.defined())
|
||||
throw macdns_watchdog_error("watched_keys is undefined");
|
||||
if (!SCDynamicStoreSetNotificationKeys(ds(),
|
||||
watched_keys(),
|
||||
nullptr))
|
||||
throw macdns_watchdog_error("SCDynamicStoreSetNotificationKeys failed");
|
||||
CF::RunLoopSource rls(SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, ds(), 0));
|
||||
if (!rls.defined())
|
||||
throw macdns_watchdog_error("SCDynamicStoreCreateRunLoopSource failed");
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls(), kCFRunLoopDefaultMode);
|
||||
|
||||
// process event loop until CFRunLoopStop is called from parent thread
|
||||
CFRunLoopRun();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNSWatchdog::thread_func exception: " << e.what());
|
||||
}
|
||||
cancel_push_timer();
|
||||
}
|
||||
|
||||
static void callback_static(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
|
||||
{
|
||||
MacDNSWatchdog *self = (MacDNSWatchdog *)arg;
|
||||
self->callback(store, changedKeys);
|
||||
}
|
||||
|
||||
void callback(SCDynamicStoreRef store, CFArrayRef changedKeys)
|
||||
{
|
||||
// DNS Watchdog delay from the time that change is detected
|
||||
// to when we forcibly revert it (seconds).
|
||||
schedule_push_timer(1);
|
||||
}
|
||||
|
||||
void schedule_push_timer(const int seconds)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(push_timer_lock);
|
||||
CFRunLoopTimerContext context = { 0, this, nullptr, nullptr, nullptr };
|
||||
cancel_push_timer_nolock();
|
||||
push_timer.reset(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + seconds, 0, 0, 0, push_timer_callback_static, &context));
|
||||
if (push_timer.defined())
|
||||
CFRunLoopAddTimer(runloop(), push_timer(), kCFRunLoopCommonModes);
|
||||
else
|
||||
OPENVPN_LOG("MacDNSWatchdog::schedule_push_timer: failed to create timer");
|
||||
}
|
||||
|
||||
void cancel_push_timer_nolock()
|
||||
{
|
||||
if (push_timer.defined())
|
||||
{
|
||||
CFRunLoopTimerInvalidate(push_timer());
|
||||
push_timer.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_push_timer()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(push_timer_lock);
|
||||
cancel_push_timer_nolock();
|
||||
}
|
||||
|
||||
static void push_timer_callback_static(CFRunLoopTimerRef timer, void *info)
|
||||
{
|
||||
MacDNSWatchdog* self = (MacDNSWatchdog*)info;
|
||||
self->push_timer_callback(timer);
|
||||
}
|
||||
|
||||
void push_timer_callback(CFRunLoopTimerRef timer)
|
||||
{
|
||||
try {
|
||||
// reset DNS settings after watcher detected modifications by third party
|
||||
const MacDNS::Config::Ptr config(config_);
|
||||
if (macdns->setdns(*config))
|
||||
OPENVPN_LOG("MacDNSWatchdog: updated DNS settings");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNSWatchdog::push_timer_callback exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MacDNS::Config::Ptr config_;
|
||||
MacDNS::Ptr macdns;
|
||||
|
||||
std::thread* thread; // watcher thread
|
||||
CF::RunLoop runloop; // run loop in watcher thread
|
||||
CF::Timer push_timer; // watcher thread timer
|
||||
std::mutex push_timer_lock;
|
||||
Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_MACGW_H
|
||||
#define OPENVPN_TUN_MAC_MACGW_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/apple/scdynstore.hpp>
|
||||
#include <openvpn/apple/cf/cfhelper.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct MacGWInfo
|
||||
{
|
||||
struct Variant
|
||||
{
|
||||
friend struct MacGWInfo;
|
||||
public:
|
||||
bool defined() const {
|
||||
return !iface.empty() && router.defined();
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return iface + '/' + router.to_string();
|
||||
}
|
||||
|
||||
std::string iface;
|
||||
IP::Addr router;
|
||||
|
||||
private:
|
||||
Variant() {}
|
||||
|
||||
Variant(const IP::Addr::Version v, const CF::DynamicStore& dstore)
|
||||
{
|
||||
const std::string key = std::string("State:/Network/Global/IP") + IP::Addr::version_string_static(v);
|
||||
const CF::Dict d(CF::DynamicStoreCopyDict(dstore, key));
|
||||
iface = CF::dict_get_str(d, "PrimaryInterface");
|
||||
const std::string addr = CF::dict_get_str(d, "Router");
|
||||
if (!addr.empty())
|
||||
router = IP::Addr::from_string(addr, "MacGWInfo::Variant", v);
|
||||
else
|
||||
router.reset();
|
||||
}
|
||||
};
|
||||
|
||||
MacGWInfo()
|
||||
{
|
||||
const CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
|
||||
CFSTR("MacGWInfo"),
|
||||
nullptr,
|
||||
nullptr));
|
||||
v4 = Variant(IP::Addr::V4, ds);
|
||||
v6 = Variant(IP::Addr::V6, ds);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return "IPv4=" + v4.to_string() + " IPv6=" + v6.to_string();
|
||||
}
|
||||
|
||||
Variant v4;
|
||||
Variant v6;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/tun/proxy.hpp>
|
||||
#include <openvpn/tun/mac/dsdict.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class MacProxySettings : public ProxySettings
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(macproxy_error);
|
||||
|
||||
typedef RCPtr<MacProxySettings> Ptr;
|
||||
|
||||
class Info : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Info> Ptr;
|
||||
|
||||
Info(CF::DynamicStore& sc, const std::string& sname)
|
||||
: ipv4(sc, sname, "State:/Network/Global/IPv4"),
|
||||
info(sc, sname, "State:/Network/Service/" + sname + "/Info"),
|
||||
proxy(sc, sname, proxies(ipv4.dict, info.dict)) { }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << ipv4.to_string();
|
||||
os << info.to_string();
|
||||
os << proxy.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
DSDict ipv4;
|
||||
DSDict info;
|
||||
DSDict proxy;
|
||||
|
||||
private:
|
||||
static std::string proxies(const CF::Dict& ipv4, const CF::Dict& info)
|
||||
{
|
||||
std::string serv = CF::dict_get_str(ipv4, "PrimaryService");
|
||||
if (serv.empty())
|
||||
serv = CF::dict_get_str(info, "PrimaryService");
|
||||
if (serv.empty())
|
||||
throw macproxy_error("no primary service");
|
||||
return "Setup:/Network/Service/" + serv + "/Proxies";
|
||||
}
|
||||
};
|
||||
|
||||
MacProxySettings(const TunBuilderCapture::ProxyAutoConfigURL& config_arg)
|
||||
: ProxySettings(config_arg) { }
|
||||
|
||||
void set_proxy(bool del) override
|
||||
{
|
||||
if (!config.defined())
|
||||
return;
|
||||
|
||||
CF::DynamicStore sc = DSDict::ds_create(sname);
|
||||
Info::Ptr info(new Info(sc, sname));
|
||||
|
||||
info->proxy.will_modify();
|
||||
|
||||
if (!del)
|
||||
{
|
||||
info->proxy.backup_orig("ProxyAutoConfigEnable");
|
||||
CF::dict_set_int(info->proxy.mod, "ProxyAutoConfigEnable", 1);
|
||||
|
||||
info->proxy.backup_orig("ProxyAutoConfigURLString");
|
||||
CF::dict_set_str(info->proxy.mod, "ProxyAutoConfigURLString", config.to_string());
|
||||
}
|
||||
else
|
||||
info->proxy.restore_orig();
|
||||
|
||||
info->proxy.push_to_store();
|
||||
|
||||
OPENVPN_LOG("MacProxy: set_proxy " << info->to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Tun interface utilities for Mac OS X.
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_TUNUTIL_H
|
||||
#define OPENVPN_TUN_MAC_TUNUTIL_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunMac {
|
||||
namespace Util {
|
||||
OPENVPN_EXCEPTION(tun_mac_util);
|
||||
|
||||
inline int tuntap_open(const Layer& layer, std::string& name)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
const char *tuntap;
|
||||
if (layer() == Layer::OSI_LAYER_3)
|
||||
tuntap = "tun";
|
||||
else if (layer() == Layer::OSI_LAYER_2)
|
||||
tuntap = "tap";
|
||||
else
|
||||
throw tun_mac_util("unknown OSI layer");
|
||||
const std::string node_str = tuntap + to_string(i);
|
||||
const std::string node_fn = "/dev/" + node_str;
|
||||
|
||||
ScopedFD fd(open(node_fn.c_str(), O_RDWR));
|
||||
if (fd.defined())
|
||||
{
|
||||
// got it
|
||||
if (fcntl(fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_mac_util("fcntl error on " + node_fn + " : " + errinfo(errno));
|
||||
|
||||
name = node_str;
|
||||
return fd.release();
|
||||
}
|
||||
}
|
||||
throw tun_mac_util(std::string("error opening Mac ") + layer.dev_type() + " device");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Thanks to Jonathan Levin for proof-of-concept utun code for Mac OS X.
|
||||
// http://newosxbook.com/src.jl?tree=listings&file=17-15-utun.c
|
||||
|
||||
// Open a utun device on Mac OS X.
|
||||
|
||||
#ifndef OPENVPN_TUN_MAC_UTUN_H
|
||||
#define OPENVPN_TUN_MAC_UTUN_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sys_domain.h>
|
||||
#include <sys/kern_control.h>
|
||||
#include <net/if_utun.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunMac {
|
||||
namespace UTun {
|
||||
OPENVPN_EXCEPTION(utun_error);
|
||||
|
||||
// Open specific utun device unit and return fd.
|
||||
// If the unit number is already in use, return -1.
|
||||
// Throw exceptions for all other errors.
|
||||
// Return the iface name in name.
|
||||
inline int utun_open(std::string& name, const int unit)
|
||||
{
|
||||
struct sockaddr_ctl sc;
|
||||
struct ctl_info ctlInfo;
|
||||
|
||||
memset(&ctlInfo, 0, sizeof(ctlInfo));
|
||||
if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name))
|
||||
>= sizeof(ctlInfo.ctl_name))
|
||||
throw utun_error("UTUN_CONTROL_NAME too long");
|
||||
|
||||
ScopedFD fd(socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL));
|
||||
if (!fd.defined())
|
||||
throw utun_error("socket(SYSPROTO_CONTROL)");
|
||||
|
||||
if (ioctl(fd(), CTLIOCGINFO, &ctlInfo) == -1)
|
||||
throw utun_error("ioctl(CTLIOCGINFO)");
|
||||
|
||||
sc.sc_id = ctlInfo.ctl_id;
|
||||
sc.sc_len = sizeof(sc);
|
||||
sc.sc_family = AF_SYSTEM;
|
||||
sc.ss_sysaddr = AF_SYS_CONTROL;
|
||||
sc.sc_unit = unit + 1;
|
||||
|
||||
// If the connect is successful, a utunX device will be created, where X
|
||||
// is our unit number - 1.
|
||||
if (connect(fd(), (struct sockaddr *)&sc, sizeof(sc)) == -1)
|
||||
return -1;
|
||||
|
||||
// Get iface name of newly created utun dev.
|
||||
char utunname[20];
|
||||
socklen_t utunname_len = sizeof(utunname);
|
||||
if (getsockopt(fd(), SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len))
|
||||
throw utun_error("getsockopt(SYSPROTO_CONTROL)");
|
||||
name = utunname;
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
// Try to open an available utun device unit.
|
||||
// Return the iface name in name.
|
||||
inline int utun_open(std::string& name)
|
||||
{
|
||||
for (int unit = 0; unit < 256; ++unit)
|
||||
{
|
||||
const int fd = utun_open(name, unit);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
}
|
||||
throw utun_error("cannot open available utun device");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_PERSIST_TUNPERSIST_H
|
||||
#define OPENVPN_TUN_PERSIST_TUNPERSIST_H
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/tun/persist/tunwrap.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// TunPersistTemplate adds persistence capabilities onto TunWrapTemplate,
|
||||
// in order to implement logic for the persist-tun directive.
|
||||
template <typename SCOPED_OBJ, typename STATE=TunProp::State::Ptr>
|
||||
class TunPersistTemplate : public TunWrapTemplate<SCOPED_OBJ>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TunPersistTemplate> Ptr;
|
||||
|
||||
TunPersistTemplate(const bool enable_persistence, const bool retain_obj, TunBuilderBase* tb)
|
||||
: TunWrapTemplate<SCOPED_OBJ>(retain_obj),
|
||||
enable_persistence_(enable_persistence),
|
||||
tb_(tb),
|
||||
use_persisted_tun_(false),
|
||||
disconnect(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Current persisted state
|
||||
const STATE& state() const
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
virtual ~TunPersistTemplate()
|
||||
{
|
||||
close_local();
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
options_.clear();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
close_local();
|
||||
TunWrapTemplate<SCOPED_OBJ>::close();
|
||||
}
|
||||
|
||||
void set_disconnect()
|
||||
{
|
||||
disconnect = true;
|
||||
}
|
||||
|
||||
// Current persisted options
|
||||
const std::string& options()
|
||||
{
|
||||
return options_;
|
||||
}
|
||||
|
||||
// Return true if we should use previously persisted
|
||||
// tun socket descriptor/handle
|
||||
bool use_persisted_tun(const IP::Addr server_addr,
|
||||
const TunProp::Config& tun_prop,
|
||||
const OptionList& opt)
|
||||
{
|
||||
#if OPENVPN_DEBUG_TUN_BUILDER > 0
|
||||
{
|
||||
TunBuilderCapture::Ptr capture = new TunBuilderCapture();
|
||||
try {
|
||||
TunProp::configure_builder(capture.get(), nullptr, nullptr, server_addr, tun_prop, opt, nullptr, true);
|
||||
OPENVPN_LOG("*** TUN BUILDER CAPTURE" << std::endl << capture->to_string());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("*** TUN BUILDER CAPTURE exception: " << e.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// In tun_persist mode, capture tun builder settings so we can
|
||||
// compare them to previous persisted settings.
|
||||
if (enable_persistence_)
|
||||
{
|
||||
copt_.reset(new TunBuilderCapture());
|
||||
try {
|
||||
TunProp::configure_builder(copt_.get(), nullptr, nullptr, server_addr, tun_prop, opt, nullptr, true);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
copt_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if previous tun session matches properties of to-be-created session
|
||||
use_persisted_tun_ = (TunWrapTemplate<SCOPED_OBJ>::obj_defined()
|
||||
&& copt_
|
||||
&& !options_.empty()
|
||||
&& options_ == copt_->to_string()
|
||||
&& (tb_ ? tb_->tun_builder_persist() : true));
|
||||
return use_persisted_tun_;
|
||||
}
|
||||
|
||||
// Possibly save tunnel fd/handle, state, and options.
|
||||
bool persist_tun_state(const typename SCOPED_OBJ::base_type obj,
|
||||
const STATE& state)
|
||||
{
|
||||
if (!enable_persistence_ || !use_persisted_tun_)
|
||||
{
|
||||
TunWrapTemplate<SCOPED_OBJ>::save_replace_sock(obj);
|
||||
}
|
||||
if (enable_persistence_ && copt_ && !use_persisted_tun_)
|
||||
{
|
||||
state_ = state;
|
||||
options_ = copt_->to_string();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void close_local()
|
||||
{
|
||||
if (tb_)
|
||||
tb_->tun_builder_teardown(disconnect);
|
||||
state_.reset();
|
||||
options_ = "";
|
||||
}
|
||||
|
||||
const bool enable_persistence_;
|
||||
TunBuilderBase * const tb_;
|
||||
STATE state_;
|
||||
std::string options_;
|
||||
|
||||
TunBuilderCapture::Ptr copt_;
|
||||
bool use_persisted_tun_;
|
||||
|
||||
bool disconnect;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_PERSIST_TUNWRAP_H
|
||||
#define OPENVPN_TUN_PERSIST_TUNWRAP_H
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/destruct.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// TunWrapTemplate is used client-side to store the underlying tun
|
||||
// interface fd/handle. SCOPED_OBJ is generally a ScopedFD (unix) or a
|
||||
// ScopedHANDLE (Windows). It can also be a ScopedAsioStream.
|
||||
template <typename SCOPED_OBJ>
|
||||
class TunWrapTemplate : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TunWrapTemplate> Ptr;
|
||||
|
||||
TunWrapTemplate(const bool retain_obj)
|
||||
: retain_obj_(retain_obj)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TunWrapTemplate()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool obj_defined() const
|
||||
{
|
||||
return obj_.defined();
|
||||
}
|
||||
|
||||
// Current persisted tun fd/handle
|
||||
typename SCOPED_OBJ::base_type obj() const
|
||||
{
|
||||
return obj_();
|
||||
}
|
||||
|
||||
bool destructor_defined() const
|
||||
{
|
||||
return bool(destruct_);
|
||||
}
|
||||
|
||||
// destruct object performs cleanup prior to TAP device
|
||||
// HANDLE close, such as removing added routes.
|
||||
void add_destructor(const DestructorBase::Ptr& destruct)
|
||||
{
|
||||
close_destructor();
|
||||
destruct_ = destruct;
|
||||
}
|
||||
|
||||
void close_destructor()
|
||||
{
|
||||
try {
|
||||
if (destruct_)
|
||||
{
|
||||
std::ostringstream os;
|
||||
destruct_->destroy(os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
destruct_.reset();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("TunWrap destructor exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (retain_obj_)
|
||||
obj_.release();
|
||||
else
|
||||
{
|
||||
close_destructor();
|
||||
obj_.close();
|
||||
}
|
||||
}
|
||||
|
||||
void save_replace_sock(const typename SCOPED_OBJ::base_type obj)
|
||||
{
|
||||
if (retain_obj_)
|
||||
obj_.replace(obj);
|
||||
else
|
||||
obj_.reset(obj);
|
||||
}
|
||||
|
||||
private:
|
||||
const bool retain_obj_;
|
||||
DestructorBase::Ptr destruct_;
|
||||
SCOPED_OBJ obj_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_PERSIST_TUNWRAPASIO_H
|
||||
#define OPENVPN_TUN_PERSIST_TUNWRAPASIO_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// This object supports that subset of the Asio stream
|
||||
// interface required by TunIO, and is intended to wrap
|
||||
// a ScopedAsioStream embedded in a TunWrap object.
|
||||
// It is used primarily on Windows to wrap the TAP
|
||||
// interface HANDLE in way that plays well with Windows
|
||||
// I/O completion ports (once a HANDLE is bound to an
|
||||
// I/O completion port it cannot be unbound).
|
||||
template <typename TunWrap>
|
||||
class TunWrapAsioStream
|
||||
{
|
||||
public:
|
||||
TunWrapAsioStream(const typename TunWrap::Ptr& tun_wrap_arg)
|
||||
: tun_wrap(tun_wrap_arg) {}
|
||||
|
||||
|
||||
void release()
|
||||
{
|
||||
tun_wrap.reset();
|
||||
}
|
||||
|
||||
// Delegate STREAM methods (only need to support the
|
||||
// subset of methods used by TunIO).
|
||||
// Prototypes from asio/windows/basic_stream_handle.hpp
|
||||
|
||||
template <typename MUTABLE_BUFFER, typename HANDLER>
|
||||
void async_read_some(const MUTABLE_BUFFER& buffers, HANDLER&& handler)
|
||||
{
|
||||
return tun_wrap->obj()->async_read_some(buffers, std::move(handler));
|
||||
}
|
||||
|
||||
template <typename CONST_BUFFER>
|
||||
std::size_t write_some(const CONST_BUFFER& buffers)
|
||||
{
|
||||
return tun_wrap->obj()->write_some(buffers);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
tun_wrap->obj()->cancel();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
tun_wrap->obj()->close();
|
||||
}
|
||||
|
||||
private:
|
||||
typename TunWrap::Ptr tun_wrap;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class ProxySettings : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(proxy_error);
|
||||
|
||||
typedef RCPtr<ProxySettings> Ptr;
|
||||
|
||||
class ProxyAction : public Action
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ProxyAction> Ptr;
|
||||
|
||||
ProxyAction(ProxySettings::Ptr parent_arg, bool del_arg)
|
||||
: parent(parent_arg), del(del_arg) { }
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
os << to_string() << std::endl;
|
||||
if (parent)
|
||||
parent->set_proxy(del);
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
if (parent && parent->config.defined())
|
||||
os << "ProxyAction: auto config: " << parent->config.to_string();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
const ProxySettings::Ptr parent;
|
||||
bool del;
|
||||
};
|
||||
|
||||
ProxySettings(const TunBuilderCapture::ProxyAutoConfigURL& config_arg)
|
||||
: config(config_arg) { }
|
||||
|
||||
virtual void set_proxy(bool del) = 0;
|
||||
|
||||
template<class T>
|
||||
static void add_actions(const TunBuilderCapture& settings,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
ProxySettings::Ptr proxy(new T(settings.proxy_auto_config_url));
|
||||
ProxyAction::Ptr create_action(new ProxyAction(proxy, false));
|
||||
ProxyAction::Ptr destroy_action(new ProxyAction(proxy, true));
|
||||
create.add(create_action);
|
||||
destroy.add(destroy_action);
|
||||
}
|
||||
|
||||
const std::string sname = "OpenVPNConnect";
|
||||
|
||||
TunBuilderCapture::ProxyAutoConfigURL config;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Abstract base classes for server tun objects
|
||||
|
||||
#ifndef OPENVPN_TUN_SERVER_TUNBASE_H
|
||||
#define OPENVPN_TUN_SERVER_TUNBASE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/function.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/server/servhalt.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunClientInstance {
|
||||
|
||||
typedef Function<void(int fd)> PostCloseFunc;
|
||||
|
||||
// A native reference to a client instance
|
||||
struct NativeHandle
|
||||
{
|
||||
NativeHandle() {}
|
||||
|
||||
NativeHandle(const int fd_arg, const int peer_id_arg)
|
||||
: fd(fd_arg),
|
||||
peer_id(peer_id_arg)
|
||||
{
|
||||
}
|
||||
|
||||
bool fd_defined() const
|
||||
{
|
||||
return fd >= 0;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return fd >= 0 && peer_id >= 0;
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
int peer_id = -1;
|
||||
};
|
||||
|
||||
// Base class for the client instance receiver. Note that all
|
||||
// client instance receivers (transport, routing, management,
|
||||
// etc.) must inherit virtually from RC because the client instance
|
||||
// object will inherit from multiple receivers.
|
||||
struct Recv : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Recv> Ptr;
|
||||
|
||||
//virtual bool defined() const = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
// Called with IP packets from tun layer.
|
||||
virtual void tun_recv(BufferAllocated& buf) = 0;
|
||||
|
||||
// push a halt or restart message to client
|
||||
virtual void push_halt_restart_msg(const HaltRestart::Type type,
|
||||
const std::string& reason,
|
||||
const bool tell_client) = 0;
|
||||
};
|
||||
|
||||
// Base class for the per-client-instance state of the TunServer.
|
||||
// Each client instance uses this class to send data to the tun layer.
|
||||
struct Send : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Send> Ptr;
|
||||
|
||||
//virtual bool defined() const = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual bool tun_send_const(const Buffer& buf) = 0;
|
||||
virtual bool tun_send(BufferAllocated& buf) = 0;
|
||||
|
||||
// get the native handle for tun/peer
|
||||
virtual NativeHandle tun_native_handle() = 0;
|
||||
|
||||
// set up relay to target
|
||||
virtual void relay(const IP::Addr& target, const int port) = 0;
|
||||
|
||||
virtual const std::string& tun_info() const = 0;
|
||||
};
|
||||
|
||||
// Factory for server tun object.
|
||||
struct Factory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Factory> Ptr;
|
||||
|
||||
virtual Send::Ptr new_obj(Recv* parent) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Low level tun device I/O class for all platforms (Unix and Windows)
|
||||
|
||||
#ifndef OPENVPN_TUN_TUNIO_H
|
||||
#define OPENVPN_TUN_TUNIO_H
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/bigmutex.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/ip/ipcommon.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/tun/tunlog.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename ReadHandler, typename PacketFrom, typename STREAM>
|
||||
class TunIO : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TunIO> Ptr;
|
||||
|
||||
TunIO(ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const size_t frame_context_type=Frame::READ_TUN)
|
||||
: stream(nullptr),
|
||||
retain_stream(false),
|
||||
tun_prefix(false),
|
||||
halt(false),
|
||||
read_handler(read_handler_arg),
|
||||
frame(frame_arg),
|
||||
frame_context((*frame_arg)[frame_context_type]),
|
||||
stats(stats_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TunIO()
|
||||
{
|
||||
//OPENVPN_LOG("**** TUNIO destruct");
|
||||
stop();
|
||||
delete stream;
|
||||
}
|
||||
|
||||
bool write(Buffer& buf)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
try {
|
||||
// handle tun packet prefix, if enabled
|
||||
if (tun_prefix)
|
||||
{
|
||||
if (buf.offset() >= 4 && buf.size() >= 1)
|
||||
{
|
||||
switch (IPCommon::version(buf[0]))
|
||||
{
|
||||
case 4:
|
||||
prepend_pf_inet(buf, PF_INET);
|
||||
break;
|
||||
case 6:
|
||||
prepend_pf_inet(buf, PF_INET6);
|
||||
break;
|
||||
default:
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write error: cannot identify IP version for prefix");
|
||||
tun_error(Error::TUN_FRAMING_ERROR, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write error: cannot write prefix");
|
||||
tun_error(Error::TUN_FRAMING_ERROR, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// write data to tun device
|
||||
const size_t wrote = stream->write_some(buf.const_buffer());
|
||||
if (stats)
|
||||
{
|
||||
stats->inc_stat(SessionStats::TUN_BYTES_OUT, wrote);
|
||||
stats->inc_stat(SessionStats::TUN_PACKETS_OUT, 1);
|
||||
}
|
||||
if (wrote == buf.size())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
|
||||
tun_error(Error::TUN_WRITE_ERROR, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class BUFSEQ>
|
||||
bool write_seq(const BUFSEQ& bs)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
try {
|
||||
// write data to tun device
|
||||
const size_t wrote = stream->write_some(bs);
|
||||
if (stats)
|
||||
{
|
||||
stats->inc_stat(SessionStats::TUN_BYTES_OUT, wrote);
|
||||
stats->inc_stat(SessionStats::TUN_PACKETS_OUT, 1);
|
||||
}
|
||||
if (wrote == bs.size())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
|
||||
tun_error(Error::TUN_WRITE_ERROR, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void start(const int n_parallel)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
for (int i = 0; i < n_parallel; i++)
|
||||
queue_read(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// must be called by derived class destructor
|
||||
void stop()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
if (stream)
|
||||
{
|
||||
stream->cancel();
|
||||
if (!retain_stream)
|
||||
{
|
||||
//OPENVPN_LOG("**** TUNIO close");
|
||||
stream->close();
|
||||
}
|
||||
else
|
||||
stream->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
private:
|
||||
void prepend_pf_inet(Buffer& buf, const std::uint32_t value)
|
||||
{
|
||||
const std::uint32_t net_value = htonl(value);
|
||||
buf.prepend((unsigned char *)&net_value, sizeof(net_value));
|
||||
}
|
||||
|
||||
protected:
|
||||
void queue_read(PacketFrom *tunfrom)
|
||||
{
|
||||
OPENVPN_LOG_TUN_VERBOSE("TunIO::queue_read");
|
||||
if (!tunfrom)
|
||||
tunfrom = new PacketFrom();
|
||||
frame_context.prepare(tunfrom->buf);
|
||||
|
||||
// queue read on tun device
|
||||
stream->async_read_some(frame_context.mutable_buffer(tunfrom->buf),
|
||||
[self=Ptr(this), tunfrom=typename PacketFrom::SPtr(tunfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->handle_read(std::move(tunfrom), error, bytes_recvd);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_read(typename PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
OPENVPN_LOG_TUN_VERBOSE("TunIO::handle_read: " << error.message());
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
pfp->buf.set_size(bytes_recvd);
|
||||
if (stats)
|
||||
{
|
||||
stats->inc_stat(SessionStats::TUN_BYTES_IN, bytes_recvd);
|
||||
stats->inc_stat(SessionStats::TUN_PACKETS_IN, 1);
|
||||
}
|
||||
if (!tun_prefix)
|
||||
{
|
||||
read_handler->tun_read_handler(pfp);
|
||||
}
|
||||
else if (pfp->buf.size() >= 4)
|
||||
{
|
||||
// handle tun packet prefix, if enabled
|
||||
pfp->buf.advance(4);
|
||||
read_handler->tun_read_handler(pfp);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN Read Error: cannot read prefix");
|
||||
tun_error(Error::TUN_READ_ERROR, nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN Read Error: " << error.message());
|
||||
tun_error(Error::TUN_READ_ERROR, &error);
|
||||
}
|
||||
if (!halt)
|
||||
queue_read(pfp.release()); // reuse buffer if still available
|
||||
}
|
||||
}
|
||||
|
||||
void tun_error(const Error::Type errtype, const openvpn_io::error_code* error)
|
||||
{
|
||||
if (stats)
|
||||
stats->error(errtype);
|
||||
read_handler->tun_error_handler(errtype, error);
|
||||
}
|
||||
|
||||
// should be set by derived class constructor
|
||||
std::string name_;
|
||||
STREAM *stream;
|
||||
bool retain_stream; // don't close tun stream
|
||||
bool tun_prefix;
|
||||
|
||||
bool halt;
|
||||
ReadHandler read_handler;
|
||||
const Frame::Ptr frame;
|
||||
const Frame::Context& frame_context;
|
||||
SessionStats::Ptr stats;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Define tun logging macros using OPENVPN_DEBUG_TUN as a verbosity level.
|
||||
|
||||
#ifndef OPENVPN_TUN_TUNLOG_H
|
||||
#define OPENVPN_TUN_TUNLOG_H
|
||||
|
||||
#if defined(OPENVPN_DEBUG_TUN) && OPENVPN_DEBUG_TUN >= 1
|
||||
#define OPENVPN_LOG_TUN_ERROR(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_TUN_ERROR(x)
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_TUN) && OPENVPN_DEBUG_TUN >= 2
|
||||
#define OPENVPN_LOG_TUN(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_TUN(x)
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_TUN) && OPENVPN_DEBUG_TUN >= 3
|
||||
#define OPENVPN_LOG_TUN_VERBOSE(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_TUN_VERBOSE(x)
|
||||
#endif
|
||||
|
||||
#endif // OPENVPN_TUN_TUNLOG_H
|
||||
@@ -0,0 +1,34 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_TUNMTU_H
|
||||
#define OPENVPN_TUN_TUNMTU_H
|
||||
|
||||
#include <openvpn/common/options.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline unsigned int parse_tun_mtu(const OptionList& opt, unsigned int default_value)
|
||||
{
|
||||
return opt.get_num<unsigned int>("tun-mtu", 1, default_value, 576, 65535);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Parse the argument of a "tun" or "tap" directive. Also parse an optional
|
||||
// "/v4" or "/v6" after the tun name to denote IPv4 or IPv6 usage.
|
||||
|
||||
#ifndef OPENVPN_TUN_TUNSPEC_H
|
||||
#define OPENVPN_TUN_TUNSPEC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/split.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct ParseTunSpec
|
||||
{
|
||||
OPENVPN_EXCEPTION(bad_tun_spec);
|
||||
|
||||
ParseTunSpec(const std::string& tun_spec)
|
||||
: ipv6(false)
|
||||
{
|
||||
std::vector<std::string> s = Split::by_char<std::vector<std::string>, NullLex, Split::NullLimit>(tun_spec, '/');
|
||||
if (s.size() == 1)
|
||||
{
|
||||
tun_name = s[0];
|
||||
}
|
||||
else if (s.size() == 2)
|
||||
{
|
||||
tun_name = s[0];
|
||||
if (s[1] == "v4")
|
||||
ipv6 = false;
|
||||
else if (s[1] == "v6")
|
||||
ipv6 = true;
|
||||
else
|
||||
throw bad_tun_spec();
|
||||
}
|
||||
else
|
||||
throw bad_tun_spec();
|
||||
}
|
||||
bool ipv6;
|
||||
std::string tun_name;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_TUNSPEC_H
|
||||
@@ -0,0 +1,99 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/asio/scoped_asio_stream.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/win/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
// These types manage the underlying TAP driver HANDLE
|
||||
typedef openvpn_io::windows::stream_handle TAPStream;
|
||||
typedef ScopedAsioStream<TAPStream> ScopedTAPStream;
|
||||
struct TunPersistState {
|
||||
TunProp::State::Ptr state;
|
||||
RingBuffer::Ptr ring_buffer;
|
||||
|
||||
void reset()
|
||||
{
|
||||
state.reset();
|
||||
ring_buffer.reset();
|
||||
}
|
||||
};
|
||||
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState> TunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
friend class Client; // accesses wfp
|
||||
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
int n_parallel = 8; // number of parallel async reads on tun socket
|
||||
bool wintun = false;
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
Stop* stop = nullptr;
|
||||
|
||||
TunPersist::Ptr tun_persist;
|
||||
|
||||
TunWin::SetupFactory::Ptr tun_setup_factory;
|
||||
|
||||
TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context)
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj(io_context, wintun);
|
||||
else
|
||||
return new TunWin::Setup(io_context, wintun);
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli) override;
|
||||
|
||||
virtual void finalize(const bool disconnected) override
|
||||
{
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
}
|
||||
|
||||
virtual bool layer_2_supported() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Client tun setup base class for Windows
|
||||
|
||||
#ifndef OPENVPN_TUN_WIN_CLIENT_SETUPBASE_H
|
||||
#define OPENVPN_TUN_WIN_CLIENT_SETUPBASE_H
|
||||
|
||||
#include <windows.h> // for HANDLE
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/destruct.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
#include <openvpn/tun/win/ringbuffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
struct SetupBase : public DestructorBase
|
||||
{
|
||||
typedef RCPtr<SetupBase> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(tun_win_setup);
|
||||
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
const std::wstring& openvpn_app_path,
|
||||
Stop* stop,
|
||||
std::ostream& os,
|
||||
RingBuffer::Ptr rings) = 0;
|
||||
|
||||
virtual bool l2_ready(const TunBuilderCapture& pull) = 0;
|
||||
|
||||
virtual void l2_finish(const TunBuilderCapture& pull,
|
||||
Stop* stop,
|
||||
std::ostream& os) = 0;
|
||||
|
||||
virtual void confirm()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void set_service_fail_handler(std::function<void()>&& handler)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SetupFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<SetupFactory> Ptr;
|
||||
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,410 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Client tun interface for Windows
|
||||
|
||||
#ifndef OPENVPN_TUN_WIN_CLIENT_TUNCLI_H
|
||||
#define OPENVPN_TUN_WIN_CLIENT_TUNCLI_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/asio/scoped_asio_stream.hpp>
|
||||
#include <openvpn/common/cleanup.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/client/dhcp_capture.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/persist/tunwrapasio.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/win/client/clientconfig.hpp>
|
||||
#include <openvpn/tun/win/client/tunsetup.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
#include <openvpn/tun/win/client/wintun.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_win_error);
|
||||
|
||||
// struct used to pass received tun packets
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
// tun interface wrapper for Windows
|
||||
template <typename ReadHandler, typename TunPersist>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, TunWrapAsioStream<TunPersist> >
|
||||
{
|
||||
typedef TunIO<ReadHandler, PacketFrom, TunWrapAsioStream<TunPersist> > Base;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
Tun(const typename TunPersist::Ptr& tun_persist,
|
||||
const std::string& name,
|
||||
const bool retain_stream,
|
||||
ReadHandler read_handler,
|
||||
const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats)
|
||||
: Base(read_handler, frame, stats, Frame::READ_TUN)
|
||||
{
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = retain_stream;
|
||||
Base::stream = new TunWrapAsioStream<TunPersist>(tun_persist);
|
||||
}
|
||||
};
|
||||
|
||||
class Client : public TunClient
|
||||
{
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend class TunIO<Client*, PacketFrom, TunWrapAsioStream<TunPersist> >; // calls tun_read_handler
|
||||
|
||||
typedef Tun<Client*, TunPersist> TunImpl;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Client> Ptr;
|
||||
|
||||
virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
if (config->tun_persist)
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
else
|
||||
tun_persist.reset(new TunPersist(false, false, nullptr)); // short-term
|
||||
|
||||
try {
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state().state;
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// close old TAP handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj(io_context);
|
||||
|
||||
// open/config TAP
|
||||
HANDLE th;
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
th = tun_setup->establish(*po, Win::module_name(), config->stop, os, NULL);
|
||||
}
|
||||
|
||||
// create ASIO wrapper for HANDLE
|
||||
TAPStream* ts = new TAPStream(io_context, th);
|
||||
|
||||
// persist tun settings state
|
||||
if (tun_persist->persist_tun_state(ts, { state, nullptr }))
|
||||
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
|
||||
|
||||
// setup handler for external tun close
|
||||
tun_setup->set_service_fail_handler([self=Ptr(this)]() {
|
||||
if (!self->halt)
|
||||
self->parent.tun_error(Error::TUN_IFACE_DISABLED, "service failure");
|
||||
});
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
|
||||
// assert ownership over TAP device handle
|
||||
tun_setup->confirm();
|
||||
|
||||
// if layer 2, set up to capture DHCP messages over the tunnel
|
||||
if (config->tun_prop.layer() == Layer::OSI_LAYER_2)
|
||||
dhcp_capture.reset(new DHCPCapture(po));
|
||||
}
|
||||
|
||||
// configure tun interface packet forwarding
|
||||
impl.reset(new TunImpl(tun_persist,
|
||||
"TUN_WIN",
|
||||
true,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
if (!dhcp_capture)
|
||||
parent.tun_connected(); // signal that we are connected
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->close();
|
||||
stop();
|
||||
Error::Type err = Error::TUN_SETUP_FAILED;
|
||||
const ExceptionCode *ec = dynamic_cast<const ExceptionCode *>(&e);
|
||||
if (ec && ec->code_defined())
|
||||
err = ec->code();
|
||||
parent.tun_error(err, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual std::string tun_name() const override
|
||||
{
|
||||
if (impl)
|
||||
return impl->name();
|
||||
else
|
||||
return "UNDEF_TUN";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual void set_disconnect() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
l2_timer(io_context_arg),
|
||||
halt(false),
|
||||
frame_context((*config_arg->frame)[Frame::READ_TUN])
|
||||
{
|
||||
}
|
||||
|
||||
bool send(Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
{
|
||||
if (dhcp_capture)
|
||||
dhcp_inspect(buf);
|
||||
|
||||
return impl->write(buf);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
#ifdef OPENVPN_DEBUG_TAPWIN
|
||||
tap_process_logging();
|
||||
#endif
|
||||
}
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
|
||||
#ifdef OPENVPN_DEBUG_TAPWIN
|
||||
tap_process_logging();
|
||||
#endif
|
||||
}
|
||||
|
||||
void tun_error_handler(const Error::Type errtype, // called by TunImpl
|
||||
const openvpn_io::error_code* error)
|
||||
{
|
||||
if (errtype == Error::TUN_READ_ERROR && error && error->value() == 995)
|
||||
parent.tun_error(Error::TUN_IFACE_DISABLED, "TAP adapter is disabled");
|
||||
else
|
||||
parent.tun_error(Error::TUN_ERROR, "TUN I/O error");
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
l2_timer.cancel();
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE tap_handle()
|
||||
{
|
||||
if (tun_persist)
|
||||
{
|
||||
TAPStream* stream = tun_persist->obj();
|
||||
if (stream)
|
||||
return stream->native_handle();
|
||||
}
|
||||
return Win::Handle::undefined();
|
||||
}
|
||||
|
||||
void tap_process_logging()
|
||||
{
|
||||
HANDLE h = tap_handle();
|
||||
if (Win::Handle::defined(h))
|
||||
Util::tap_process_logging(h);
|
||||
}
|
||||
|
||||
void dhcp_inspect(Buffer& buf)
|
||||
{
|
||||
try {
|
||||
if (dhcp_capture->mod_reply(buf))
|
||||
{
|
||||
OPENVPN_LOG("DHCP PROPS:" << std::endl << dhcp_capture->get_props().to_string());
|
||||
layer_2_schedule_timer(1);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stop();
|
||||
parent.tun_error(Error::TUN_SETUP_FAILED, std::string("L2 exception: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void layer_2_schedule_timer(const unsigned int seconds)
|
||||
{
|
||||
l2_timer.expires_after(Time::Duration::seconds(seconds));
|
||||
l2_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
if (!error && !self->halt)
|
||||
self->layer_2_timer_callback();
|
||||
});
|
||||
}
|
||||
|
||||
// Normally called once per second by l2_timer while we are waiting
|
||||
// for layer 2 DHCP handshake to complete.
|
||||
void layer_2_timer_callback()
|
||||
{
|
||||
try {
|
||||
if (dhcp_capture && tun_setup)
|
||||
{
|
||||
if (tun_setup->l2_ready(dhcp_capture->get_props()))
|
||||
{
|
||||
std::ostringstream os;
|
||||
tun_setup->l2_finish(dhcp_capture->get_props(), config->stop, os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
parent.tun_connected();
|
||||
dhcp_capture.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("L2: Waiting for DHCP handshake...");
|
||||
layer_2_schedule_timer(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stop();
|
||||
parent.tun_error(Error::TUN_SETUP_FAILED, std::string("L2 exception: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // contains the TAP device HANDLE
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
TunProp::State::Ptr state;
|
||||
TunWin::SetupBase::Ptr tun_setup;
|
||||
|
||||
// Layer 2 DHCP stuff
|
||||
std::unique_ptr<DHCPCapture> dhcp_capture;
|
||||
AsioTimer l2_timer;
|
||||
|
||||
Frame::Context& frame_context;
|
||||
|
||||
bool halt;
|
||||
};
|
||||
|
||||
inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli)
|
||||
{
|
||||
if (wintun)
|
||||
return TunClient::Ptr(new WintunClient(io_context, this, parent));
|
||||
else
|
||||
return TunClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,921 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Client tun setup for Windows
|
||||
|
||||
#ifndef OPENVPN_TUN_WIN_CLIENT_TUNSETUP_H
|
||||
#define OPENVPN_TUN_WIN_CLIENT_TUNSETUP_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <thread>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/arraysize.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/time/time.hpp>
|
||||
#include <openvpn/tun/proxy.hpp>
|
||||
#include <openvpn/tun/win/tunutil.hpp>
|
||||
#include <openvpn/tun/win/winproxy.hpp>
|
||||
#include <openvpn/tun/win/client/setupbase.hpp>
|
||||
#include <openvpn/win/scoped_handle.hpp>
|
||||
#include <openvpn/win/cmd.hpp>
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600 // Vista+
|
||||
#include <openvpn/tun/win/nrpt.hpp>
|
||||
#include <openvpn/tun/win/wfp.hpp>
|
||||
#endif
|
||||
|
||||
#include <versionhelpers.h>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
class Setup : public SetupBase
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
Setup(openvpn_io::io_context& io_context_arg, bool wintun_arg=false)
|
||||
: delete_route_timer(io_context_arg),
|
||||
wintun(wintun_arg) {}
|
||||
|
||||
// Set up the TAP device
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
const std::wstring& openvpn_app_path,
|
||||
Stop* stop,
|
||||
std::ostream& os,
|
||||
RingBuffer::Ptr ring_buffer) override // defined by SetupBase
|
||||
{
|
||||
// close out old remove cmds, if they exist
|
||||
destroy(os);
|
||||
|
||||
// enumerate available TAP adapters
|
||||
Util::TapNameGuidPairList guids(wintun);
|
||||
os << "TAP ADAPTERS:" << std::endl << guids.to_string() << std::endl;
|
||||
|
||||
// open TAP device handle
|
||||
std::string path_opened;
|
||||
Util::TapNameGuidPair tap;
|
||||
Win::ScopedHANDLE th(Util::tap_open(guids, path_opened, tap, wintun));
|
||||
const std::string msg = "Open TAP device \"" + tap.name + "\" PATH=\"" + path_opened + '\"';
|
||||
|
||||
if (!th.defined())
|
||||
{
|
||||
os << msg << " FAILED" << std::endl;
|
||||
throw ErrorCode(Error::TUN_IFACE_CREATE, true, "cannot acquire TAP handle");
|
||||
}
|
||||
|
||||
os << msg << " SUCCEEDED" << std::endl;
|
||||
if (!wintun)
|
||||
{
|
||||
Util::TAPDriverVersion version(th());
|
||||
os << version.to_string() << std::endl;
|
||||
}
|
||||
|
||||
// create ActionLists for setting up and removing adapter properties
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
remove_cmds.reset(new ActionList());
|
||||
|
||||
// populate add/remove lists with actions
|
||||
switch (pull.layer())
|
||||
{
|
||||
case Layer::OSI_LAYER_3:
|
||||
adapter_config(th(), openvpn_app_path, tap, pull, false, *add_cmds, *remove_cmds, os);
|
||||
break;
|
||||
case Layer::OSI_LAYER_2:
|
||||
adapter_config_l2(th(), openvpn_app_path, tap, pull, *add_cmds, *remove_cmds, os);
|
||||
break;
|
||||
default:
|
||||
throw tun_win_setup("layer undefined");
|
||||
}
|
||||
// execute the add actions
|
||||
add_cmds->execute(os);
|
||||
|
||||
// now that the add actions have succeeded,
|
||||
// enable the remove actions
|
||||
remove_cmds->enable_destroy(true);
|
||||
|
||||
// if layer 2, save state
|
||||
if (pull.layer() == Layer::OSI_LAYER_2)
|
||||
l2_state.reset(new L2State(tap, openvpn_app_path));
|
||||
|
||||
if (ring_buffer)
|
||||
register_rings(th(), ring_buffer);
|
||||
|
||||
return th.release();
|
||||
}
|
||||
|
||||
// In layer 2 mode, return true route_delay seconds after
|
||||
// the adapter properties matches the data given in pull.
|
||||
// This method is usually called once per second until it
|
||||
// returns true.
|
||||
virtual bool l2_ready(const TunBuilderCapture& pull) override
|
||||
{
|
||||
const unsigned int route_delay = 5;
|
||||
if (l2_state)
|
||||
{
|
||||
if (l2_state->props_ready.defined())
|
||||
{
|
||||
if (Time::now() >= l2_state->props_ready)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Util::IPNetmask4 vpn_addr(pull, "VPN IP");
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (ai.is_up(l2_state->tap.index, vpn_addr))
|
||||
l2_state->props_ready = Time::now() + Time::Duration::seconds(route_delay);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish the layer 2 configuration, should be called
|
||||
// after l2_ready() returns true.
|
||||
virtual void l2_finish(const TunBuilderCapture& pull,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
std::unique_ptr<L2State> l2s(std::move(l2_state));
|
||||
if (l2s)
|
||||
{
|
||||
Win::ScopedHANDLE nh;
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, pull, true, *add_cmds, *remove_cmds, os);
|
||||
add_cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void destroy(std::ostream& os) override // defined by DestructorBase
|
||||
{
|
||||
// l2_state
|
||||
l2_state.reset();
|
||||
|
||||
// l2_thread
|
||||
if (l2_thread)
|
||||
{
|
||||
try {
|
||||
l2_thread->join();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
l2_thread.reset();
|
||||
}
|
||||
|
||||
// remove_cmds
|
||||
if (remove_cmds)
|
||||
{
|
||||
remove_cmds->destroy(os);
|
||||
remove_cmds.reset();
|
||||
}
|
||||
|
||||
delete_route_timer.cancel();
|
||||
}
|
||||
|
||||
virtual ~Setup()
|
||||
{
|
||||
std::ostringstream os;
|
||||
destroy(os);
|
||||
}
|
||||
|
||||
static void add_bypass_route(const std::string& route,
|
||||
bool ipv6,
|
||||
ActionList& add_cmds,
|
||||
ActionList& remove_cmds_bypass_gw)
|
||||
{
|
||||
const Util::DefaultGateway gw;
|
||||
|
||||
if (!ipv6)
|
||||
{
|
||||
add_cmds.add(new WinCmd("netsh interface ip add route " + route + "/32 " + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + " store=active"));
|
||||
remove_cmds_bypass_gw.add(new WinCmd("netsh interface ip delete route " + route + "/32 " + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + " store=active"));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct L2State
|
||||
{
|
||||
L2State(const Util::TapNameGuidPair& tap_arg,
|
||||
const std::wstring& openvpn_app_path_arg)
|
||||
: tap(tap_arg),
|
||||
openvpn_app_path(openvpn_app_path_arg)
|
||||
{
|
||||
}
|
||||
|
||||
Util::TapNameGuidPair tap;
|
||||
std::wstring openvpn_app_path;
|
||||
Time props_ready;
|
||||
};
|
||||
|
||||
class UseDNS
|
||||
{
|
||||
public:
|
||||
UseDNS() {}
|
||||
|
||||
UseDNS(const TunBuilderCapture& pull)
|
||||
{
|
||||
for (auto &ds : pull.dns_servers)
|
||||
add(ds, pull);
|
||||
}
|
||||
|
||||
static bool enabled(const TunBuilderCapture::DNSServer& ds,
|
||||
const TunBuilderCapture& pull)
|
||||
{
|
||||
if (ds.ipv6 && pull.block_ipv6)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int add(const TunBuilderCapture::DNSServer& ds,
|
||||
const TunBuilderCapture& pull)
|
||||
{
|
||||
if (enabled(ds, pull))
|
||||
return indices[ds.ipv6 ? 1 : 0]++;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ipv4() const { return indices[0]; }
|
||||
int ipv6() const { return indices[1]; }
|
||||
|
||||
private:
|
||||
int indices[2] = {0, 0};
|
||||
};
|
||||
|
||||
void register_rings(HANDLE handle, RingBuffer::Ptr ring_buffer)
|
||||
{
|
||||
TUN_REGISTER_RINGS rings;
|
||||
|
||||
ZeroMemory(&rings, sizeof(rings));
|
||||
|
||||
rings.receive.ring = ring_buffer->receive_ring();
|
||||
rings.receive.tail_moved = ring_buffer->receive_ring_tail_moved();
|
||||
rings.receive.ring_size = sizeof(rings.receive.ring->data);
|
||||
|
||||
rings.send.ring = ring_buffer->send_ring();
|
||||
rings.send.tail_moved = ring_buffer->send_ring_tail_moved();
|
||||
rings.send.ring_size = sizeof(rings.send.ring->data);
|
||||
|
||||
{
|
||||
Win::Impersonate imp(true);
|
||||
|
||||
DWORD len;
|
||||
if (!DeviceIoControl(handle, TUN_IOCTL_REGISTER_RINGS, &rings, sizeof(rings), NULL, NULL, &len, NULL))
|
||||
{
|
||||
const Win::LastError err;
|
||||
throw ErrorCode(Error::TUN_REGISTER_RINGS_ERROR, true, "Error registering ring buffers: " + err.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600
|
||||
// Configure TAP adapter on Vista and higher
|
||||
void adapter_config(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
const bool l2_post,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
{
|
||||
// Windows interface index
|
||||
const std::string tap_index_name = tap.index_or_name();
|
||||
|
||||
// special IPv6 next-hop recognized by TAP driver (magic)
|
||||
const std::string ipv6_next_hop = "fe80::8";
|
||||
|
||||
// get default gateway
|
||||
const Util::DefaultGateway gw;
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
if (!l2_post)
|
||||
{
|
||||
// set TAP media status to CONNECTED
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// try to delete any stale routes on interface left over from previous session
|
||||
create.add(new Util::ActionDeleteAllRoutesOnInterface(tap.index));
|
||||
}
|
||||
|
||||
// Set IPv4 Interface
|
||||
//
|
||||
// Usage: set address [name=]<string>
|
||||
// [[source=]dhcp|static]
|
||||
// [[address=]<IPv4 address>[/<integer>] [[mask=]<IPv4 mask>]
|
||||
// [[gateway=]<IPv4 address>|none [gwmetric=]<integer>]
|
||||
// [[type=]unicast|anycast]
|
||||
// [[subinterface=]<string>]
|
||||
// [[store=]active|persistent]
|
||||
// Usage: delete address [name=]<string> [[address=]<IPv4 address>]
|
||||
// [[gateway=]<IPv4 address>|all]
|
||||
// [[store=]active|persistent]
|
||||
if (local4)
|
||||
{
|
||||
// Process ifconfig and topology
|
||||
if (!l2_post)
|
||||
{
|
||||
// set lowest interface metric to make Windows use pushed DNS search domain
|
||||
create.add(new WinCmd("netsh interface ip set interface " + tap_index_name + " metric=1"));
|
||||
|
||||
const std::string metric = route_metric_opt(pull, *local4, MT_IFACE);
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (!wintun)
|
||||
{
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
}
|
||||
create.add(new WinCmd("netsh interface ip set address " + tap_index_name + " static " + local4->address + ' ' + netmask + " gateway=" + local4->gateway + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete address " + tap_index_name + ' ' + local4->address + " gateway=all store=active"));
|
||||
|
||||
// specifying 'gateway' when setting ip address makes Windows add unnecessary route 0.0.0.0/0,
|
||||
// which might cause routing conflicts, so we have to delete it after a small delay.
|
||||
// If route is deleted before profile is created, then profile won't be created at all (OVPN-135)
|
||||
WinCmd::Ptr cmd = new WinCmd("netsh interface ip delete route 0.0.0.0/0 " + tap_index_name + ' ' + local4->gateway + " store=active");
|
||||
delete_route_timer.expires_after(Time::Duration::seconds(5));
|
||||
delete_route_timer.async_wait([self=Ptr(this), cmd=std::move(cmd)](const openvpn_io::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::ostringstream os;
|
||||
cmd->execute(os);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Should we block IPv6?
|
||||
if (pull.block_ipv6)
|
||||
{
|
||||
static const char *const block_ipv6_net[] = {
|
||||
"2000::/4",
|
||||
"3000::/4",
|
||||
"fc00::/7",
|
||||
};
|
||||
for (size_t i = 0; i < array_size(block_ipv6_net); ++i)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ipv6 add route " + std::string(block_ipv6_net[i]) + " interface=1 store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete route " + std::string(block_ipv6_net[i]) + " interface=1 store=active"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
//
|
||||
// Usage: set address [interface=]<string> [address=]<IPv6 address>
|
||||
// [[type=]unicast|anycast]
|
||||
// [[validlifetime=]<integer>|infinite]
|
||||
// [[preferredlifetime=]<integer>|infinite]
|
||||
// [[store=]active|persistent]
|
||||
//Usage: delete address [interface=]<string> [address=]<IPv6 address>
|
||||
// [[store=]active|persistent]
|
||||
if (local6 && !pull.block_ipv6 && !l2_post)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ipv6 set address " + tap_index_name + ' ' + local6->address + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete address " + tap_index_name + ' ' + local6->address + " store=active"));
|
||||
|
||||
create.add(new WinCmd("netsh interface ipv6 add route " + local6->gateway + '/' + to_string(local6->prefix_length) + ' ' + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete route " + local6->gateway + '/' + to_string(local6->prefix_length) + ' ' + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
}
|
||||
|
||||
// Process Routes
|
||||
//
|
||||
// Usage: add route [prefix=]<IPv4 address>/<integer> [interface=]<string>
|
||||
// [[nexthop=]<IPv4 address>] [[siteprefixlength=]<integer>]
|
||||
// [[metric=]<integer>] [[publish=]no|age|yes]
|
||||
// [[validlifetime=]<integer>|infinite]
|
||||
// [[preferredlifetime=]<integer>|infinite]
|
||||
// [[store=]active|persistent]
|
||||
// Usage: delete route [prefix=]<IPv4 address>/<integer> [interface=]<string>
|
||||
// [[nexthop=]<IPv4 address>]
|
||||
// [[store=]active|persistent]
|
||||
//
|
||||
// Usage: add route [prefix=]<IPv6 address>/<integer> [interface=]<string>
|
||||
// [[nexthop=]<IPv6 address>] [[siteprefixlength=]<integer>]
|
||||
// [[metric=]<integer>] [[publish=]no|age|yes]
|
||||
// [[validlifetime=]<integer>|infinite]
|
||||
// [[preferredlifetime=]<integer>|infinite]
|
||||
// [[store=]active|persistent]
|
||||
// Usage: delete route [prefix=]<IPv6 address>/<integer> [interface=]<string>
|
||||
// [[nexthop=]<IPv6 address>]
|
||||
// [[store=]active|persistent]
|
||||
{
|
||||
for (auto &route : pull.add_routes)
|
||||
{
|
||||
const std::string metric = route_metric_opt(pull, route, MT_NETSH);
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ipv6 add route " + route.address + '/' + to_string(route.prefix_length) + ' ' + tap_index_name + ' ' + ipv6_next_hop + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete route " + route.address + '/' + to_string(route.prefix_length) + ' ' + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ip add route " + route.address + '/' + to_string(route.prefix_length) + ' ' + tap_index_name + ' ' + local4->gateway + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete route " + route.address + '/' + to_string(route.prefix_length) + ' ' + tap_index_name + ' ' + local4->gateway + " store=active"));
|
||||
}
|
||||
else
|
||||
throw tun_win_setup("IPv4 routes pushed without IPv4 ifconfig");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
if (gw.defined())
|
||||
{
|
||||
bool ipv6_error = false;
|
||||
for (auto &route : pull.exclude_routes)
|
||||
{
|
||||
const std::string metric = route_metric_opt(pull, route, MT_NETSH);
|
||||
if (route.ipv6)
|
||||
{
|
||||
ipv6_error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ip add route " + route.address + '/' + to_string(route.prefix_length) + ' ' + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete route " + route.address + '/' + to_string(route.prefix_length) + ' ' + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + " store=active"));
|
||||
}
|
||||
}
|
||||
if (ipv6_error)
|
||||
os << "NOTE: exclude IPv6 routes not currently supported" << std::endl;
|
||||
}
|
||||
else
|
||||
os << "NOTE: exclude routes error: cannot detect default gateway" << std::endl;
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add server bypass route
|
||||
if (gw.defined())
|
||||
{
|
||||
if (!pull.remote_address.ipv6)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ip add route " + pull.remote_address.address + "/32 " + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete route " + pull.remote_address.address + "/32 " + to_string(gw.interface_index()) + ' ' + gw.gateway_address() + " store=active"));
|
||||
}
|
||||
}
|
||||
else
|
||||
throw tun_win_setup("redirect-gateway error: cannot detect default gateway");
|
||||
|
||||
create.add(new WinCmd("netsh interface ip add route 0.0.0.0/1 " + tap_index_name + ' ' + local4->gateway + " store=active"));
|
||||
create.add(new WinCmd("netsh interface ip add route 128.0.0.0/1 " + tap_index_name + ' ' + local4->gateway + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete route 0.0.0.0/1 " + tap_index_name + ' ' + local4->gateway + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete route 128.0.0.0/1 " + tap_index_name + ' ' + local4->gateway + " store=active"));
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ipv6 add route 0::/1 " + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
create.add(new WinCmd("netsh interface ipv6 add route 8000::/1 " + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete route 0::/1 " + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ipv6 delete route 8000::/1 " + tap_index_name + ' ' + ipv6_next_hop + " store=active"));
|
||||
}
|
||||
|
||||
// Process DNS Servers
|
||||
//
|
||||
// Usage: set dnsservers [name=]<string> [source=]dhcp|static
|
||||
// [[address=]<IP address>|none]
|
||||
// [[register=]none|primary|both]
|
||||
// [[validate=]yes|no]
|
||||
// Usage: add dnsservers [name=]<string> [address=]<IPv4 address>
|
||||
// [[index=]<integer>] [[validate=]yes|no]
|
||||
// Usage: delete dnsservers [name=]<string> [[address=]<IP address>|all] [[validate=]yes|no]
|
||||
//
|
||||
// Usage: set dnsservers [name=]<string> [source=]dhcp|static
|
||||
// [[address=]<IPv6 address>|none]
|
||||
// [[register=]none|primary|both]
|
||||
// [[validate=]yes|no]
|
||||
// Usage: add dnsservers [name=]<string> [address=]<IPv6 address>
|
||||
// [[index=]<integer>] [[validate=]yes|no]
|
||||
// Usage: delete dnsservers [name=]<string> [[address=]<IPv6 address>|all] [[validate=]yes|no]
|
||||
{
|
||||
// fix for vista and dnsserver vs win7+ dnsservers
|
||||
std::string dns_servers_cmd = "dnsservers";
|
||||
std::string validate_cmd = " validate=no";
|
||||
if (IsWindowsVistaOrGreater() && !IsWindows7OrGreater()) {
|
||||
dns_servers_cmd = "dnsserver";
|
||||
validate_cmd = "";
|
||||
}
|
||||
|
||||
#if 1
|
||||
// normal production setting
|
||||
const bool use_nrpt = IsWindows8OrGreater();
|
||||
const bool use_wfp = IsWindows8OrGreater();
|
||||
const bool add_netsh_rules = true;
|
||||
#else
|
||||
// test NRPT registry settings on pre-Win8
|
||||
const bool use_nrpt = true;
|
||||
const bool use_wfp = true;
|
||||
const bool add_netsh_rules = true;
|
||||
#endif
|
||||
// determine IPv4/IPv6 DNS redirection
|
||||
const UseDNS dns(pull);
|
||||
|
||||
// will DNS requests be split between VPN DNS server and local?
|
||||
const bool split_dns = (!pull.search_domains.empty()
|
||||
&& !(pull.reroute_gw.ipv4 && dns.ipv4())
|
||||
&& !(pull.reroute_gw.ipv6 && dns.ipv6()));
|
||||
|
||||
// add DNS servers via netsh
|
||||
if (add_netsh_rules && !(use_nrpt && split_dns) && !l2_post)
|
||||
{
|
||||
UseDNS dc;
|
||||
for (auto &ds : pull.dns_servers)
|
||||
{
|
||||
// 0-based index for specific IPv4/IPv6 protocol, or -1 if disabled
|
||||
const int count = dc.add(ds, pull);
|
||||
if (count >= 0)
|
||||
{
|
||||
const std::string proto = ds.ipv6 ? "ipv6" : "ip";
|
||||
if (count)
|
||||
create.add(new WinCmd("netsh interface " + proto + " add " + dns_servers_cmd + " " + tap_index_name + ' ' + ds.address + " " + to_string(count+1) + validate_cmd));
|
||||
else
|
||||
{
|
||||
create.add(new WinCmd("netsh interface " + proto + " set " + dns_servers_cmd + " " + tap_index_name + " static " + ds.address + " register=primary" + validate_cmd));
|
||||
destroy.add(new WinCmd("netsh interface " + proto + " delete " + dns_servers_cmd + " " + tap_index_name + " all" + validate_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If NRPT enabled and at least one IPv4 or IPv6 DNS
|
||||
// server was added, add NRPT registry entries to
|
||||
// route DNS through the tunnel.
|
||||
// Also consider selective DNS routing using domain
|
||||
// suffix list from pull.search_domains as set by
|
||||
// "dhcp-option DOMAIN ..." directives.
|
||||
if (use_nrpt && (dns.ipv4() || dns.ipv6()))
|
||||
{
|
||||
// domain suffix list
|
||||
std::vector<std::string> dsfx;
|
||||
|
||||
// Only add DNS routing suffixes if not rerouting gateway.
|
||||
// Otherwise, route all DNS requests with wildcard (".").
|
||||
if (split_dns)
|
||||
{
|
||||
for (const auto &sd : pull.search_domains)
|
||||
{
|
||||
std::string dom = sd.domain;
|
||||
if (!dom.empty())
|
||||
{
|
||||
// each DNS suffix must begin with '.'
|
||||
if (dom[0] != '.')
|
||||
dom = "." + dom;
|
||||
dsfx.push_back(std::move(dom));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dsfx.empty())
|
||||
dsfx.emplace_back(".");
|
||||
|
||||
// DNS server list
|
||||
std::vector<std::string> dserv;
|
||||
for (const auto &ds : pull.dns_servers)
|
||||
dserv.push_back(ds.address);
|
||||
|
||||
create.add(new NRPT::ActionCreate(dsfx, dserv));
|
||||
destroy.add(new NRPT::ActionDelete);
|
||||
}
|
||||
|
||||
// Use WFP for DNS leak protection.
|
||||
// If we added DNS servers, block DNS on all interfaces except
|
||||
// the TAP adapter.
|
||||
if (use_wfp && !split_dns && !openvpn_app_path.empty() && (dns.ipv4() || dns.ipv6()))
|
||||
{
|
||||
create.add(new ActionWFP(openvpn_app_path, tap.index, true, wfp));
|
||||
destroy.add(new ActionWFP(openvpn_app_path, tap.index, false, wfp));
|
||||
}
|
||||
}
|
||||
|
||||
// Set a default TAP-adapter domain suffix using
|
||||
// "dhcp-option ADAPTER_DOMAIN_SUFFIX mycompany.com" directive.
|
||||
if (!pull.adapter_domain_suffix.empty())
|
||||
{
|
||||
// Only the first search domain is used
|
||||
create.add(new Util::ActionSetAdapterDomainSuffix(pull.adapter_domain_suffix, tap.guid));
|
||||
destroy.add(new Util::ActionSetAdapterDomainSuffix("", tap.guid));
|
||||
}
|
||||
|
||||
// Process WINS Servers
|
||||
//
|
||||
// Usage: set winsservers [name=]<string> [source=]dhcp|static
|
||||
// [[address=]<IP address>|none]
|
||||
// Usage: add winsservers [name=]<string> [address=]<IP address> [[index=]<integer>]
|
||||
// Usage: delete winsservers [name=]<string> [[address=]<IP address>|all]
|
||||
{
|
||||
for (size_t i = 0; i < pull.wins_servers.size(); ++i)
|
||||
{
|
||||
const TunBuilderCapture::WINSServer& ws = pull.wins_servers[i];
|
||||
if (i)
|
||||
create.add(new WinCmd("netsh interface ip add winsservers " + tap_index_name + ' ' + ws.address + ' ' + to_string(i+1)));
|
||||
else
|
||||
{
|
||||
create.add(new WinCmd("netsh interface ip set winsservers " + tap_index_name + " static " + ws.address));
|
||||
destroy.add(new WinCmd("netsh interface ip delete winsservers " + tap_index_name + " all"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_LOG("proxy_auto_config_url " << pull.proxy_auto_config_url.url);
|
||||
if (pull.proxy_auto_config_url.defined())
|
||||
ProxySettings::add_actions<WinProxySettings>(pull, create, destroy);
|
||||
|
||||
// flush DNS cache
|
||||
create.add(new WinCmd("ipconfig /flushdns"));
|
||||
destroy.add(new WinCmd("ipconfig /flushdns"));
|
||||
}
|
||||
#else
|
||||
// Configure TAP adapter for pre-Vista
|
||||
// Currently we don't support IPv6 on pre-Vista
|
||||
void adapter_config(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
const bool l2_post,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
{
|
||||
// Windows interface index
|
||||
const std::string tap_index_name = tap.index_or_name();
|
||||
|
||||
// get default gateway
|
||||
const Util::DefaultGateway gw;
|
||||
|
||||
// set local4 to point to IPv4 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
|
||||
// This section skipped on layer 2 post-config
|
||||
if (!l2_post)
|
||||
{
|
||||
// Make sure the TAP adapter is set for DHCP
|
||||
{
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (!ai.is_dhcp_enabled(tap.index))
|
||||
{
|
||||
os << "TAP: DHCP is disabled, attempting to enable" << std::endl;
|
||||
ActionList::Ptr cmds(new ActionList());
|
||||
cmds->add(new Util::ActionEnableDHCP(tap));
|
||||
cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
// Process ifconfig and topology
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
}
|
||||
|
||||
// On pre-Vista, set up TAP adapter DHCP masquerade for
|
||||
// configuring adapter properties.
|
||||
{
|
||||
os << "TAP: configure DHCP masquerade" << std::endl;
|
||||
Util::TAPDHCPMasquerade dhmasq;
|
||||
dhmasq.init_from_capture(pull);
|
||||
dhmasq.ioctl(th);
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
|
||||
// DHCP release/renew
|
||||
{
|
||||
const Util::InterfaceInfoList ii;
|
||||
Util::dhcp_release(ii, tap.index, os);
|
||||
Util::dhcp_renew(ii, tap.index, os);
|
||||
}
|
||||
|
||||
// Wait for TAP adapter to come up
|
||||
{
|
||||
bool succeed = false;
|
||||
const Util::IPNetmask4 vpn_addr(pull, "VPN IP");
|
||||
for (int i = 1; i <= 30; ++i)
|
||||
{
|
||||
os << '[' << i << "] waiting for TAP adapter to receive DHCP settings..." << std::endl;
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (ai.is_up(tap.index, vpn_addr))
|
||||
{
|
||||
succeed = true;
|
||||
break;
|
||||
}
|
||||
::Sleep(1000);
|
||||
}
|
||||
if (!succeed)
|
||||
throw tun_win_setup("TAP adapter DHCP handshake failed");
|
||||
}
|
||||
|
||||
// Pre route-add sleep
|
||||
os << "Sleeping 5 seconds prior to adding routes..." << std::endl;
|
||||
::Sleep(5000);
|
||||
}
|
||||
|
||||
// Process routes
|
||||
for (auto &route : pull.add_routes)
|
||||
{
|
||||
const std::string metric = route_metric_opt(pull, route, MT_ROUTE);
|
||||
if (!route.ipv6)
|
||||
{
|
||||
if (local4)
|
||||
{
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(route.prefix_length).to_string();
|
||||
create.add(new WinCmd("route ADD " + route.address + " MASK " + netmask + ' ' + local4->gateway + metric));
|
||||
destroy.add(new WinCmd("route DELETE " + route.address + " MASK " + netmask + ' ' + local4->gateway));
|
||||
}
|
||||
else
|
||||
throw tun_win_setup("IPv4 routes pushed without IPv4 ifconfig");
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
if (gw.defined())
|
||||
{
|
||||
for (auto &route : pull.exclude_routes)
|
||||
{
|
||||
const std::string metric = route_metric_opt(pull, route, MT_ROUTE);
|
||||
if (!route.ipv6)
|
||||
{
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(route.prefix_length).to_string();
|
||||
create.add(new WinCmd("route ADD " + route.address + " MASK " + netmask + ' ' + gw.gateway_address() + metric));
|
||||
destroy.add(new WinCmd("route DELETE " + route.address + " MASK " + netmask + ' ' + gw.gateway_address()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
os << "NOTE: exclude routes error: cannot detect default gateway" << std::endl;
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add server bypass route
|
||||
if (gw.defined())
|
||||
{
|
||||
if (!pull.remote_address.ipv6)
|
||||
{
|
||||
create.add(new WinCmd("route ADD " + pull.remote_address.address + " MASK 255.255.255.255 " + gw.gateway_address()));
|
||||
destroy.add(new WinCmd("route DELETE " + pull.remote_address.address + " MASK 255.255.255.255 " + gw.gateway_address()));
|
||||
}
|
||||
}
|
||||
else
|
||||
throw tun_win_setup("redirect-gateway error: cannot detect default gateway");
|
||||
|
||||
create.add(new WinCmd("route ADD 0.0.0.0 MASK 128.0.0.0 " + local4->gateway));
|
||||
create.add(new WinCmd("route ADD 128.0.0.0 MASK 128.0.0.0 " + local4->gateway));
|
||||
destroy.add(new WinCmd("route DELETE 0.0.0.0 MASK 128.0.0.0 " + local4->gateway));
|
||||
destroy.add(new WinCmd("route DELETE 128.0.0.0 MASK 128.0.0.0 " + local4->gateway));
|
||||
}
|
||||
|
||||
// flush DNS cache
|
||||
//create.add(new WinCmd("net stop dnscache"));
|
||||
//create.add(new WinCmd("net start dnscache"));
|
||||
create.add(new WinCmd("ipconfig /flushdns"));
|
||||
//create.add(new WinCmd("ipconfig /registerdns"));
|
||||
destroy.add(new WinCmd("ipconfig /flushdns"));
|
||||
}
|
||||
#endif
|
||||
|
||||
void adapter_config_l2(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
const TunBuilderCapture& pull,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
std::ostream& os)
|
||||
{
|
||||
// Make sure the TAP adapter is set for DHCP
|
||||
{
|
||||
const Util::IPAdaptersInfo ai;
|
||||
if (!ai.is_dhcp_enabled(tap.index))
|
||||
{
|
||||
os << "TAP: DHCP is disabled, attempting to enable" << std::endl;
|
||||
ActionList::Ptr cmds(new ActionList());
|
||||
cmds->add(new Util::ActionEnableDHCP(tap));
|
||||
cmds->execute(os);
|
||||
}
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
|
||||
// We must do DHCP release/renew in a background thread
|
||||
// so the foreground can forward the DHCP negotiation packets
|
||||
// over the tunnel.
|
||||
l2_thread.reset(new std::thread([this, logwrap=Log::Context::Wrapper(), tap]() {
|
||||
Log::Context logctx(logwrap);
|
||||
::Sleep(250);
|
||||
const Util::InterfaceInfoList ii;
|
||||
{
|
||||
std::ostringstream os;
|
||||
Util::dhcp_release(ii, tap.index, os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
}
|
||||
::Sleep(250);
|
||||
{
|
||||
std::ostringstream os;
|
||||
Util::dhcp_renew(ii, tap.index, os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
enum MetricType {
|
||||
MT_ROUTE,
|
||||
MT_NETSH,
|
||||
MT_IFACE,
|
||||
};
|
||||
|
||||
static std::string route_metric_opt(const TunBuilderCapture& pull,
|
||||
const TunBuilderCapture::RouteBase& route,
|
||||
const MetricType mt)
|
||||
{
|
||||
int metric = pull.route_metric_default;
|
||||
if (route.metric >= 0)
|
||||
metric = route.metric;
|
||||
if (metric >= 0)
|
||||
{
|
||||
switch (mt)
|
||||
{
|
||||
case MT_ROUTE:
|
||||
return " METRIC " + std::to_string(metric); // route command form
|
||||
case MT_NETSH:
|
||||
return " metric=" + std::to_string(metric); // "netsh interface ip[v6] add route" form
|
||||
case MT_IFACE:
|
||||
return " gwmetric=" + std::to_string(metric); // "netsh interface ip set address" form
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600 // Vista+
|
||||
TunWin::WFPContext::Ptr wfp{new TunWin::WFPContext};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<std::thread> l2_thread;
|
||||
std::unique_ptr<L2State> l2_state;
|
||||
|
||||
ActionList::Ptr remove_cmds;
|
||||
|
||||
AsioTimer delete_route_timer;
|
||||
|
||||
bool wintun = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/win/client/setupbase.hpp>
|
||||
#include <openvpn/tun/win/client/clientconfig.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
class WintunClient : public TunClient
|
||||
{
|
||||
typedef RCPtr<WintunClient> Ptr;
|
||||
|
||||
public:
|
||||
WintunClient(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
frame(config_arg->frame)
|
||||
{
|
||||
}
|
||||
|
||||
// Inherited via TunClient
|
||||
void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
halt = false;
|
||||
if (config->tun_persist)
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
else
|
||||
tun_persist.reset(new TunPersist(false, false, nullptr)); // short-term
|
||||
|
||||
try {
|
||||
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state().state;
|
||||
ring_buffer = tun_persist->state().ring_buffer;
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// close old TAP handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj(io_context);
|
||||
|
||||
ring_buffer.reset(new RingBuffer(io_context));
|
||||
|
||||
// open/config TAP
|
||||
HANDLE th;
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os]() { OPENVPN_LOG_STRING(os.str()); });
|
||||
th = tun_setup->establish(*po, Win::module_name(), config->stop, os, ring_buffer);
|
||||
}
|
||||
|
||||
// create ASIO wrapper for HANDLE
|
||||
TAPStream* ts = new TAPStream(io_context, th);
|
||||
|
||||
// persist tun settings state
|
||||
if (tun_persist->persist_tun_state(ts, { state, ring_buffer }))
|
||||
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
|
||||
// assert ownership over TAP device handle
|
||||
tun_setup->confirm();
|
||||
}
|
||||
|
||||
openvpn_io::post(io_context, [self=Ptr(this)](){
|
||||
self->read();
|
||||
});
|
||||
|
||||
parent.tun_connected(); // signal that we are connected
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stop();
|
||||
Error::Type err = Error::TUN_SETUP_FAILED;
|
||||
const ExceptionCode* ec = dynamic_cast<const ExceptionCode*>(&e);
|
||||
if (ec && ec->code_defined())
|
||||
err = ec->code();
|
||||
parent.tun_error(err, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void set_disconnect() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
TUN_RING* receive_ring = ring_buffer->receive_ring();
|
||||
|
||||
ULONG head = receive_ring->head.load(std::memory_order_acquire);
|
||||
if (head > WINTUN_RING_CAPACITY)
|
||||
{
|
||||
if (head == 0xFFFFFFFF)
|
||||
parent.tun_error(Error::TUN_WRITE_ERROR, "invalid ring head/tail or bogus packet received");
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONG tail = receive_ring->tail.load(std::memory_order_acquire);
|
||||
if (tail >= WINTUN_RING_CAPACITY)
|
||||
return false;
|
||||
|
||||
ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + buf.size());
|
||||
ULONG buf_space = wrap(head - tail - WINTUN_PACKET_ALIGN);
|
||||
if (aligned_packet_size > buf_space)
|
||||
{
|
||||
OPENVPN_LOG("ring is full");
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy packet size and data into ring
|
||||
TUN_PACKET* packet = (TUN_PACKET*)& receive_ring->data[tail];
|
||||
packet->size = buf.size();
|
||||
std::memcpy(packet->data, buf.data(), buf.size());
|
||||
|
||||
// move ring tail
|
||||
receive_ring->tail.store(wrap(tail + aligned_packet_size), std::memory_order_release);
|
||||
if (receive_ring->alertable.load(std::memory_order_acquire) != 0)
|
||||
SetEvent(ring_buffer->receive_ring_tail_moved());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string tun_name() const override
|
||||
{
|
||||
return "wintun";
|
||||
}
|
||||
|
||||
std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
void read()
|
||||
{
|
||||
TUN_RING* send_ring = ring_buffer->send_ring();
|
||||
|
||||
if (halt)
|
||||
return;
|
||||
|
||||
ULONG head = send_ring->head.load(std::memory_order_acquire);
|
||||
if (head >= WINTUN_RING_CAPACITY)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "ring head exceeds ring capacity");
|
||||
return;
|
||||
}
|
||||
|
||||
ULONG tail = send_ring->tail.load(std::memory_order_acquire);
|
||||
if (tail >= WINTUN_RING_CAPACITY)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "ring tail exceeds ring capacity");
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// tail has moved?
|
||||
if (head == tail)
|
||||
{
|
||||
ring_buffer->send_tail_moved_asio_event().async_wait([self = Ptr(this)](const openvpn_io::error_code& error) {
|
||||
if (!error)
|
||||
self->read();
|
||||
else
|
||||
{
|
||||
if (!self->halt)
|
||||
self->parent.tun_error(Error::TUN_ERROR, "error waiting on ring send tail moved");
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// read buffer content
|
||||
ULONG content_len = wrap(tail - head);
|
||||
if (content_len < sizeof(TUN_PACKET_HEADER))
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "incomplete packet header in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
TUN_PACKET* packet = (TUN_PACKET *)&send_ring->data[head];
|
||||
if (packet->size > WINTUN_MAX_PACKET_SIZE)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "packet too big in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + packet->size);
|
||||
if (aligned_packet_size > content_len)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "incomplete packet in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
frame->prepare(Frame::READ_TUN, buf);
|
||||
|
||||
buf.write(packet->data, packet->size);
|
||||
|
||||
head = wrap(head + aligned_packet_size);
|
||||
send_ring->head.store(head, std::memory_order_release);
|
||||
|
||||
parent.tun_recv(buf);
|
||||
|
||||
if (halt)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct TUN_PACKET_HEADER
|
||||
{
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct TUN_PACKET
|
||||
{
|
||||
uint32_t size;
|
||||
UCHAR data[WINTUN_MAX_PACKET_SIZE];
|
||||
};
|
||||
|
||||
ULONG packet_align(ULONG size)
|
||||
{
|
||||
return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1);
|
||||
}
|
||||
|
||||
ULONG wrap(ULONG value)
|
||||
{
|
||||
return value & (WINTUN_RING_CAPACITY - 1);
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // contains the TAP device HANDLE
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunProp::State::Ptr state;
|
||||
TunWin::SetupBase::Ptr tun_setup;
|
||||
|
||||
TUN_RING* receive_ring = nullptr;
|
||||
TUN_RING* send_ring = nullptr;
|
||||
|
||||
BufferAllocated buf;
|
||||
|
||||
Frame::Ptr frame;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
ScopedHANDLE driver_handle;
|
||||
|
||||
RingBuffer::Ptr ring_buffer;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Name Resolution Policy Table (NRPT) utilities for Windows
|
||||
|
||||
#ifndef OPENVPN_TUN_WIN_NRPT_H
|
||||
#define OPENVPN_TUN_WIN_NRPT_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/wstring.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/win/reg.hpp>
|
||||
#include <openvpn/win/winerr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
// NRPT rules described here: https://msdn.microsoft.com/en-us/library/ff957356.aspx
|
||||
class NRPT
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(nrpt_error);
|
||||
|
||||
static void create_rule(const std::vector<std::string> names, const std::vector<std::string> dns_servers)
|
||||
{
|
||||
Win::RegKey key;
|
||||
|
||||
for (auto i = 0; i < names.size(); ++ i)
|
||||
{
|
||||
// open/create the key
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << dnsPolicyConfig() << "\\" << policyPrefix() << "-" << i;
|
||||
auto key_name = ss.str();
|
||||
|
||||
const LONG status = ::RegCreateKeyA(HKEY_LOCAL_MACHINE, key_name.c_str(), key.ref());
|
||||
check_reg_error<nrpt_error>(status, key_name);
|
||||
}
|
||||
|
||||
// Name
|
||||
{
|
||||
std::wstring name(wstring::from_utf8(names[i]));
|
||||
name += L'\0';
|
||||
const LONG status = ::RegSetValueExW(key(),
|
||||
L"Name",
|
||||
0,
|
||||
REG_MULTI_SZ,
|
||||
(const BYTE *)name.c_str(),
|
||||
(name.length()+1)*2);
|
||||
check_reg_error<nrpt_error>(status, "Name");
|
||||
}
|
||||
|
||||
// GenericDNSServers
|
||||
{
|
||||
const std::wstring dns_servers_joined = wstring::from_utf8(string::join(dns_servers, ";"));
|
||||
const LONG status = ::RegSetValueExW(key(),
|
||||
L"GenericDNSServers",
|
||||
0,
|
||||
REG_SZ,
|
||||
(const BYTE *)dns_servers_joined.c_str(),
|
||||
(dns_servers_joined.length()+1)*2);
|
||||
check_reg_error<nrpt_error>(status, "GenericDNSServers");
|
||||
}
|
||||
|
||||
// ConfigOptions
|
||||
{
|
||||
const DWORD value = 0x8; // Only the Generic DNS server option (that is, the option defined in section 2.2.2.13) is specified.
|
||||
const LONG status = ::RegSetValueExW(key(),
|
||||
L"ConfigOptions",
|
||||
0,
|
||||
REG_DWORD,
|
||||
(const BYTE *)&value,
|
||||
sizeof(value));
|
||||
check_reg_error<nrpt_error>(status, "ConfigOptions");
|
||||
}
|
||||
|
||||
// Version
|
||||
{
|
||||
const DWORD value = 0x2;
|
||||
const LONG status = ::RegSetValueExW(key(),
|
||||
L"Version",
|
||||
0,
|
||||
REG_DWORD,
|
||||
(const BYTE *)&value,
|
||||
sizeof(value));
|
||||
check_reg_error<nrpt_error>(status, "Version");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool delete_rule()
|
||||
{
|
||||
Win::RegKeyEnumerator keys(HKEY_LOCAL_MACHINE, dnsPolicyConfig());
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
// remove only own policies
|
||||
if (key.find(policyPrefix()) == std::string::npos)
|
||||
continue;
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << dnsPolicyConfig() << "\\" << key;
|
||||
auto path = ss.str();
|
||||
::RegDeleteTreeA(HKEY_LOCAL_MACHINE, path.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static const char *dnsPolicyConfig()
|
||||
{
|
||||
static const char subkey[] = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
|
||||
return subkey;
|
||||
}
|
||||
|
||||
static const char *policyPrefix()
|
||||
{
|
||||
static const char prefix[] = "OpenVPNDNSRouting";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public:
|
||||
class ActionCreate : public Action
|
||||
{
|
||||
public:
|
||||
ActionCreate(const std::vector<std::string>& names_arg,
|
||||
const std::vector<std::string>& dns_servers_arg)
|
||||
: names(names_arg),
|
||||
dns_servers(dns_servers_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& log) override
|
||||
{
|
||||
log << to_string() << std::endl;
|
||||
create_rule(names, dns_servers);
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "NRPT::ActionCreate"
|
||||
<< " names=[" << string::join(names, ",") << "]"
|
||||
<< " dns_servers=[" << string::join(dns_servers, ",") << "]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::string> names;
|
||||
const std::vector<std::string> dns_servers;
|
||||
};
|
||||
|
||||
class ActionDelete : public Action
|
||||
{
|
||||
public:
|
||||
virtual void execute(std::ostream& log) override
|
||||
{
|
||||
log << to_string() << std::endl;
|
||||
delete_rule();
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
return "NRPT::ActionDelete";
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <openvpn/buffer/bufhex.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/win/scoped_handle.hpp>
|
||||
#include <openvpn/win/event.hpp>
|
||||
|
||||
#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
#define WINTUN_RING_CAPACITY 0x800000
|
||||
#define WINTUN_RING_TRAILING_BYTES 0x10000
|
||||
#define WINTUN_RING_FRAMING_SIZE 12
|
||||
#define WINTUN_MAX_PACKET_SIZE 0xffff
|
||||
#define WINTUN_PACKET_ALIGN 4
|
||||
|
||||
namespace openvpn
|
||||
{
|
||||
namespace TunWin
|
||||
{
|
||||
struct TUN_RING {
|
||||
std::atomic_ulong head;
|
||||
std::atomic_ulong tail;
|
||||
std::atomic_long alertable;
|
||||
UCHAR data[WINTUN_RING_CAPACITY + WINTUN_RING_TRAILING_BYTES + WINTUN_RING_FRAMING_SIZE];
|
||||
};
|
||||
|
||||
struct TUN_REGISTER_RINGS
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG ring_size;
|
||||
TUN_RING* ring;
|
||||
HANDLE tail_moved;
|
||||
} send, receive;
|
||||
};
|
||||
|
||||
typedef openvpn_io::windows::object_handle AsioEvent;
|
||||
|
||||
class RingBuffer : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<RingBuffer> Ptr;
|
||||
|
||||
RingBuffer(openvpn_io::io_context& io_context)
|
||||
: send_ring_hmem(CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TUN_RING), NULL)),
|
||||
receive_ring_hmem(CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TUN_RING), NULL)),
|
||||
send_tail_moved_asio_event_(io_context)
|
||||
{
|
||||
// sanity checks
|
||||
static_assert((sizeof(TUN_RING) - sizeof(TUN_RING::data)) == 12, "sizeof(TUN_RING) is expected to be 12");
|
||||
#if !defined(ATOMIC_LONG_LOCK_FREE) || (ATOMIC_LONG_LOCK_FREE != 2)
|
||||
#error Atomic long is expected to be always lock-free
|
||||
#endif
|
||||
|
||||
send_ring_ = (TUN_RING*)MapViewOfFile(send_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING));
|
||||
receive_ring_ = (TUN_RING*)MapViewOfFile(receive_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING));
|
||||
send_tail_moved_asio_event_.assign(send_ring_tail_moved_());
|
||||
}
|
||||
|
||||
RingBuffer(openvpn_io::io_context& io_context,
|
||||
HANDLE client_process,
|
||||
const std::string& send_ring_hmem_hex,
|
||||
const std::string& receive_ring_hmem_hex,
|
||||
const std::string& send_ring_tail_moved_hex,
|
||||
const std::string& receive_ring_tail_moved_hex)
|
||||
: send_tail_moved_asio_event_(io_context)
|
||||
{
|
||||
HANDLE remote_handle = BufHex::parse<HANDLE>(send_ring_hmem_hex, "send_ring_hmem");
|
||||
HANDLE handle;
|
||||
DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
send_ring_hmem.reset(handle);
|
||||
|
||||
remote_handle = BufHex::parse<HANDLE>(receive_ring_hmem_hex, "receive_ring_hmem");
|
||||
DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
receive_ring_hmem.reset(handle);
|
||||
|
||||
remote_handle = BufHex::parse<HANDLE>(send_ring_tail_moved_hex, "send_ring_tail_moved");
|
||||
DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
send_ring_tail_moved_.reset(handle);
|
||||
|
||||
remote_handle = BufHex::parse<HANDLE>(receive_ring_tail_moved_hex, "receive_ring_tail_moved");
|
||||
DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
receive_ring_tail_moved_.reset(handle);
|
||||
|
||||
send_ring_ = (TUN_RING*)MapViewOfFile(send_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING));
|
||||
receive_ring_ = (TUN_RING*)MapViewOfFile(receive_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING));
|
||||
}
|
||||
|
||||
RingBuffer(RingBuffer const&) = delete;
|
||||
RingBuffer& operator=(RingBuffer const&) = delete;
|
||||
|
||||
~RingBuffer()
|
||||
{
|
||||
UnmapViewOfFile(send_ring_);
|
||||
UnmapViewOfFile(receive_ring_);
|
||||
}
|
||||
|
||||
HANDLE send_ring_tail_moved()
|
||||
{
|
||||
return send_ring_tail_moved_();
|
||||
}
|
||||
|
||||
HANDLE receive_ring_tail_moved()
|
||||
{
|
||||
return receive_ring_tail_moved_();
|
||||
}
|
||||
|
||||
TUN_RING* send_ring()
|
||||
{
|
||||
return send_ring_;
|
||||
}
|
||||
|
||||
TUN_RING* receive_ring()
|
||||
{
|
||||
return receive_ring_;
|
||||
}
|
||||
|
||||
AsioEvent& send_tail_moved_asio_event()
|
||||
{
|
||||
return send_tail_moved_asio_event_;
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
void serialize(Json::Value& json)
|
||||
{
|
||||
json["send_ring_hmem"] = BufHex::render(send_ring_hmem());
|
||||
json["receive_ring_hmem"] = BufHex::render(receive_ring_hmem());
|
||||
json["send_ring_tail_moved"] = BufHex::render(send_ring_tail_moved());
|
||||
json["receive_ring_tail_moved"] = BufHex::render(receive_ring_tail_moved());
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Win::ScopedHANDLE send_ring_hmem;
|
||||
Win::ScopedHANDLE receive_ring_hmem;
|
||||
Win::Event send_ring_tail_moved_{FALSE};
|
||||
Win::Event receive_ring_tail_moved_{FALSE};
|
||||
AsioEvent send_tail_moved_asio_event_;
|
||||
|
||||
TUN_RING* send_ring_ = nullptr;
|
||||
TUN_RING* receive_ring_ = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,314 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TUN_WIN_WFP_H
|
||||
#define OPENVPN_TUN_WIN_WFP_H
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/wstring.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
#include <openvpn/tun/win/tunutil.hpp>
|
||||
#include <openvpn/win/winerr.hpp>
|
||||
|
||||
#include <fwpmu.h>
|
||||
#include <fwpmtypes.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
class WFP : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<WFP> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(wfp_error);
|
||||
|
||||
// Block DNS from all apps except openvpn_app_path and
|
||||
// from all interfaces except tap_index.
|
||||
// Derived from https://github.com/ValdikSS/openvpn-with-patches/commit/3bd4d503d21aa34636e4f97b3e32ae0acca407f0
|
||||
void block_dns(const std::wstring& openvpn_app_path,
|
||||
const NET_IFINDEX tap_index,
|
||||
std::ostream& log)
|
||||
{
|
||||
// WFP filter/conditions
|
||||
FWPM_FILTER0 filter = {0};
|
||||
FWPM_FILTER_CONDITION0 condition[2] = {0};
|
||||
UINT64 filterid = 0;
|
||||
|
||||
// Get NET_LUID object for adapter
|
||||
NET_LUID tap_luid = adapter_index_to_luid(tap_index);
|
||||
|
||||
// Get app ID
|
||||
unique_ptr_del<FWP_BYTE_BLOB> openvpn_app_id_blob = get_app_id_blob(openvpn_app_path);
|
||||
|
||||
// Populate packet filter layer information
|
||||
{
|
||||
FWPM_SUBLAYER0 subLayer = {0};
|
||||
subLayer.subLayerKey = subLayerGUID;
|
||||
subLayer.displayData.name = L"OpenVPN";
|
||||
subLayer.displayData.description = L"OpenVPN";
|
||||
subLayer.flags = 0;
|
||||
subLayer.weight = 0x100;
|
||||
|
||||
// Add packet filter to interface
|
||||
const DWORD status = ::FwpmSubLayerAdd0(engineHandle(), &subLayer, NULL);
|
||||
if (status != ERROR_SUCCESS)
|
||||
OPENVPN_THROW(wfp_error, "FwpmSubLayerAdd0 failed with status=0x" << std::hex << status);
|
||||
}
|
||||
|
||||
// Prepare filter
|
||||
filter.subLayerKey = subLayerGUID;
|
||||
filter.displayData.name = L"OpenVPN";
|
||||
filter.weight.type = FWP_UINT8;
|
||||
filter.weight.uint8 = 0xF;
|
||||
filter.filterCondition = condition;
|
||||
|
||||
// Filter #1 -- permit IPv4 DNS requests from OpenVPN app
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_PERMIT;
|
||||
filter.numFilterConditions = 2;
|
||||
|
||||
condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||
condition[0].matchType = FWP_MATCH_EQUAL;
|
||||
condition[0].conditionValue.type = FWP_UINT16;
|
||||
condition[0].conditionValue.uint16 = 53;
|
||||
|
||||
condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
|
||||
condition[1].matchType = FWP_MATCH_EQUAL;
|
||||
condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
|
||||
condition[1].conditionValue.byteBlob = openvpn_app_id_blob.get();
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv4 DNS requests from OpenVPN app" << std::endl;
|
||||
|
||||
// Filter #2 -- permit IPv6 DNS requests from OpenVPN app
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv6 DNS requests from OpenVPN app" << std::endl;
|
||||
|
||||
// Filter #3 -- block IPv4 DNS requests from other apps
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_BLOCK;
|
||||
filter.weight.type = FWP_EMPTY;
|
||||
filter.numFilterConditions = 1;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "block IPv4 DNS requests from other apps" << std::endl;
|
||||
|
||||
// Filter #4 -- block IPv6 DNS requests from other apps
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "block IPv6 DNS requests from other apps" << std::endl;
|
||||
|
||||
// Filter #5 -- allow IPv4 traffic from TAP
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_PERMIT;
|
||||
filter.numFilterConditions = 2;
|
||||
|
||||
condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
|
||||
condition[1].matchType = FWP_MATCH_EQUAL;
|
||||
condition[1].conditionValue.type = FWP_UINT64;
|
||||
condition[1].conditionValue.uint64 = &tap_luid.Value;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "allow IPv4 traffic from TAP" << std::endl;
|
||||
|
||||
// Filter #6 -- allow IPv6 traffic from TAP
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "allow IPv6 traffic from TAP" << std::endl;
|
||||
}
|
||||
|
||||
void reset(std::ostream& log)
|
||||
{
|
||||
engineHandle.reset(&log);
|
||||
}
|
||||
|
||||
private:
|
||||
class WFPEngine
|
||||
{
|
||||
public:
|
||||
WFPEngine()
|
||||
{
|
||||
FWPM_SESSION0 session = {0};
|
||||
|
||||
// delete all filters when engine handle is closed
|
||||
session.flags = FWPM_SESSION_FLAG_DYNAMIC;
|
||||
|
||||
const DWORD status = ::FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &handle);
|
||||
if (status != ERROR_SUCCESS)
|
||||
OPENVPN_THROW(wfp_error, "FwpmEngineOpen0 failed with status=0x" << std::hex << status);
|
||||
}
|
||||
|
||||
void reset(std::ostream* log)
|
||||
{
|
||||
if (defined())
|
||||
{
|
||||
const DWORD status = ::FwpmEngineClose0(handle);
|
||||
handle = NULL;
|
||||
if (log)
|
||||
{
|
||||
if (status != ERROR_SUCCESS)
|
||||
*log << "FwpmEngineClose0 failed, status=" << status << std::endl;
|
||||
else
|
||||
*log << "WFPEngine closed" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WFPEngine()
|
||||
{
|
||||
reset(nullptr);
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return Win::Handle::defined(handle);
|
||||
}
|
||||
|
||||
HANDLE operator()() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
WFPEngine(const WFPEngine&) = delete;
|
||||
WFPEngine& operator=(const WFPEngine&) = delete;
|
||||
|
||||
HANDLE handle = NULL;
|
||||
};
|
||||
|
||||
static GUID new_guid()
|
||||
{
|
||||
UUID ret;
|
||||
const RPC_STATUS status = ::UuidCreate(&ret);
|
||||
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
|
||||
throw wfp_error("UuidCreate failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static NET_LUID adapter_index_to_luid(const NET_IFINDEX index)
|
||||
{
|
||||
NET_LUID tap_luid;
|
||||
const NETIO_STATUS ret = ::ConvertInterfaceIndexToLuid(index, &tap_luid);
|
||||
if (ret != NO_ERROR)
|
||||
throw wfp_error("ConvertInterfaceIndexToLuid failed");
|
||||
return tap_luid;
|
||||
}
|
||||
|
||||
static unique_ptr_del<FWP_BYTE_BLOB> get_app_id_blob(const std::wstring& app_path)
|
||||
{
|
||||
FWP_BYTE_BLOB *blob;
|
||||
const DWORD status = ::FwpmGetAppIdFromFileName0(app_path.c_str(), &blob);
|
||||
if (status != ERROR_SUCCESS)
|
||||
OPENVPN_THROW(wfp_error, "FwpmGetAppIdFromFileName0 failed, status=0x" << std::hex << status);
|
||||
return unique_ptr_del<FWP_BYTE_BLOB>(blob, [](FWP_BYTE_BLOB* blob) {
|
||||
::FwpmFreeMemory0((void**)&blob);
|
||||
});
|
||||
}
|
||||
|
||||
void add_filter(const FWPM_FILTER0 *filter,
|
||||
PSECURITY_DESCRIPTOR sd,
|
||||
UINT64 *id)
|
||||
{
|
||||
const DWORD status = ::FwpmFilterAdd0(engineHandle(), filter, sd, id);
|
||||
if (status != ERROR_SUCCESS)
|
||||
OPENVPN_THROW(wfp_error, "FwpmFilterAdd0 failed, status=0x" << std::hex << status);
|
||||
}
|
||||
|
||||
const GUID subLayerGUID{new_guid()};
|
||||
WFPEngine engineHandle;
|
||||
};
|
||||
|
||||
class WFPContext : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<WFPContext> Ptr;
|
||||
|
||||
private:
|
||||
friend class ActionWFP;
|
||||
|
||||
void block(const std::wstring& openvpn_app_path,
|
||||
const NET_IFINDEX tap_index,
|
||||
std::ostream& log)
|
||||
{
|
||||
unblock(log);
|
||||
wfp.reset(new WFP());
|
||||
wfp->block_dns(openvpn_app_path, tap_index, log);
|
||||
}
|
||||
|
||||
void unblock(std::ostream& log)
|
||||
{
|
||||
if (wfp)
|
||||
{
|
||||
wfp->reset(log);
|
||||
wfp.reset();
|
||||
}
|
||||
}
|
||||
|
||||
WFP::Ptr wfp;
|
||||
};
|
||||
|
||||
class ActionWFP : public Action
|
||||
{
|
||||
public:
|
||||
ActionWFP(const std::wstring& openvpn_app_path_arg,
|
||||
const NET_IFINDEX tap_index_arg,
|
||||
const bool enable_arg,
|
||||
const WFPContext::Ptr& wfp_arg)
|
||||
: openvpn_app_path(openvpn_app_path_arg),
|
||||
tap_index(tap_index_arg),
|
||||
enable(enable_arg),
|
||||
wfp(wfp_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& log) override
|
||||
{
|
||||
log << to_string() << std::endl;
|
||||
if (enable)
|
||||
wfp->block(openvpn_app_path, tap_index, log);
|
||||
else
|
||||
wfp->unblock(log);
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
return "ActionWFP openvpn_app_path=" + wstring::to_utf8(openvpn_app_path) + " tap_index=" + std::to_string(tap_index) + " enable=" + std::to_string(enable);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::wstring openvpn_app_path;
|
||||
const NET_IFINDEX tap_index;
|
||||
const bool enable;
|
||||
WFPContext::Ptr wfp;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// proxy settings for Windows
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WinInet.h>
|
||||
|
||||
#include <openvpn\win\impersonate.hpp>
|
||||
#include <openvpn\tun\proxy.hpp>
|
||||
#include <openvpn\win\reg.hpp>
|
||||
|
||||
using namespace openvpn::Win;
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
class WinProxySettings : public ProxySettings {
|
||||
public:
|
||||
typedef RCPtr<WinProxySettings> Ptr;
|
||||
|
||||
WinProxySettings(const TunBuilderCapture::ProxyAutoConfigURL& config_arg)
|
||||
: ProxySettings(config_arg) { }
|
||||
|
||||
void set_proxy(bool del) override
|
||||
{
|
||||
Impersonate imp{false};
|
||||
|
||||
LONG status;
|
||||
RegKey hkcu;
|
||||
RegKey key;
|
||||
|
||||
status = ::RegOpenCurrentUser(KEY_QUERY_VALUE | KEY_SET_VALUE, hkcu.ref());
|
||||
check_reg_error<proxy_error>(status, "RegOpenCurrentUser");
|
||||
|
||||
status = ::RegCreateKeyExA(hkcu(), key_name, 0, NULL, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, key.ref(), NULL);
|
||||
check_reg_error<proxy_error>(status, key_name);
|
||||
|
||||
if (!del)
|
||||
{
|
||||
save_key(key, "AutoConfigURL", config.url, true);
|
||||
save_key(key, "ProxyEnable", "0", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
restore_key(key, "AutoConfigURL", true);
|
||||
restore_key(key, "ProxyEnable", false);
|
||||
}
|
||||
|
||||
// WinInet API cannot be called from service, even via impersonation
|
||||
if (!imp.is_local_system())
|
||||
{
|
||||
OPENVPN_LOG("Refresh proxy settings");
|
||||
|
||||
InternetSetOptionA(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
|
||||
InternetSetOptionA(NULL, INTERNET_OPTION_REFRESH, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void restore_key(Win::RegKey& regkey, const std::string& key, bool str)
|
||||
{
|
||||
LONG status;
|
||||
char prev_val_str[1024] = { 0 }; // should be enough to fit proxy URL
|
||||
DWORD prev_val_dword;
|
||||
DWORD prev_buf_size = str ? sizeof(prev_val_str) : sizeof(prev_val_dword);
|
||||
bool del = false;
|
||||
Win::RegKey hkcu;
|
||||
|
||||
status = ::RegOpenCurrentUser(KEY_QUERY_VALUE | KEY_SET_VALUE, hkcu.ref());
|
||||
check_reg_error<proxy_error>(status, "RegOpenCurrentUser");
|
||||
|
||||
// get previous value
|
||||
std::string prev_key_name = sname + key;
|
||||
status = ::RegGetValueA(hkcu(),
|
||||
key_name,
|
||||
prev_key_name.c_str(),
|
||||
str ? RRF_RT_REG_SZ : RRF_RT_REG_DWORD,
|
||||
NULL,
|
||||
str ? (PVOID)prev_val_str : (PVOID)&prev_val_dword,
|
||||
&prev_buf_size);
|
||||
check_reg_error<proxy_error>(status, prev_key_name);
|
||||
|
||||
RegDeleteValueA(regkey(), prev_key_name.c_str());
|
||||
|
||||
// check if previous value needs to be deleted
|
||||
if (str)
|
||||
del = strcmp(delete_value_str, prev_val_str) == 0;
|
||||
else
|
||||
del = prev_val_dword == delete_value_dword;
|
||||
|
||||
if (del)
|
||||
::RegDeleteValueA(regkey(), key.c_str());
|
||||
else
|
||||
::RegSetValueExA(regkey(),
|
||||
key.c_str(),
|
||||
0,
|
||||
str ? REG_SZ : REG_DWORD,
|
||||
str ? (const BYTE *)prev_val_str : (CONST BYTE *)&prev_val_dword,
|
||||
str ? strlen(prev_val_str) + 1 : sizeof(prev_val_dword));
|
||||
}
|
||||
|
||||
void save_key(Win::RegKey& regkey, const std::string& key, const std::string& value, bool str)
|
||||
{
|
||||
LONG status;
|
||||
char prev_val_str[1024] = { 0 }; // should be enought to fit proxy URL
|
||||
DWORD prev_val_dword;
|
||||
DWORD prev_buf_size = str ? sizeof(prev_val_str) : sizeof(prev_val_dword);
|
||||
Win::RegKey hkcu;
|
||||
|
||||
status = ::RegOpenCurrentUser(KEY_QUERY_VALUE | KEY_SET_VALUE, hkcu.ref());
|
||||
check_reg_error<proxy_error>(status, "RegOpenCurrentUser");
|
||||
|
||||
// get original value
|
||||
status = ::RegGetValueA(hkcu(),
|
||||
key_name,
|
||||
key.c_str(),
|
||||
str ? RRF_RT_REG_SZ : RRF_RT_REG_DWORD,
|
||||
NULL,
|
||||
str ? (PVOID)prev_val_str : (PVOID)&prev_val_dword,
|
||||
&prev_buf_size);
|
||||
switch (status)
|
||||
{
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
// mark that original value doesn't exist
|
||||
strcpy(prev_val_str, delete_value_str);
|
||||
prev_val_dword = delete_value_dword;
|
||||
case ERROR_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
check_reg_error<proxy_error>(status, key);
|
||||
break;
|
||||
}
|
||||
|
||||
// save original value
|
||||
std::string prev_key_name = sname + key;
|
||||
status = ::RegSetValueExA(regkey(),
|
||||
prev_key_name.c_str(),
|
||||
0,
|
||||
str ? REG_SZ : REG_DWORD,
|
||||
str ? (const BYTE *)prev_val_str : (CONST BYTE *)&prev_val_dword,
|
||||
str ? strlen(prev_val_str) + 1 : sizeof(DWORD));
|
||||
check_reg_error<proxy_error>(status, prev_key_name);
|
||||
|
||||
// save new value
|
||||
DWORD val_dword = 0;
|
||||
if (!str)
|
||||
val_dword = std::atol(value.c_str());
|
||||
status = ::RegSetValueExA(regkey(),
|
||||
key.c_str(),
|
||||
0,
|
||||
str ? REG_SZ : REG_DWORD,
|
||||
str ? (const BYTE *)value.c_str() : (CONST BYTE *)&val_dword,
|
||||
str ? value.length() + 1 : sizeof(val_dword));
|
||||
check_reg_error<proxy_error>(status, key);
|
||||
}
|
||||
|
||||
const char* key_name = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
||||
const char* delete_value_str = "DeleteValue";
|
||||
const DWORD delete_value_dword = 0xCAFEBABE;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user