mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Put OpenVPN adapter into separate framework and add libraries compiled for simulator
This commit is contained in:
@@ -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-2016 OpenVPN Technologies, 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_ADDR_ADDRLIST_H
|
||||
#define OPENVPN_ADDR_ADDRLIST_H
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
|
||||
// A list of unique IP addresses
|
||||
class AddrList : public std::vector<IP::Addr>, public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<AddrList> Ptr;
|
||||
|
||||
void add(const IP::Addr& a)
|
||||
{
|
||||
if (!exists(a))
|
||||
push_back(a);
|
||||
}
|
||||
|
||||
bool exists(const IP::Addr& a) const
|
||||
{
|
||||
for (const_iterator i = begin(); i != end(); ++i)
|
||||
{
|
||||
if (a == *i)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void dump() const
|
||||
{
|
||||
OPENVPN_LOG("******* AddrList::dump");
|
||||
for (const_iterator i = begin(); i != end(); ++i)
|
||||
OPENVPN_LOG(i->to_string());
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,218 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_ADDRPAIR_H
|
||||
#define OPENVPN_ADDR_ADDRPAIR_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
|
||||
// AddrMaskPair is basically an object that combines an IP address (v4 or v6)
|
||||
// with a netmask or prefix length.
|
||||
struct AddrMaskPair
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(addr_pair_mask_parse_error);
|
||||
|
||||
class StringPair {
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(addr_pair_string_error);
|
||||
|
||||
StringPair()
|
||||
: size_(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit StringPair(const std::string& s1)
|
||||
: size_(1)
|
||||
{
|
||||
data[0] = s1;
|
||||
}
|
||||
|
||||
explicit StringPair(const std::string& s1, const std::string& s2)
|
||||
: size_(2)
|
||||
{
|
||||
data[0] = s1;
|
||||
data[1] = s2;
|
||||
}
|
||||
|
||||
void push_back(const std::string& s)
|
||||
{
|
||||
if (size_ < 2)
|
||||
data[size_++] = s;
|
||||
else
|
||||
throw addr_pair_string_error();
|
||||
}
|
||||
|
||||
const std::string& operator[](const size_t i) const
|
||||
{
|
||||
if (i >= 2)
|
||||
throw addr_pair_string_error();
|
||||
return data[i];
|
||||
}
|
||||
|
||||
std::string& operator[](const size_t i)
|
||||
{
|
||||
if (i >= 2)
|
||||
throw addr_pair_string_error();
|
||||
return data[i];
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
std::string render() const
|
||||
{
|
||||
switch (size_)
|
||||
{
|
||||
case 1:
|
||||
return data[0];
|
||||
case 2:
|
||||
return data[0] + "/" + data[1];
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string data[2];
|
||||
unsigned int size_;
|
||||
};
|
||||
|
||||
static AddrMaskPair from_string(const std::string& s1, const std::string& s2, const char *title = nullptr)
|
||||
{
|
||||
try {
|
||||
if (s2.empty())
|
||||
{
|
||||
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s1, '/');
|
||||
return from_string_impl(pair, title);
|
||||
}
|
||||
else
|
||||
{
|
||||
const StringPair pair(s1, s2);
|
||||
return from_string_impl(pair, title);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
const StringPair pair(s1, s2);
|
||||
error(e, pair.render(), title);
|
||||
}
|
||||
return AddrMaskPair(); // NOTREACHED
|
||||
}
|
||||
|
||||
static AddrMaskPair from_string(const std::string& s, const char *title = nullptr)
|
||||
{
|
||||
try {
|
||||
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s, '/');
|
||||
return from_string_impl(pair, title);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error(e, s, title);
|
||||
}
|
||||
return AddrMaskPair(); // NOTREACHED
|
||||
}
|
||||
|
||||
static AddrMaskPair from_string(const StringPair& pair, const char *title = nullptr)
|
||||
{
|
||||
try {
|
||||
return from_string_impl(pair, title);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error(e, pair.render(), title);
|
||||
}
|
||||
return AddrMaskPair(); // NOTREACHED
|
||||
}
|
||||
|
||||
std::string to_string(const bool netmask_form=false) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
if (netmask_form)
|
||||
os << addr.to_string() << '/' << netmask.to_string();
|
||||
else
|
||||
os << addr.to_string() << '/' << netmask.prefix_len();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool is_canonical() const
|
||||
{
|
||||
return (addr & netmask) == addr;
|
||||
}
|
||||
|
||||
Addr::Version version() const
|
||||
{
|
||||
const Addr::Version v1 = addr.version();
|
||||
const Addr::Version v2 = netmask.version();
|
||||
if (v1 == v2)
|
||||
return v1;
|
||||
else
|
||||
return Addr::UNSPEC;
|
||||
}
|
||||
|
||||
Addr addr;
|
||||
Addr netmask;
|
||||
|
||||
private:
|
||||
static void error(const std::exception& e, const std::string& s, const char *title)
|
||||
{
|
||||
if (!title)
|
||||
title = "";
|
||||
OPENVPN_THROW(addr_pair_mask_parse_error, "AddrMaskPair parse error '" << title << "': " << s << " : " << e.what());
|
||||
}
|
||||
|
||||
static AddrMaskPair from_string_impl(const StringPair& pair, const char *title = nullptr)
|
||||
{
|
||||
AddrMaskPair ret;
|
||||
if (pair.size() == 1 || pair.size() == 2)
|
||||
{
|
||||
ret.addr = Addr::from_string(pair[0], title);
|
||||
if (pair.size() == 2 && !pair[1].empty())
|
||||
{
|
||||
if (is_number(pair[1].c_str()))
|
||||
ret.netmask = Addr::netmask_from_prefix_len(ret.addr.version(),
|
||||
parse_number_throw<unsigned int>(pair[1], "prefix length"));
|
||||
else
|
||||
ret.netmask = Addr::from_string(pair[1]);
|
||||
ret.netmask.prefix_len(); // verify that netmask is ok
|
||||
}
|
||||
else
|
||||
ret.netmask = Addr::from_zero_complement(ret.addr.version());
|
||||
ret.addr.verify_version_consistency(ret.netmask);
|
||||
}
|
||||
else
|
||||
throw addr_pair_mask_parse_error("only one or two address terms allowed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
OPENVPN_OSTREAM(AddrMaskPair, to_string)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,895 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_IP_H
|
||||
#define OPENVPN_ADDR_IP_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring> // for std::memset
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/ostream.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/ipv4.hpp>
|
||||
#include <openvpn/addr/ipv6.hpp>
|
||||
#include <openvpn/addr/iperr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// This is our fundamental IP address class that handles IPv4 or IPv6
|
||||
// IP addresses. It is implemented as a discriminated union of IPv4::Addr
|
||||
// and IPv6::Addr.
|
||||
namespace IP {
|
||||
|
||||
OPENVPN_EXCEPTION(ip_exception);
|
||||
|
||||
class Addr
|
||||
{
|
||||
public:
|
||||
enum Version { UNSPEC, V4, V6 };
|
||||
|
||||
enum { V4_MASK=(1<<0), V6_MASK=(1<<1) };
|
||||
typedef unsigned int VersionMask;
|
||||
|
||||
enum VersionSize {
|
||||
V4_SIZE = IPv4::Addr::SIZE,
|
||||
V6_SIZE = IPv6::Addr::SIZE,
|
||||
};
|
||||
|
||||
Addr(const Addr& other, const char *title = nullptr, Version required_version = UNSPEC)
|
||||
: ver(other.ver)
|
||||
{
|
||||
other.validate_version(title, required_version);
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
u.v4 = other.u.v4;
|
||||
break;
|
||||
case V6:
|
||||
u.v6 = other.u.v6;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Addr(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC)
|
||||
: Addr(from_string(ipstr, title, required_version))
|
||||
{
|
||||
}
|
||||
|
||||
Addr(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC)
|
||||
: Addr(from_string(ipstr, title.c_str(), required_version))
|
||||
{
|
||||
}
|
||||
|
||||
void validate_version(const char *title, Version required_version) const
|
||||
{
|
||||
if (required_version != UNSPEC && required_version != ver)
|
||||
throw ip_exception(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version"));
|
||||
}
|
||||
|
||||
void validate_version(const std::string& title, Version required_version) const
|
||||
{
|
||||
validate_version(title.c_str(), required_version);
|
||||
}
|
||||
|
||||
static std::string validate(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC)
|
||||
{
|
||||
Addr a = from_string(ipstr, title, required_version);
|
||||
return a.to_string();
|
||||
}
|
||||
|
||||
static std::string validate(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC)
|
||||
{
|
||||
return validate(ipstr, title.c_str(), required_version);
|
||||
}
|
||||
|
||||
static bool is_valid(const std::string& ipstr)
|
||||
{
|
||||
// fast path -- rule out validity if invalid chars
|
||||
for (size_t i = 0; i < ipstr.length(); ++i)
|
||||
{
|
||||
const char c = ipstr[i];
|
||||
if (!((c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F')
|
||||
|| (c == '.' || c == ':' || c == '%')))
|
||||
return false;
|
||||
}
|
||||
|
||||
// slow path
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::make_address(ipstr, ec);
|
||||
return !ec;
|
||||
}
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC)
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::address a = asio::ip::make_address(ipstr, ec);
|
||||
if (ec)
|
||||
throw ip_exception(internal::format_error(ipstr, title, "", ec));
|
||||
const Addr ret = from_asio(a);
|
||||
if (required_version != UNSPEC && required_version != ret.ver)
|
||||
throw ip_exception(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_hex(Version v, const std::string& s)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_hex(s));
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_hex(s));
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
static Addr from_ipv4(const IPv4::Addr& addr)
|
||||
{
|
||||
Addr a;
|
||||
a.ver = V4;
|
||||
a.u.v4 = addr;
|
||||
return a;
|
||||
}
|
||||
|
||||
static Addr from_ipv6(const IPv6::Addr& addr)
|
||||
{
|
||||
Addr a;
|
||||
a.ver = V6;
|
||||
a.u.v6 = addr;
|
||||
return a;
|
||||
}
|
||||
|
||||
const IPv4::Addr& to_ipv4() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4;
|
||||
else
|
||||
throw ip_exception("address is not IPv4");
|
||||
}
|
||||
|
||||
const IPv6::Addr& to_ipv6() const
|
||||
{
|
||||
if (ver == V6)
|
||||
return u.v6;
|
||||
else
|
||||
throw ip_exception("address is not IPv6");
|
||||
}
|
||||
|
||||
const IPv4::Addr& to_ipv4_nocheck() const
|
||||
{
|
||||
return u.v4;
|
||||
}
|
||||
|
||||
const IPv6::Addr& to_ipv6_nocheck() const
|
||||
{
|
||||
return u.v6;
|
||||
}
|
||||
|
||||
static Addr from_sockaddr(const struct sockaddr *sa)
|
||||
{
|
||||
if (sa->sa_family == AF_INET)
|
||||
return from_ipv4(IPv4::Addr::from_sockaddr((struct sockaddr_in *)sa));
|
||||
else if (sa->sa_family == AF_INET6)
|
||||
return from_ipv6(IPv6::Addr::from_sockaddr((struct sockaddr_in6 *)sa));
|
||||
else
|
||||
return Addr();
|
||||
}
|
||||
|
||||
static bool sockaddr_defined(const struct sockaddr *sa)
|
||||
{
|
||||
return sa && (sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
|
||||
}
|
||||
|
||||
static Addr from_ulong(Version v, unsigned long ul)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_ulong(ul));
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_ulong(ul));
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// return *this as a ulong, will raise exception on overflow
|
||||
unsigned long to_ulong() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4.to_ulong();
|
||||
else if (ver == V6)
|
||||
return u.v6.to_ulong();
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
static Addr from_long(Version v, long ul)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_long(ul));
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_long(ul));
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// return *this as a long, will raise exception on overflow
|
||||
long to_long() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4.to_long();
|
||||
else if (ver == V6)
|
||||
return u.v6.to_long();
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// return Addr from 16 byte binary string
|
||||
static Addr from_byte_string(const unsigned char *bytestr)
|
||||
{
|
||||
Addr a;
|
||||
if (IPv6::Addr::byte_string_is_v4(bytestr))
|
||||
{
|
||||
a.ver = V4;
|
||||
a.u.v4 = IPv4::Addr::from_uint32_net(IPv6::Addr::v4_from_byte_string(bytestr));
|
||||
}
|
||||
else
|
||||
{
|
||||
a.ver = V4;
|
||||
a.u.v6 = IPv6::Addr::from_byte_string(bytestr);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// convert Addr to 16 byte binary string
|
||||
void to_byte_string(unsigned char *bytestr) const
|
||||
{
|
||||
if (ver == V4)
|
||||
IPv6::Addr::v4_to_byte_string(bytestr, u.v4.to_uint32_net());
|
||||
else if (ver == V6)
|
||||
u.v6.to_byte_string(bytestr);
|
||||
else
|
||||
std::memset(bytestr, 0, 16);
|
||||
}
|
||||
|
||||
std::uint32_t to_uint32_net() const // return value in net byte order
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4.to_uint32_net();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// construct an address where all bits are zero
|
||||
static Addr from_zero(Version v)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_zero());
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_zero());
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// construct an address where all bits are zero
|
||||
static Addr from_one(Version v)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_one());
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_one());
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// construct an address where all bits are one
|
||||
static Addr from_zero_complement(Version v)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::from_zero_complement());
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::from_zero_complement());
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// build a netmask using given prefix_len
|
||||
static Addr netmask_from_prefix_len(Version v, const unsigned int prefix_len)
|
||||
{
|
||||
if (v == V4)
|
||||
return from_ipv4(IPv4::Addr::netmask_from_prefix_len(prefix_len));
|
||||
else if (v == V6)
|
||||
return from_ipv6(IPv6::Addr::netmask_from_prefix_len(prefix_len));
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
// build a netmask using *this as extent
|
||||
Addr netmask_from_extent() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return from_ipv4(u.v4.netmask_from_extent());
|
||||
else if (ver == V6)
|
||||
return from_ipv6(u.v6.netmask_from_extent());
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
if (ver != UNSPEC)
|
||||
{
|
||||
const asio::ip::address a = to_asio();
|
||||
std::string ret = a.to_string();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
return "UNSPEC";
|
||||
}
|
||||
|
||||
std::string to_string_bracket_ipv6() const
|
||||
{
|
||||
std::string ret;
|
||||
if (ver == V6)
|
||||
ret += '[';
|
||||
ret += to_string();
|
||||
if (ver == V6)
|
||||
ret += ']';
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string to_hex() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4.to_hex();
|
||||
else if (ver == V6)
|
||||
return u.v6.to_hex();
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
std::string arpa() const
|
||||
{
|
||||
if (ver == V4)
|
||||
return u.v4.arpa();
|
||||
else if (ver == V6)
|
||||
return u.v6.arpa();
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
static Addr from_asio(const asio::ip::address& addr)
|
||||
{
|
||||
if (addr.is_v4())
|
||||
{
|
||||
Addr a;
|
||||
a.ver = V4;
|
||||
a.u.v4 = IPv4::Addr::from_asio(addr.to_v4());
|
||||
return a;
|
||||
}
|
||||
else if (addr.is_v6())
|
||||
{
|
||||
Addr a;
|
||||
a.ver = V6;
|
||||
a.u.v6 = IPv6::Addr::from_asio(addr.to_v6());
|
||||
return a;
|
||||
}
|
||||
else
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
|
||||
asio::ip::address to_asio() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return asio::ip::address_v4(u.v4.to_asio());
|
||||
case V6:
|
||||
return asio::ip::address_v6(u.v6.to_asio());
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
Addr operator+(const long delta) const {
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V4;
|
||||
ret.u.v4 = u.v4 + delta;
|
||||
return ret;
|
||||
}
|
||||
case V6:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V6;
|
||||
ret.u.v6 = u.v6 + delta;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
Addr operator-(const long delta) const {
|
||||
return operator+(-delta);
|
||||
}
|
||||
|
||||
#define OPENVPN_IP_OPERATOR_BINOP(OP) \
|
||||
Addr operator OP (const Addr& other) const { \
|
||||
if (ver != other.ver) \
|
||||
throw ip_exception("version inconsistency"); \
|
||||
switch (ver) \
|
||||
{ \
|
||||
case V4: \
|
||||
{ \
|
||||
Addr ret; \
|
||||
ret.ver = V4; \
|
||||
ret.u.v4 = u.v4 OP other.u.v4; \
|
||||
return ret; \
|
||||
} \
|
||||
case V6: \
|
||||
{ \
|
||||
Addr ret; \
|
||||
ret.ver = V6; \
|
||||
ret.u.v6 = u.v6 OP other.u.v6; \
|
||||
return ret; \
|
||||
} \
|
||||
default: \
|
||||
throw ip_exception("address unspecified"); \
|
||||
} \
|
||||
}
|
||||
|
||||
OPENVPN_IP_OPERATOR_BINOP(+)
|
||||
OPENVPN_IP_OPERATOR_BINOP(-)
|
||||
OPENVPN_IP_OPERATOR_BINOP(*)
|
||||
OPENVPN_IP_OPERATOR_BINOP(/)
|
||||
OPENVPN_IP_OPERATOR_BINOP(%)
|
||||
OPENVPN_IP_OPERATOR_BINOP(&)
|
||||
OPENVPN_IP_OPERATOR_BINOP(|)
|
||||
|
||||
#undef OPENVPN_IP_OPERATOR_BINOP
|
||||
|
||||
Addr operator<<(const unsigned int shift) const {
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V4;
|
||||
ret.u.v4 = u.v4 << shift;
|
||||
return ret;
|
||||
}
|
||||
case V6:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V6;
|
||||
ret.u.v6 = u.v6 << shift;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
Addr operator>>(const unsigned int shift) const {
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V4;
|
||||
ret.u.v4 = u.v4 >> shift;
|
||||
return ret;
|
||||
}
|
||||
case V6:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V6;
|
||||
ret.u.v6 = u.v6 >> shift;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
Addr operator~() const {
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V4;
|
||||
ret.u.v4 = ~u.v4;
|
||||
return ret;
|
||||
}
|
||||
case V6:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V6;
|
||||
ret.u.v6 = ~u.v6;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
Addr network_addr(const unsigned int prefix_len) const {
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V4;
|
||||
ret.u.v4 = u.v4.network_addr(prefix_len);
|
||||
return ret;
|
||||
}
|
||||
case V6:
|
||||
{
|
||||
Addr ret;
|
||||
ret.ver = V6;
|
||||
ret.u.v6 = u.v6.network_addr(prefix_len);
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Addr& other) const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case UNSPEC:
|
||||
return other.ver == UNSPEC;
|
||||
case V4:
|
||||
if (ver == other.ver)
|
||||
return u.v4 == other.u.v4;
|
||||
break;
|
||||
case V6:
|
||||
if (ver == other.ver)
|
||||
return u.v6 == other.u.v6;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const Addr& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
#define OPENVPN_IP_OPERATOR_REL(OP) \
|
||||
bool operator OP(const Addr& other) const \
|
||||
{ \
|
||||
if (ver == other.ver) \
|
||||
{ \
|
||||
switch (ver) \
|
||||
{ \
|
||||
case V4: \
|
||||
return u.v4 OP other.u.v4; \
|
||||
case V6: \
|
||||
return u.v6 OP other.u.v6; \
|
||||
default: \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
else if (ver OP other.ver) \
|
||||
return true; \
|
||||
else \
|
||||
return false; \
|
||||
}
|
||||
|
||||
OPENVPN_IP_OPERATOR_REL(<)
|
||||
OPENVPN_IP_OPERATOR_REL(>)
|
||||
OPENVPN_IP_OPERATOR_REL(<=)
|
||||
OPENVPN_IP_OPERATOR_REL(>=)
|
||||
|
||||
#undef OPENVPN_IP_OPERATOR_REL
|
||||
|
||||
bool unspecified() const
|
||||
{
|
||||
return all_zeros();
|
||||
}
|
||||
|
||||
bool specified() const
|
||||
{
|
||||
return !unspecified();
|
||||
}
|
||||
|
||||
bool all_zeros() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return u.v4.all_zeros();
|
||||
case V6:
|
||||
return u.v6.all_zeros();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool all_ones() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return u.v4.all_ones();
|
||||
case V6:
|
||||
return u.v6.all_ones();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return ver != UNSPEC;
|
||||
}
|
||||
|
||||
const char *version_string() const
|
||||
{
|
||||
return version_string_static(ver);
|
||||
}
|
||||
|
||||
static const char *version_string_static(Version ver)
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return "v4";
|
||||
case V6:
|
||||
return "v6";
|
||||
default:
|
||||
return "v?";
|
||||
}
|
||||
}
|
||||
|
||||
Version version() const { return ver; }
|
||||
|
||||
static VersionMask version_mask(const Version ver)
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return V4_MASK;
|
||||
case V6:
|
||||
return V6_MASK;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
VersionMask version_mask() const
|
||||
{
|
||||
return version_mask(ver);
|
||||
}
|
||||
|
||||
bool is_compatible(const Addr& other) const
|
||||
{
|
||||
return ver == other.ver;
|
||||
}
|
||||
|
||||
void verify_version_consistency(const Addr& other) const
|
||||
{
|
||||
if (!is_compatible(other))
|
||||
throw ip_exception("version inconsistency");
|
||||
}
|
||||
|
||||
// throw exception if address is not a valid netmask
|
||||
void validate_netmask()
|
||||
{
|
||||
prefix_len();
|
||||
}
|
||||
|
||||
// number of network bits in netmask,
|
||||
// throws exception if addr is not a netmask
|
||||
unsigned int prefix_len() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return u.v4.prefix_len();
|
||||
case V6:
|
||||
return u.v6.prefix_len();
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6 scope ID or -1 if not IPv6
|
||||
int scope_id() const
|
||||
{
|
||||
return ver == V6 ? u.v6.scope_id() : -1;
|
||||
}
|
||||
|
||||
// number of host bits in netmask
|
||||
unsigned int host_len() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return u.v4.host_len();
|
||||
case V6:
|
||||
return u.v6.host_len();
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
// return the number of host addresses contained within netmask
|
||||
Addr extent_from_netmask() const
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
return from_ipv4(u.v4.extent_from_netmask());
|
||||
case V6:
|
||||
return from_ipv6(u.v6.extent_from_netmask());
|
||||
default:
|
||||
throw ip_exception("address unspecified");
|
||||
}
|
||||
}
|
||||
|
||||
// address size in bits
|
||||
unsigned int size() const
|
||||
{
|
||||
return version_size(ver);
|
||||
}
|
||||
|
||||
// address size in bits of particular IP version
|
||||
static unsigned int version_size(Version v)
|
||||
{
|
||||
if (v == V4)
|
||||
return IPv4::Addr::SIZE;
|
||||
else if (v == V6)
|
||||
return IPv6::Addr::SIZE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t hashval() const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
switch (ver)
|
||||
{
|
||||
case Addr::V4:
|
||||
Hash::combine(seed, 4, u.v4);
|
||||
break;
|
||||
case Addr::V6:
|
||||
Hash::combine(seed, 6, u.v6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_IP_IMMUTABLE
|
||||
private:
|
||||
#endif
|
||||
|
||||
Addr()
|
||||
: ver(UNSPEC)
|
||||
{
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
ver = UNSPEC;
|
||||
}
|
||||
|
||||
Addr& operator=(const Addr& other)
|
||||
{
|
||||
switch (ver = other.ver)
|
||||
{
|
||||
case V4:
|
||||
u.v4 = other.u.v4;
|
||||
break;
|
||||
case V6:
|
||||
u.v6 = other.u.v6;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator++()
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
++u.v4;
|
||||
break;
|
||||
case V6:
|
||||
++u.v6;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator+=(const long delta)
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
u.v4 += delta;
|
||||
break;
|
||||
case V6:
|
||||
u.v6 += delta;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator-=(const long delta)
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case V4:
|
||||
u.v4 -= delta;
|
||||
break;
|
||||
case V6:
|
||||
u.v6 -= delta;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset_ipv4_from_uint32(const IPv4::Addr::base_type addr)
|
||||
{
|
||||
ver = V4;
|
||||
u.v4 = IPv4::Addr::from_uint32(addr);
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
IPv4::Addr v4;
|
||||
IPv6::Addr v6;
|
||||
} u;
|
||||
|
||||
Version ver;
|
||||
};
|
||||
|
||||
OPENVPN_OSTREAM(Addr, to_string)
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_HASH_METHOD(openvpn::IP::Addr, hashval);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_IPERR_H
|
||||
#define OPENVPN_ADDR_IPERR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
namespace internal {
|
||||
// Called internally by IP, IPv4, and IPv6 classes
|
||||
|
||||
inline std::string format_error(const std::string& ipstr, const char *title, const char *ipver, const asio::error_code& ec)
|
||||
{
|
||||
std::string err = "error parsing";
|
||||
if (title)
|
||||
{
|
||||
err += ' ';
|
||||
err += title;
|
||||
}
|
||||
err += " IP";
|
||||
err += ipver;
|
||||
err += " address '";
|
||||
err += ipstr;
|
||||
err += "' : ";
|
||||
err += ec.message();
|
||||
return err;
|
||||
}
|
||||
|
||||
inline std::string format_error(const std::string& ipstr, const char *title, const char *ipver, const char *message)
|
||||
{
|
||||
std::string err = "error parsing";
|
||||
if (title)
|
||||
{
|
||||
err += ' ';
|
||||
err += title;
|
||||
}
|
||||
err += " IP";
|
||||
err += ipver;
|
||||
err += " address '";
|
||||
err += ipstr;
|
||||
err += '\'';
|
||||
if (message)
|
||||
{
|
||||
err += " : ";
|
||||
err += message;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_IPV4_H
|
||||
#define OPENVPN_ADDR_IPV4_H
|
||||
|
||||
#include <cstring> // for std::memcpy, std::memset
|
||||
#include <sstream>
|
||||
#include <cstdint> // for std::uint32_t
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/endian.hpp>
|
||||
#include <openvpn/common/ostream.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/ffs.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/iperr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
class Addr;
|
||||
}
|
||||
|
||||
// Fundamental classes for representing an IPv4 IP address.
|
||||
|
||||
namespace IPv4 {
|
||||
|
||||
OPENVPN_EXCEPTION(ipv4_exception);
|
||||
|
||||
class Addr // NOTE: must be union-legal, so default constructor does not initialize
|
||||
{
|
||||
friend class IP::Addr;
|
||||
|
||||
public:
|
||||
enum { SIZE=32 };
|
||||
|
||||
typedef std::uint32_t base_type;
|
||||
typedef std::int32_t signed_base_type;
|
||||
|
||||
static Addr from_addr(const Addr& addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static Addr from_in_addr(const struct in_addr *in4)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = ntohl(in4->s_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct in_addr to_in_addr() const
|
||||
{
|
||||
struct in_addr ret;
|
||||
ret.s_addr = htonl(u.addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_sockaddr(const struct sockaddr_in *sa)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = ntohl(sa->sin_addr.s_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in to_sockaddr() const
|
||||
{
|
||||
struct sockaddr_in ret;
|
||||
std::memset(&ret, 0, sizeof(ret));
|
||||
ret.sin_family = AF_INET;
|
||||
ret.sin_port = 0;
|
||||
ret.sin_addr.s_addr = htonl(u.addr);;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_uint32(const base_type addr) // host byte order
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::uint32_t to_uint32() const // host byte order
|
||||
{
|
||||
return u.addr;
|
||||
}
|
||||
|
||||
static Addr from_uint32_net(const base_type addr) // addr in net byte order
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = ntohl(addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::uint32_t to_uint32_net() const // return value in net byte order
|
||||
{
|
||||
return htonl(u.addr);
|
||||
}
|
||||
|
||||
static Addr from_ulong(unsigned long ul)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = (base_type)ul;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return *this as a unsigned long
|
||||
unsigned long to_ulong() const
|
||||
{
|
||||
return (unsigned long)u.addr;
|
||||
}
|
||||
|
||||
static Addr from_long(long ul)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = (base_type)(signed_base_type)ul;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return *this as a long
|
||||
long to_long() const
|
||||
{
|
||||
return (long)(signed_base_type)u.addr;
|
||||
}
|
||||
|
||||
static Addr from_bytes(const unsigned char *bytes) // host byte order
|
||||
{
|
||||
Addr ret;
|
||||
std::memcpy(ret.u.bytes, bytes, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_bytes_net(const unsigned char *bytes) // network byte order
|
||||
{
|
||||
Addr ret;
|
||||
std::memcpy(ret.u.bytes, bytes, 4);
|
||||
ret.u.addr = ntohl(ret.u.addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_zero()
|
||||
{
|
||||
Addr ret;
|
||||
ret.zero();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_one()
|
||||
{
|
||||
Addr ret;
|
||||
ret.one();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_zero_complement()
|
||||
{
|
||||
Addr ret;
|
||||
ret.zero_complement();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// build a netmask using given prefix_len
|
||||
static Addr netmask_from_prefix_len(const unsigned int prefix_len)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = prefix_len_to_netmask(prefix_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// build a netmask using given extent
|
||||
Addr netmask_from_extent() const
|
||||
{
|
||||
const int lb = find_last_set(u.addr - 1);
|
||||
return netmask_from_prefix_len(SIZE - lb);
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr, const char *title = nullptr)
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::address_v4 a = asio::ip::make_address_v4(ipstr, ec);
|
||||
if (ec)
|
||||
throw ipv4_exception(IP::internal::format_error(ipstr, title, "v4", ec));
|
||||
return from_asio(a);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const asio::ip::address_v4 a = to_asio();
|
||||
std::string ret = a.to_string();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_hex(const std::string& s)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = 0;
|
||||
size_t len = s.length();
|
||||
size_t base = 0;
|
||||
if (len > 0 && s[len-1] == 'L')
|
||||
len -= 1;
|
||||
if (len >= 2 && s[0] == '0' && s[1] == 'x')
|
||||
{
|
||||
base = 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (len < 1 || len > 8)
|
||||
throw ipv4_exception("parse hex error");
|
||||
size_t di = (len-1)>>1;
|
||||
for (int i = (len & 1) ? -1 : 0; i < int(len); i += 2)
|
||||
{
|
||||
const size_t idx = base + i;
|
||||
const int bh = (i >= 0) ? parse_hex_char(s[idx]) : 0;
|
||||
const int bl = parse_hex_char(s[idx+1]);
|
||||
if (bh == -1 || bl == -1)
|
||||
throw ipv4_exception("parse hex error");
|
||||
ret.u.bytes[Endian::e4(di--)] = (bh<<4) + bl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string to_hex() const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(8);
|
||||
bool firstnonzero = false;
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
const unsigned char b = u.bytes[Endian::e4rev(i)];
|
||||
if (b || firstnonzero || i == 3)
|
||||
{
|
||||
const char bh = b >> 4;
|
||||
if (bh || firstnonzero)
|
||||
ret += render_hex_char(bh);
|
||||
ret += render_hex_char(b & 0x0F);
|
||||
firstnonzero = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string arpa() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << int(u.bytes[Endian::e4(0)]) << '.'
|
||||
<< int(u.bytes[Endian::e4(1)]) << '.'
|
||||
<< int(u.bytes[Endian::e4(2)]) << '.'
|
||||
<< int(u.bytes[Endian::e4(3)]) << ".in-addr.arpa";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static Addr from_asio(const asio::ip::address_v4& asio_addr)
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = (std::uint32_t)asio_addr.to_uint();
|
||||
return ret;
|
||||
}
|
||||
|
||||
asio::ip::address_v4 to_asio() const
|
||||
{
|
||||
return asio::ip::address_v4(u.addr);
|
||||
}
|
||||
|
||||
Addr operator&(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr & other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator|(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr | other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator+(const long delta) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr + (std::uint32_t)delta;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator+(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr + other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator-(const long delta) const {
|
||||
return operator+(-delta);
|
||||
}
|
||||
|
||||
Addr operator-(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr - other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator*(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr * other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator/(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr / other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator%(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr % other.u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator<<(const unsigned int shift) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr << shift;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator>>(const unsigned int shift) const {
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr >> shift;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator~() const {
|
||||
Addr ret;
|
||||
ret.u.addr = ~u.addr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return the network that contains the current address
|
||||
Addr network_addr(const unsigned int prefix_len) const
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = u.addr & prefix_len_to_netmask(prefix_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool operator==(const Addr& other) const
|
||||
{
|
||||
return u.addr == other.u.addr;
|
||||
}
|
||||
|
||||
bool operator!=(const Addr& other) const
|
||||
{
|
||||
return u.addr != other.u.addr;
|
||||
}
|
||||
|
||||
bool operator<(const Addr& other) const
|
||||
{
|
||||
return u.addr < other.u.addr;
|
||||
}
|
||||
|
||||
bool operator>(const Addr& other) const
|
||||
{
|
||||
return u.addr > other.u.addr;
|
||||
}
|
||||
|
||||
bool operator<=(const Addr& other) const
|
||||
{
|
||||
return u.addr <= other.u.addr;
|
||||
}
|
||||
|
||||
bool operator>=(const Addr& other) const
|
||||
{
|
||||
return u.addr >= other.u.addr;
|
||||
}
|
||||
|
||||
bool unspecified() const
|
||||
{
|
||||
return all_zeros();
|
||||
}
|
||||
|
||||
bool specified() const
|
||||
{
|
||||
return !unspecified();
|
||||
}
|
||||
|
||||
bool all_zeros() const
|
||||
{
|
||||
return u.addr == 0;
|
||||
}
|
||||
|
||||
bool all_ones() const
|
||||
{
|
||||
return ~u.addr == 0;
|
||||
}
|
||||
|
||||
// number of network bits in netmask,
|
||||
// throws exception if addr is not a netmask
|
||||
unsigned int prefix_len() const
|
||||
{
|
||||
const int ret = prefix_len_32(u.addr);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
else
|
||||
throw ipv4_exception("malformed netmask");
|
||||
}
|
||||
|
||||
int prefix_len_nothrow() const
|
||||
{
|
||||
return prefix_len_32(u.addr);
|
||||
}
|
||||
|
||||
// number of host bits in netmask
|
||||
unsigned int host_len() const
|
||||
{
|
||||
return SIZE - prefix_len();
|
||||
}
|
||||
|
||||
// return the number of host addresses contained within netmask
|
||||
Addr extent_from_netmask() const
|
||||
{
|
||||
Addr ret;
|
||||
ret.u.addr = extent_from_netmask_uint32();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::uint32_t extent_from_netmask_uint32() const
|
||||
{
|
||||
const unsigned int hl = host_len();
|
||||
if (hl < SIZE)
|
||||
return 1 << hl;
|
||||
else if (hl == SIZE)
|
||||
return 0;
|
||||
else
|
||||
throw ipv4_exception("extent overflow");
|
||||
}
|
||||
|
||||
// convert netmask in addr to prefix_len, will return -1 on error
|
||||
static int prefix_len_32(const std::uint32_t addr)
|
||||
{
|
||||
if (addr == ~std::uint32_t(0))
|
||||
return 32;
|
||||
else if (addr == 0)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
unsigned int high = 32;
|
||||
unsigned int low = 1;
|
||||
for (unsigned int i = 0; i < 5; ++i)
|
||||
{
|
||||
const unsigned int mid = (high + low) / 2;
|
||||
const IPv4::Addr::base_type test = prefix_len_to_netmask_unchecked(mid);
|
||||
if (addr == test)
|
||||
return mid;
|
||||
else if (addr > test)
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// address size in bits
|
||||
static unsigned int size()
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
std::size_t hashval() const
|
||||
{
|
||||
return Hash::value(u.addr);
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_IP_IMMUTABLE
|
||||
private:
|
||||
#endif
|
||||
|
||||
void negate()
|
||||
{
|
||||
u.addr = ~u.addr;
|
||||
}
|
||||
|
||||
void zero()
|
||||
{
|
||||
u.addr = 0;
|
||||
}
|
||||
|
||||
void zero_complement()
|
||||
{
|
||||
u.addr = ~0;
|
||||
}
|
||||
|
||||
void one()
|
||||
{
|
||||
u.addr = 1;
|
||||
}
|
||||
|
||||
Addr& operator++()
|
||||
{
|
||||
++u.addr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator+=(const long delta)
|
||||
{
|
||||
u.addr += (std::uint32_t)delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator-=(const long delta)
|
||||
{
|
||||
return operator+=(-delta);
|
||||
}
|
||||
|
||||
private:
|
||||
static base_type prefix_len_to_netmask_unchecked(const unsigned int prefix_len)
|
||||
{
|
||||
if (prefix_len)
|
||||
return ~((1 << (SIZE - prefix_len)) - 1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static base_type prefix_len_to_netmask(const unsigned int prefix_len)
|
||||
{
|
||||
if (prefix_len <= SIZE)
|
||||
return prefix_len_to_netmask_unchecked(prefix_len);
|
||||
else
|
||||
throw ipv4_exception("bad prefix len");
|
||||
}
|
||||
|
||||
union {
|
||||
base_type addr; // host byte order
|
||||
unsigned char bytes[4];
|
||||
} u;
|
||||
};
|
||||
|
||||
OPENVPN_OSTREAM(Addr, to_string)
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_HASH_METHOD(openvpn::IPv4::Addr, hashval);
|
||||
|
||||
#endif // OPENVPN_ADDR_IPV4_H
|
||||
@@ -0,0 +1,825 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_IPV6_H
|
||||
#define OPENVPN_ADDR_IPV6_H
|
||||
|
||||
#include <cstring> // for std::memcpy, std::memset
|
||||
#include <algorithm> // for std::min
|
||||
#include <cstdint> // for std::uint32_t
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/ostream.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/ffs.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/ipv4.hpp>
|
||||
#include <openvpn/addr/iperr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
class Addr;
|
||||
}
|
||||
|
||||
// Fundamental classes for representing an IPv6 IP address.
|
||||
|
||||
namespace IPv6 {
|
||||
|
||||
OPENVPN_EXCEPTION(ipv6_exception);
|
||||
|
||||
class Addr // NOTE: must be union-legal, so default constructor does not initialize
|
||||
{
|
||||
friend class IP::Addr;
|
||||
|
||||
public:
|
||||
enum { SIZE=128 };
|
||||
|
||||
static Addr from_addr(const Addr& addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static Addr from_in6_addr(const struct in6_addr *in6)
|
||||
{
|
||||
Addr ret;
|
||||
network_to_host_order(&ret.u, (const union ipv6addr *)in6->s6_addr);
|
||||
ret.scope_id_ = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct in6_addr to_in6_addr() const
|
||||
{
|
||||
struct in6_addr ret;
|
||||
host_to_network_order((union ipv6addr *)&ret, &u);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_sockaddr(const struct sockaddr_in6 *sa)
|
||||
{
|
||||
Addr ret;
|
||||
network_to_host_order(&ret.u, (const union ipv6addr *)sa->sin6_addr.s6_addr);
|
||||
ret.scope_id_ = sa->sin6_scope_id;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 to_sockaddr() const
|
||||
{
|
||||
struct sockaddr_in6 ret;
|
||||
std::memset(&ret, 0, sizeof(ret));
|
||||
ret.sin6_family = AF_INET6;
|
||||
ret.sin6_port = 0;
|
||||
host_to_network_order((union ipv6addr *)&ret.sin6_addr.s6_addr, &u);
|
||||
ret.sin6_scope_id = scope_id_;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr, const char *title = nullptr)
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::address_v6 a = asio::ip::make_address_v6(ipstr, ec);
|
||||
if (ec)
|
||||
throw ipv6_exception(IP::internal::format_error(ipstr, title, "v6", ec));
|
||||
return from_asio(a);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const asio::ip::address_v6 a = to_asio();
|
||||
std::string ret = a.to_string();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_hex(const std::string& s)
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.zero();
|
||||
size_t len = s.length();
|
||||
size_t base = 0;
|
||||
if (len > 0 && s[len-1] == 'L')
|
||||
len -= 1;
|
||||
if (len >= 2 && s[0] == '0' && s[1] == 'x')
|
||||
{
|
||||
base = 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (len < 1 || len > 32)
|
||||
throw ipv6_exception("parse hex error");
|
||||
size_t di = (len-1)>>1;
|
||||
for (int i = (len & 1) ? -1 : 0; i < int(len); i += 2)
|
||||
{
|
||||
const size_t idx = base + i;
|
||||
const int bh = (i >= 0) ? parse_hex_char(s[idx]) : 0;
|
||||
const int bl = parse_hex_char(s[idx+1]);
|
||||
if (bh == -1 || bl == -1)
|
||||
throw ipv6_exception("parse hex error");
|
||||
ret.u.bytes[Endian::e16(di--)] = (bh<<4) + bl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string to_hex() const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(32);
|
||||
bool firstnonzero = false;
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
const unsigned char b = u.bytes[Endian::e16rev(i)];
|
||||
if (b || firstnonzero || i == 15)
|
||||
{
|
||||
const char bh = b >> 4;
|
||||
if (bh || firstnonzero)
|
||||
ret += render_hex_char(bh);
|
||||
ret += render_hex_char(b & 0x0F);
|
||||
firstnonzero = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_ulong(unsigned long ul)
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.u.u64[Endian::e2(0)] = std::uint64_t(ul);
|
||||
ret.u.u64[Endian::e2(1)] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return *this as a unsigned long
|
||||
unsigned long to_ulong() const
|
||||
{
|
||||
const unsigned long ret = (unsigned long)u.u64[Endian::e2(0)];
|
||||
const std::uint64_t cmp = std::uint64_t(ret);
|
||||
if (u.u64[Endian::e2(1)] || cmp != u.u64[Endian::e2(0)])
|
||||
throw ipv6_exception("overflow in conversion from IPv6.Addr to unsigned long");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_long(long ul)
|
||||
{
|
||||
bool neg = false;
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
if (ul < 0)
|
||||
{
|
||||
ul = -(ul + 1);
|
||||
neg = true;
|
||||
}
|
||||
ret.u.u64[Endian::e2(0)] = std::uint64_t(ul);
|
||||
ret.u.u64[Endian::e2(1)] = 0;
|
||||
if (neg)
|
||||
ret.negate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return *this as a long
|
||||
long to_long() const
|
||||
{
|
||||
bool neg = false;
|
||||
Addr a = *this;
|
||||
if (a.u.u64[Endian::e2(1)])
|
||||
{
|
||||
a.negate();
|
||||
neg = true;
|
||||
}
|
||||
const long ret = (long)a.u.u64[Endian::e2(0)];
|
||||
const std::uint64_t cmp = std::uint64_t(ret);
|
||||
if (a.u.u64[Endian::e2(1)] || cmp != a.u.u64[Endian::e2(0)])
|
||||
throw ipv6_exception("overflow in conversion from IPv6.Addr to long");
|
||||
return neg ? -(ret + 1) : ret;
|
||||
}
|
||||
|
||||
std::string arpa() const
|
||||
{
|
||||
throw ipv6_exception("arpa() not implemented");
|
||||
}
|
||||
|
||||
static Addr from_asio(const asio::ip::address_v6& asio_addr)
|
||||
{
|
||||
Addr ret;
|
||||
union ipv6addr addr;
|
||||
addr.asio_bytes = asio_addr.to_bytes();
|
||||
network_to_host_order(&ret.u, &addr);
|
||||
ret.scope_id_ = (unsigned int)asio_addr.scope_id();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_byte_string(const unsigned char *bytestr)
|
||||
{
|
||||
Addr ret;
|
||||
network_to_host_order(&ret.u, (const union ipv6addr *)bytestr);
|
||||
ret.scope_id_ = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void to_byte_string(unsigned char *bytestr) const
|
||||
{
|
||||
host_to_network_order((union ipv6addr *)bytestr, &u);
|
||||
}
|
||||
|
||||
static void v4_to_byte_string(unsigned char *bytestr,
|
||||
const std::uint32_t v4addr)
|
||||
{
|
||||
union ipv6addr *a = (union ipv6addr *)bytestr;
|
||||
a->u32[0] = a->u32[1] = a->u32[2] = 0;
|
||||
a->u32[3] = v4addr;
|
||||
}
|
||||
|
||||
static bool byte_string_is_v4(const unsigned char *bytestr)
|
||||
{
|
||||
const union ipv6addr *a = (const union ipv6addr *)bytestr;
|
||||
return a->u32[0] == 0 && a->u32[1] == 0 && a->u32[2] == 0;
|
||||
}
|
||||
|
||||
static std::uint32_t v4_from_byte_string(const unsigned char *bytestr)
|
||||
{
|
||||
const union ipv6addr *a = (const union ipv6addr *)bytestr;
|
||||
return a->u32[3];
|
||||
}
|
||||
|
||||
asio::ip::address_v6 to_asio() const
|
||||
{
|
||||
union ipv6addr addr;
|
||||
host_to_network_order(&addr, &u);
|
||||
return asio::ip::address_v6(addr.asio_bytes, scope_id_);
|
||||
}
|
||||
|
||||
static Addr from_zero()
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.zero();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_one()
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.one();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Addr from_zero_complement()
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.zero_complement();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// build a netmask using given prefix_len
|
||||
static Addr netmask_from_prefix_len(const unsigned int prefix_len)
|
||||
{
|
||||
Addr ret;
|
||||
ret.scope_id_ = 0;
|
||||
ret.prefix_len_to_netmask(prefix_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// build a netmask using given extent
|
||||
Addr netmask_from_extent() const
|
||||
{
|
||||
const Addr lb = *this - 1;
|
||||
for (size_t i = 4; i --> 0 ;)
|
||||
{
|
||||
const std::uint32_t v = lb.u.u32[Endian::e4(i)];
|
||||
if (v)
|
||||
return netmask_from_prefix_len(SIZE - (((unsigned int)i<<5) + find_last_set(v)));
|
||||
}
|
||||
return from_zero_complement();
|
||||
}
|
||||
|
||||
Addr operator&(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.scope_id_ = scope_id_;
|
||||
ret.u.u64[0] = u.u64[0] & other.u.u64[0];
|
||||
ret.u.u64[1] = u.u64[1] & other.u.u64[1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator|(const Addr& other) const {
|
||||
Addr ret;
|
||||
ret.scope_id_ = scope_id_;
|
||||
ret.u.u64[0] = u.u64[0] | other.u.u64[0];
|
||||
ret.u.u64[1] = u.u64[1] | other.u.u64[1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator+(const long delta) const {
|
||||
Addr ret = *this;
|
||||
ret.u.u64[Endian::e2(0)] += delta;
|
||||
ret.u.u64[Endian::e2(1)] += (delta >= 0)
|
||||
? (ret.u.u64[Endian::e2(0)] < u.u64[Endian::e2(0)])
|
||||
: -(ret.u.u64[Endian::e2(0)] > u.u64[Endian::e2(0)]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator+(const Addr& other) const {
|
||||
Addr ret = *this;
|
||||
add(ret.u, other.u);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator-(const long delta) const {
|
||||
return operator+(-delta);
|
||||
}
|
||||
|
||||
Addr operator-(const Addr& other) const {
|
||||
Addr ret = *this;
|
||||
sub(ret.u, other.u);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator*(const Addr& d) const {
|
||||
Addr m = d;
|
||||
Addr ret = from_zero();
|
||||
for (unsigned int i = 0; i < SIZE; ++i)
|
||||
{
|
||||
if (bit(i))
|
||||
ret += m;
|
||||
m <<= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator/(const Addr& d) const {
|
||||
Addr q, r;
|
||||
div(*this, d, q, r);
|
||||
return q;
|
||||
}
|
||||
|
||||
Addr operator%(const Addr& d) const {
|
||||
Addr q, r;
|
||||
div(*this, d, q, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
Addr operator<<(const unsigned int shift) const {
|
||||
Addr ret = *this;
|
||||
shiftl128(ret.u.u64[Endian::e2(0)],
|
||||
ret.u.u64[Endian::e2(1)],
|
||||
shift);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator>>(const unsigned int shift) const {
|
||||
Addr ret = *this;
|
||||
shiftr128(ret.u.u64[Endian::e2(0)],
|
||||
ret.u.u64[Endian::e2(1)],
|
||||
shift);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Addr operator~() const {
|
||||
Addr ret;
|
||||
ret.scope_id_ = scope_id_;
|
||||
ret.u.u64[0] = ~u.u64[0];
|
||||
ret.u.u64[1] = ~u.u64[1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return the network that contains the current address
|
||||
Addr network_addr(const unsigned int prefix_len) const
|
||||
{
|
||||
return *this & netmask_from_prefix_len(prefix_len);
|
||||
}
|
||||
|
||||
bool operator==(const Addr& other) const
|
||||
{
|
||||
return u.u64[0] == other.u.u64[0] && u.u64[1] == other.u.u64[1] && scope_id_ == other.scope_id_;
|
||||
}
|
||||
|
||||
bool operator!=(const Addr& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
#define OPENVPN_IPV6_OPERATOR_REL(OP) \
|
||||
bool operator OP(const Addr& other) const \
|
||||
{ \
|
||||
if (u.u64[Endian::e2(1)] == other.u.u64[Endian::e2(1)]) \
|
||||
{ \
|
||||
if (u.u64[Endian::e2(0)] != other.u.u64[Endian::e2(0)]) \
|
||||
return u.u64[Endian::e2(0)] OP other.u.u64[Endian::e2(0)]; \
|
||||
else \
|
||||
return scope_id_ OP other.scope_id_; \
|
||||
} \
|
||||
else \
|
||||
return u.u64[Endian::e2(1)] OP other.u.u64[Endian::e2(1)]; \
|
||||
}
|
||||
|
||||
OPENVPN_IPV6_OPERATOR_REL(<)
|
||||
OPENVPN_IPV6_OPERATOR_REL(>)
|
||||
OPENVPN_IPV6_OPERATOR_REL(<=)
|
||||
OPENVPN_IPV6_OPERATOR_REL(>=)
|
||||
|
||||
#undef OPENVPN_IPV6_OPERATOR_REL
|
||||
|
||||
bool unspecified() const
|
||||
{
|
||||
return all_zeros();
|
||||
}
|
||||
|
||||
bool specified() const
|
||||
{
|
||||
return !unspecified();
|
||||
}
|
||||
|
||||
bool all_zeros() const
|
||||
{
|
||||
return u.u64[0] == 0 && u.u64[1] == 0;
|
||||
}
|
||||
|
||||
bool all_ones() const
|
||||
{
|
||||
return u.u64[0] == ~std::uint64_t(0) && u.u64[1] == ~std::uint64_t(0);
|
||||
}
|
||||
|
||||
bool bit(unsigned int pos) const
|
||||
{
|
||||
if (pos < 64)
|
||||
return (u.u64[Endian::e2(0)] & (std::uint64_t(1)<<pos)) != 0;
|
||||
else
|
||||
return (u.u64[Endian::e2(1)] & (std::uint64_t(1)<<(pos-64))) != 0;
|
||||
}
|
||||
|
||||
// number of network bits in netmask,
|
||||
// throws exception if addr is not a netmask
|
||||
unsigned int prefix_len() const
|
||||
{
|
||||
int idx = -1;
|
||||
|
||||
if (u.u32[Endian::e4(3)] != ~std::uint32_t(0))
|
||||
{
|
||||
if (!u.u32[Endian::e4(0)] && !u.u32[Endian::e4(1)] && !u.u32[Endian::e4(2)])
|
||||
idx = 0;
|
||||
}
|
||||
else if (u.u32[Endian::e4(2)] != ~std::uint32_t(0))
|
||||
{
|
||||
if (!u.u32[Endian::e4(0)] && !u.u32[Endian::e4(1)])
|
||||
idx = 1;
|
||||
}
|
||||
else if (u.u32[Endian::e4(1)] != ~std::uint32_t(0))
|
||||
{
|
||||
if (!u.u32[Endian::e4(0)])
|
||||
idx = 2;
|
||||
}
|
||||
else
|
||||
idx = 3;
|
||||
|
||||
if (idx >= 0)
|
||||
{
|
||||
const int ret = IPv4::Addr::prefix_len_32(u.u32[Endian::e4rev(idx)]);
|
||||
if (ret >= 0)
|
||||
return ret + (idx<<5);
|
||||
}
|
||||
throw ipv6_exception("malformed netmask");
|
||||
}
|
||||
|
||||
// number of host bits in netmask
|
||||
unsigned int host_len() const
|
||||
{
|
||||
return SIZE - prefix_len();
|
||||
}
|
||||
|
||||
// return the number of host addresses contained within netmask
|
||||
Addr extent_from_netmask() const
|
||||
{
|
||||
const unsigned int hl = host_len();
|
||||
if (hl < SIZE)
|
||||
{
|
||||
Addr a;
|
||||
a.scope_id_ = 0;
|
||||
a.one();
|
||||
return a << hl;
|
||||
}
|
||||
else if (hl == SIZE)
|
||||
return from_zero();
|
||||
else
|
||||
throw ipv6_exception("extent overflow");
|
||||
}
|
||||
|
||||
// address size in bits
|
||||
static unsigned int size()
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
std::size_t hashval() const
|
||||
{
|
||||
return Hash::value(u.u32[0], u.u32[1], u.u32[2], u.u32[3]);
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_IP_IMMUTABLE
|
||||
private:
|
||||
#endif
|
||||
|
||||
void negate()
|
||||
{
|
||||
u.u64[0] = ~u.u64[0];
|
||||
u.u64[1] = ~u.u64[1];
|
||||
}
|
||||
|
||||
void zero()
|
||||
{
|
||||
u.u64[0] = 0;
|
||||
u.u64[1] = 0;
|
||||
}
|
||||
|
||||
void zero_complement()
|
||||
{
|
||||
u.u64[0] = ~std::uint64_t(0);
|
||||
u.u64[1] = ~std::uint64_t(0);
|
||||
}
|
||||
|
||||
void one()
|
||||
{
|
||||
u.u64[0] = 1;
|
||||
u.u64[1] = 0;
|
||||
}
|
||||
|
||||
Addr& operator++()
|
||||
{
|
||||
if (++u.u64[Endian::e2(0)] == 0)
|
||||
++u.u64[Endian::e2(1)];
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator+=(const long delta)
|
||||
{
|
||||
*this = *this + delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator-=(const long delta)
|
||||
{
|
||||
return operator+=(-delta);
|
||||
}
|
||||
|
||||
Addr& operator+=(const Addr& other) {
|
||||
add(u, other.u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator-=(const Addr& other) {
|
||||
sub(u, other.u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator<<=(const unsigned int shift) {
|
||||
shiftl128(u.u64[Endian::e2(0)],
|
||||
u.u64[Endian::e2(1)],
|
||||
shift);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Addr& operator>>=(const unsigned int shift) {
|
||||
shiftr128(u.u64[Endian::e2(0)],
|
||||
u.u64[Endian::e2(1)],
|
||||
shift);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_clear_bit(unsigned int pos, bool value)
|
||||
{
|
||||
if (pos < 64)
|
||||
{
|
||||
if (value)
|
||||
u.u64[Endian::e2(0)] |= (std::uint64_t(1)<<pos);
|
||||
else
|
||||
u.u64[Endian::e2(0)] &= ~(std::uint64_t(1)<<pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value)
|
||||
u.u64[Endian::e2(1)] |= (std::uint64_t(1)<<(pos-64));
|
||||
else
|
||||
u.u64[Endian::e2(1)] &= ~(std::uint64_t(1)<<(pos-64));
|
||||
}
|
||||
}
|
||||
|
||||
void set_bit(unsigned int pos, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
if (pos < 64)
|
||||
u.u64[Endian::e2(0)] |= (std::uint64_t(1)<<pos);
|
||||
else
|
||||
u.u64[Endian::e2(1)] |= (std::uint64_t(1)<<(pos-64));
|
||||
}
|
||||
}
|
||||
|
||||
static void div(const Addr& n, const Addr& d, Addr& q, Addr& r)
|
||||
{
|
||||
if (d.all_zeros())
|
||||
throw ipv6_exception("division by 0");
|
||||
q = from_zero(); // quotient
|
||||
r = n; // remainder (init to numerator)
|
||||
Addr ml = from_zero(); // mask low
|
||||
Addr mh = d; // mask high (init to denominator)
|
||||
for (unsigned int i = 0; i < SIZE; ++i)
|
||||
{
|
||||
ml >>= 1;
|
||||
ml.set_bit(SIZE-1, mh.bit(0));
|
||||
mh >>= 1;
|
||||
if (mh.all_zeros() && r >= ml)
|
||||
{
|
||||
r -= ml;
|
||||
q.set_bit((SIZE-1)-i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int scope_id() const
|
||||
{
|
||||
return scope_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
union ipv6addr {
|
||||
std::uint64_t u64[2];
|
||||
std::uint32_t u32[4]; // generally stored in host byte order
|
||||
unsigned char bytes[16];
|
||||
asio::ip::address_v6::bytes_type asio_bytes;
|
||||
};
|
||||
|
||||
void prefix_len_to_netmask_unchecked(const unsigned int prefix_len)
|
||||
{
|
||||
if (prefix_len > 0)
|
||||
{
|
||||
const unsigned int pl = prefix_len - 1;
|
||||
const std::uint32_t mask = ~((1 << (31 - (pl & 31))) - 1);
|
||||
switch (pl >> 5)
|
||||
{
|
||||
case 0:
|
||||
u.u32[Endian::e4(0)] = 0;
|
||||
u.u32[Endian::e4(1)] = 0;
|
||||
u.u32[Endian::e4(2)] = 0;
|
||||
u.u32[Endian::e4(3)] = mask;
|
||||
break;
|
||||
case 1:
|
||||
u.u32[Endian::e4(0)] = 0;
|
||||
u.u32[Endian::e4(1)] = 0;
|
||||
u.u32[Endian::e4(2)] = mask;
|
||||
u.u32[Endian::e4(3)] = ~0;
|
||||
break;
|
||||
case 2:
|
||||
u.u32[Endian::e4(0)] = 0;
|
||||
u.u32[Endian::e4(1)] = mask;
|
||||
u.u32[Endian::e4(2)] = ~0;
|
||||
u.u32[Endian::e4(3)] = ~0;
|
||||
break;
|
||||
case 3:
|
||||
u.u32[Endian::e4(0)] = mask;
|
||||
u.u32[Endian::e4(1)] = ~0;
|
||||
u.u32[Endian::e4(2)] = ~0;
|
||||
u.u32[Endian::e4(3)] = ~0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
zero();
|
||||
}
|
||||
|
||||
void prefix_len_to_netmask(const unsigned int prefix_len)
|
||||
{
|
||||
if (prefix_len <= SIZE)
|
||||
return prefix_len_to_netmask_unchecked(prefix_len);
|
||||
else
|
||||
throw ipv6_exception("bad prefix len");
|
||||
}
|
||||
|
||||
static void host_to_network_order(union ipv6addr *dest, const union ipv6addr *src)
|
||||
{
|
||||
dest->u32[0] = htonl(src->u32[Endian::e4rev(0)]);
|
||||
dest->u32[1] = htonl(src->u32[Endian::e4rev(1)]);
|
||||
dest->u32[2] = htonl(src->u32[Endian::e4rev(2)]);
|
||||
dest->u32[3] = htonl(src->u32[Endian::e4rev(3)]);
|
||||
}
|
||||
|
||||
static void network_to_host_order(union ipv6addr *dest, const union ipv6addr *src)
|
||||
{
|
||||
dest->u32[0] = ntohl(src->u32[Endian::e4rev(0)]);
|
||||
dest->u32[1] = ntohl(src->u32[Endian::e4rev(1)]);
|
||||
dest->u32[2] = ntohl(src->u32[Endian::e4rev(2)]);
|
||||
dest->u32[3] = ntohl(src->u32[Endian::e4rev(3)]);
|
||||
}
|
||||
|
||||
static void shiftl128(std::uint64_t& low,
|
||||
std::uint64_t& high,
|
||||
unsigned int shift)
|
||||
{
|
||||
if (shift == 1)
|
||||
{
|
||||
high <<= 1;
|
||||
if (low & (std::uint64_t(1) << 63))
|
||||
high |= 1;
|
||||
low <<= 1;
|
||||
}
|
||||
else if (shift == 0)
|
||||
;
|
||||
else if (shift <= 128)
|
||||
{
|
||||
if (shift >= 64)
|
||||
{
|
||||
high = low;
|
||||
low = 0;
|
||||
shift -= 64;
|
||||
}
|
||||
if (shift < 64)
|
||||
{
|
||||
high = (high << shift) | (low >> (64-shift));
|
||||
low <<= shift;
|
||||
}
|
||||
else // shift == 64
|
||||
high = 0;
|
||||
}
|
||||
else
|
||||
throw ipv6_exception("l-shift too large");
|
||||
}
|
||||
|
||||
static void shiftr128(std::uint64_t& low,
|
||||
std::uint64_t& high,
|
||||
unsigned int shift)
|
||||
{
|
||||
if (shift == 1)
|
||||
{
|
||||
low >>= 1;
|
||||
if (high & 1)
|
||||
low |= (std::uint64_t(1) << 63);
|
||||
high >>= 1;
|
||||
}
|
||||
else if (shift == 0)
|
||||
;
|
||||
else if (shift <= 128)
|
||||
{
|
||||
if (shift >= 64)
|
||||
{
|
||||
low = high;
|
||||
high = 0;
|
||||
shift -= 64;
|
||||
}
|
||||
if (shift < 64)
|
||||
{
|
||||
low = (low >> shift) | (high << (64-shift));
|
||||
high >>= shift;
|
||||
}
|
||||
else // shift == 64
|
||||
low = 0;
|
||||
}
|
||||
else
|
||||
throw ipv6_exception("r-shift too large");
|
||||
}
|
||||
|
||||
static void add(ipv6addr& dest, const ipv6addr& src) {
|
||||
const std::uint64_t dorigl = dest.u64[Endian::e2(0)];
|
||||
dest.u64[Endian::e2(0)] += src.u64[Endian::e2(0)];
|
||||
dest.u64[Endian::e2(1)] += src.u64[Endian::e2(1)];
|
||||
// check for overflow of low 64 bits, add carry to high
|
||||
if (dest.u64[Endian::e2(0)] < dorigl)
|
||||
++dest.u64[Endian::e2(1)];
|
||||
}
|
||||
|
||||
static void sub(ipv6addr& dest, const ipv6addr& src) {
|
||||
const std::uint64_t dorigl = dest.u64[Endian::e2(0)];
|
||||
dest.u64[Endian::e2(0)] -= src.u64[Endian::e2(0)];
|
||||
dest.u64[Endian::e2(1)] -= src.u64[Endian::e2(1)]
|
||||
+ (dorigl < dest.u64[Endian::e2(0)]);
|
||||
}
|
||||
|
||||
union ipv6addr u;
|
||||
unsigned int scope_id_;
|
||||
};
|
||||
|
||||
OPENVPN_OSTREAM(Addr, to_string)
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_HASH_METHOD(openvpn::IPv6::Addr, hashval);
|
||||
|
||||
#endif // OPENVPN_ADDR_IPV6_H
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_MACADDR_H
|
||||
#define OPENVPN_ADDR_MACADDR_H
|
||||
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/ostream.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Fundamental class for representing an ethernet MAC address.
|
||||
|
||||
class MACAddr {
|
||||
public:
|
||||
MACAddr()
|
||||
{
|
||||
std::memset(addr_, 0, sizeof(addr_));
|
||||
}
|
||||
|
||||
MACAddr(const unsigned char *addr)
|
||||
{
|
||||
reset(addr);
|
||||
}
|
||||
|
||||
void reset(const unsigned char *addr)
|
||||
{
|
||||
std::memcpy(addr_, addr, sizeof(addr_));
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return render_hex_sep(addr_, sizeof(addr_), ':');
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char addr_[6];
|
||||
};
|
||||
|
||||
OPENVPN_OSTREAM(MACAddr, to_string)
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_ADDR_MACADDR_H
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_POOL_H
|
||||
#define OPENVPN_ADDR_POOL_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/range.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
|
||||
// Maintain a pool of IP addresses.
|
||||
// A should be IP::Addr, IPv4::Addr, or IPv6::Addr.
|
||||
template <typename ADDR>
|
||||
class PoolType
|
||||
{
|
||||
public:
|
||||
PoolType() {}
|
||||
|
||||
// Add range of addresses to pool (pool will own the addresses).
|
||||
void add_range(const RangeType<ADDR>& range)
|
||||
{
|
||||
typename RangeType<ADDR>::Iterator iter = range.iterator();
|
||||
while (iter.more())
|
||||
{
|
||||
const ADDR& a = iter.addr();
|
||||
add_addr(a);
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
|
||||
// Add single address to pool (pool will own the address).
|
||||
void add_addr(const ADDR& addr)
|
||||
{
|
||||
typename std::unordered_map<ADDR, bool>::const_iterator e = map.find(addr);
|
||||
if (e == map.end())
|
||||
{
|
||||
freelist.push_back(addr);
|
||||
map[addr] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return number of pool addresses currently in use.
|
||||
size_t n_in_use() const
|
||||
{
|
||||
return map.size() - freelist.size();
|
||||
}
|
||||
|
||||
// Acquire an address from pool. Returns true if successful,
|
||||
// with address placed in dest, or false if pool depleted.
|
||||
bool acquire_addr(ADDR& dest)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (freelist.empty())
|
||||
return false;
|
||||
const ADDR& a = freelist.front();
|
||||
typename std::unordered_map<ADDR, bool>::iterator e = map.find(a);
|
||||
if (e == map.end()) // any address in freelist must exist in map
|
||||
throw Exception("PoolType: address in freelist doesn't exist in map");
|
||||
if (!e->second)
|
||||
{
|
||||
e->second = true;
|
||||
dest = a;
|
||||
freelist.pop_front();
|
||||
return true;
|
||||
}
|
||||
freelist.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire a specific address from pool, returning true if
|
||||
// successful, or false if the address is not available.
|
||||
bool acquire_specific_addr(const ADDR& addr)
|
||||
{
|
||||
typename std::unordered_map<ADDR, bool>::iterator e = map.find(addr);
|
||||
if (e != map.end() && !e->second)
|
||||
{
|
||||
e->second = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return a previously acquired address to the pool. Does nothing if
|
||||
// (a) the address is owned by the pool and marked as free, or
|
||||
// (b) the address is not owned by the pool.
|
||||
void release_addr(const ADDR& addr)
|
||||
{
|
||||
typename std::unordered_map<ADDR, bool>::iterator e = map.find(addr);
|
||||
if (e != map.end() && e->second)
|
||||
{
|
||||
freelist.push_back(addr);
|
||||
e->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUGGING -- get the map load factor
|
||||
float load_factor() const { return map.load_factor(); }
|
||||
|
||||
private:
|
||||
std::deque<ADDR> freelist;
|
||||
std::unordered_map<ADDR, bool> map;
|
||||
};
|
||||
|
||||
typedef PoolType<IP::Addr> Pool;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_RANGE_H
|
||||
#define OPENVPN_ADDR_RANGE_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
|
||||
// Denote a range of IP addresses with a start and extent,
|
||||
// where A represents an address class.
|
||||
// A should be a network address class such as IP::Addr, IPv4::Addr, or IPv6::Addr.
|
||||
|
||||
template <typename ADDR>
|
||||
class RangeType
|
||||
{
|
||||
public:
|
||||
class Iterator
|
||||
{
|
||||
friend class RangeType;
|
||||
public:
|
||||
bool more() const { return remaining_ > 0; }
|
||||
|
||||
const ADDR& addr() const { return addr_; }
|
||||
|
||||
void next()
|
||||
{
|
||||
if (more())
|
||||
{
|
||||
++addr_;
|
||||
--remaining_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator(const RangeType& range)
|
||||
: addr_(range.start_), remaining_(range.extent_) {}
|
||||
|
||||
ADDR addr_;
|
||||
size_t remaining_;
|
||||
};
|
||||
|
||||
RangeType() : extent_(0) {}
|
||||
|
||||
RangeType(const ADDR& start, const size_t extent)
|
||||
: start_(start), extent_(extent) {}
|
||||
|
||||
Iterator iterator() const { return Iterator(*this); }
|
||||
|
||||
const bool defined() const { return extent_ > 0; }
|
||||
const ADDR& start() const { return start_; }
|
||||
size_t extent() const { return extent_; }
|
||||
|
||||
RangeType pull_front(size_t extent)
|
||||
{
|
||||
if (extent > extent_)
|
||||
extent = extent_;
|
||||
RangeType ret(start_, extent);
|
||||
start_ += extent;
|
||||
extent_ -= extent;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << start_.to_string() << '[' << extent_ << ']';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
ADDR start_;
|
||||
size_t extent_;
|
||||
};
|
||||
|
||||
template <typename ADDR>
|
||||
class RangePartitionType
|
||||
{
|
||||
public:
|
||||
RangePartitionType(const RangeType<ADDR>& src_range, const size_t n_partitions)
|
||||
: range(src_range),
|
||||
remaining(n_partitions)
|
||||
{
|
||||
}
|
||||
|
||||
bool next(RangeType<ADDR>& r)
|
||||
{
|
||||
if (remaining)
|
||||
{
|
||||
if (remaining > 1)
|
||||
r = range.pull_front(range.extent() / remaining);
|
||||
else
|
||||
r = range;
|
||||
--remaining;
|
||||
return r.defined();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
RangeType<ADDR> range;
|
||||
size_t remaining;
|
||||
};
|
||||
|
||||
typedef RangeType<IP::Addr> Range;
|
||||
typedef RangePartitionType<IP::Addr> RangePartition;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// Regular expressions for IPv4/v6
|
||||
// Source: http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
|
||||
|
||||
#ifndef OPENVPN_ADDR_REGEX_H
|
||||
#define OPENVPN_ADDR_REGEX_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
inline std::string v4_regex()
|
||||
{
|
||||
const std::string ipv4seg = "(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])";
|
||||
return "(?:" + ipv4seg + "\\.){3,3}" + ipv4seg;
|
||||
}
|
||||
|
||||
inline std::string v6_regex()
|
||||
{
|
||||
const std::string ipv6seg = "[0-9a-fA-F]{1,4}";
|
||||
return "(?:"
|
||||
"(?:" + ipv6seg + ":){7,7}" + ipv6seg + "|" // 1:2:3:4:5:6:7:8
|
||||
"(?:" + ipv6seg + ":){1,7}:|" // 1:: 1:2:3:4:5:6:7::
|
||||
"(?:" + ipv6seg + ":){1,6}:" + ipv6seg + "|" // 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
|
||||
"(?:" + ipv6seg + ":){1,5}(?::" + ipv6seg + "){1,2}|" // 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
|
||||
"(?:" + ipv6seg + ":){1,4}(?::" + ipv6seg + "){1,3}|" // 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
|
||||
"(?:" + ipv6seg + ":){1,3}(?::" + ipv6seg + "){1,4}|" // 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
|
||||
"(?:" + ipv6seg + ":){1,2}(?::" + ipv6seg + "){1,5}|" + // 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
|
||||
ipv6seg + ":(?:(?::" + ipv6seg + "){1,6})|" // 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
|
||||
":(?:(?::" + ipv6seg + "){1,7}|:)|" // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
|
||||
"fe80:(?::" + ipv6seg + "){0,4}%[0-9a-zA-Z]{1,}|" // fe80::7:8%eth0 fe80::7:8%1 (link-local IPv6 addresses with zone index)
|
||||
"::(?:ffff(?::0{1,4}){0,1}:){0,1}" + v4_regex() + "|" // ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
|
||||
"(?:" + ipv6seg + ":){1,4}:" + v4_regex() + // 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
|
||||
")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,251 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_ADDR_ROUTE_H
|
||||
#define OPENVPN_ADDR_ROUTE_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdint> // for std::uint32_t
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
// Basic route object
|
||||
template <typename ADDR>
|
||||
struct RouteType
|
||||
{
|
||||
typedef ADDR Addr;
|
||||
|
||||
ADDR addr;
|
||||
unsigned int prefix_len;
|
||||
|
||||
OPENVPN_EXCEPTION(route_error);
|
||||
|
||||
RouteType()
|
||||
: prefix_len(0)
|
||||
{
|
||||
}
|
||||
|
||||
RouteType(const std::string& rtstr, const char *title = nullptr)
|
||||
: RouteType(RouteType::from_string(rtstr, title))
|
||||
{
|
||||
}
|
||||
|
||||
RouteType(const std::string& rtstr, const std::string& title)
|
||||
: RouteType(RouteType::from_string(rtstr, title.c_str()))
|
||||
{
|
||||
}
|
||||
|
||||
RouteType(const ADDR& addr_arg,
|
||||
const unsigned int prefix_len_arg)
|
||||
: addr(addr_arg),
|
||||
prefix_len(prefix_len_arg)
|
||||
{
|
||||
}
|
||||
|
||||
static RouteType from_string(const std::string& rtstr, const char *title = nullptr)
|
||||
{
|
||||
RouteType r;
|
||||
std::vector<std::string> pair;
|
||||
pair.reserve(2);
|
||||
Split::by_char_void<std::vector<std::string>, NullLex, Split::NullLimit>(pair, rtstr, '/', 0, 1);
|
||||
r.addr = ADDR::from_string(pair[0], title);
|
||||
if (pair.size() >= 2)
|
||||
{
|
||||
r.prefix_len = parse_number_throw<unsigned int>(pair[1], "prefix length");
|
||||
if (r.prefix_len > r.addr.size())
|
||||
OPENVPN_THROW(route_error, (title ? title : "route") << " : bad prefix length : " << rtstr);
|
||||
}
|
||||
else
|
||||
r.prefix_len = r.addr.size();
|
||||
return r;
|
||||
}
|
||||
|
||||
IP::Addr::Version version() const
|
||||
{
|
||||
return addr.version();
|
||||
}
|
||||
|
||||
IP::Addr::VersionMask version_mask() const
|
||||
{
|
||||
return addr.version_mask();
|
||||
}
|
||||
|
||||
ADDR netmask() const
|
||||
{
|
||||
return ADDR::netmask_from_prefix_len(version(), prefix_len);
|
||||
}
|
||||
|
||||
size_t extent() const
|
||||
{
|
||||
return netmask().extent_from_netmask().to_ulong();
|
||||
}
|
||||
|
||||
bool is_canonical() const
|
||||
{
|
||||
return (addr & netmask()) == addr;
|
||||
}
|
||||
|
||||
void force_canonical()
|
||||
{
|
||||
addr = addr & netmask();
|
||||
}
|
||||
|
||||
bool is_host() const
|
||||
{
|
||||
return addr.defined() && prefix_len == addr.size();
|
||||
}
|
||||
|
||||
bool contains(const ADDR& a) const // assumes canonical address/routes
|
||||
{
|
||||
if (addr.defined() && addr.version() == a.version())
|
||||
return (a & netmask()) == addr;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool contains(const RouteType& r) const // assumes canonical routes
|
||||
{
|
||||
return contains(r.addr) && r.prefix_len >= prefix_len;
|
||||
}
|
||||
|
||||
bool split(RouteType& r1, RouteType& r2) const // assumes we are canonical
|
||||
{
|
||||
if (!is_host())
|
||||
{
|
||||
const unsigned int newpl = prefix_len + 1;
|
||||
r1.addr = addr;
|
||||
r1.prefix_len = newpl;
|
||||
|
||||
r2.addr = addr + ADDR::netmask_from_prefix_len(addr.version(), newpl).extent_from_netmask();
|
||||
r2.prefix_len = newpl;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return addr.to_string() + '/' + openvpn::to_string(prefix_len);
|
||||
}
|
||||
|
||||
std::string to_string_by_netmask() const
|
||||
{
|
||||
return addr.to_string() + ' ' + netmask().to_string();
|
||||
}
|
||||
|
||||
bool operator==(const RouteType& other) const
|
||||
{
|
||||
return prefix_len == other.prefix_len && addr == other.addr;
|
||||
}
|
||||
|
||||
std::size_t hash_value() const
|
||||
{
|
||||
return Hash::value(addr, prefix_len);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ADDR>
|
||||
struct RouteTypeList : public std::vector<RouteType<ADDR>>
|
||||
{
|
||||
typedef std::vector< RouteType<ADDR> > Base;
|
||||
|
||||
OPENVPN_EXCEPTION(route_list_error);
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (typename Base::const_iterator i = Base::begin(); i != Base::end(); ++i)
|
||||
os << i->to_string() << std::endl;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
IP::Addr::VersionMask version_mask() const
|
||||
{
|
||||
IP::Addr::VersionMask mask = 0;
|
||||
for (typename Base::const_iterator i = Base::begin(); i != Base::end(); ++i)
|
||||
mask |= i->version_mask();
|
||||
return mask;
|
||||
}
|
||||
|
||||
void verify_canonical() const
|
||||
{
|
||||
for (typename Base::const_iterator i = Base::begin(); i != Base::end(); ++i)
|
||||
if (!i->is_canonical())
|
||||
throw route_list_error("route not canonical: " + i->to_string());
|
||||
}
|
||||
};
|
||||
|
||||
typedef RouteType<IP::Addr> Route;
|
||||
typedef RouteType<IPv4::Addr> Route4;
|
||||
typedef RouteType<IPv6::Addr> Route6;
|
||||
|
||||
typedef RouteTypeList<IP::Addr> RouteList;
|
||||
typedef RouteTypeList<IPv4::Addr> Route4List;
|
||||
typedef RouteTypeList<IPv6::Addr> Route6List;
|
||||
|
||||
OPENVPN_OSTREAM(Route, to_string);
|
||||
OPENVPN_OSTREAM(Route4, to_string);
|
||||
OPENVPN_OSTREAM(Route6, to_string);
|
||||
|
||||
OPENVPN_OSTREAM(RouteList, to_string);
|
||||
OPENVPN_OSTREAM(Route4List, to_string);
|
||||
OPENVPN_OSTREAM(Route6List, to_string);
|
||||
|
||||
inline Route route_from_string_prefix(const std::string& addrstr,
|
||||
const unsigned int prefix_len,
|
||||
const std::string& title,
|
||||
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
|
||||
{
|
||||
Route r;
|
||||
r.addr = IP::Addr(addrstr, title, required_version);
|
||||
r.prefix_len = prefix_len;
|
||||
if (r.prefix_len > r.addr.size())
|
||||
OPENVPN_THROW(Route::route_error, title << " : bad prefix length : " << addrstr);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline Route route_from_string(const std::string& rtstr,
|
||||
const std::string& title,
|
||||
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
|
||||
{
|
||||
Route r(rtstr, title);
|
||||
r.addr.validate_version(title, required_version);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_HASH_METHOD(openvpn::IP::Route, hash_value);
|
||||
OPENVPN_HASH_METHOD(openvpn::IP::Route4, hash_value);
|
||||
OPENVPN_HASH_METHOD(openvpn::IP::Route6, hash_value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Invert a route list. Used to support excluded routes on platforms that
|
||||
// don't support them natively.
|
||||
|
||||
#ifndef OPENVPN_ADDR_ROUTEINV_H
|
||||
#define OPENVPN_ADDR_ROUTEINV_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
class RouteInverter : public RouteList
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(route_inverter);
|
||||
|
||||
RouteInverter() {}
|
||||
|
||||
// NOTE: when passing RouteInverter to this constructor, make sure
|
||||
// to static_cast it to RouteList& so as to avoid matching the
|
||||
// default copy constructor.
|
||||
explicit RouteInverter(const RouteList& in)
|
||||
: RouteInverter(in, in.version_mask())
|
||||
{
|
||||
}
|
||||
|
||||
RouteInverter(const RouteList& in, const Addr::VersionMask vermask)
|
||||
{
|
||||
in.verify_canonical();
|
||||
if (vermask & Addr::V4_MASK)
|
||||
descend(in, Addr::V4, Route(Addr::from_zero(Addr::V4), 0));
|
||||
if (vermask & Addr::V6_MASK)
|
||||
descend(in, Addr::V6, Route(Addr::from_zero(Addr::V6), 0));
|
||||
}
|
||||
|
||||
private:
|
||||
enum Type {
|
||||
EQUAL,
|
||||
SUBROUTE,
|
||||
LEAF,
|
||||
};
|
||||
|
||||
void descend(const RouteList& in, const Addr::Version ver, const Route& route)
|
||||
{
|
||||
switch (find(in, route))
|
||||
{
|
||||
case SUBROUTE:
|
||||
{
|
||||
Route r1, r2;
|
||||
if (route.split(r1, r2))
|
||||
{
|
||||
descend(in, ver, r1);
|
||||
descend(in, ver, r2);
|
||||
}
|
||||
else
|
||||
push_back(route);
|
||||
break;
|
||||
}
|
||||
case LEAF:
|
||||
push_back(route);
|
||||
break;
|
||||
case EQUAL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Type find(const RouteList& in, const Route& route)
|
||||
{
|
||||
Type type = LEAF;
|
||||
for (RouteList::const_iterator i = in.begin(); i != in.end(); ++i)
|
||||
{
|
||||
const Route& r = *i;
|
||||
if (route == r)
|
||||
return EQUAL;
|
||||
else if (route.contains(r))
|
||||
type = SUBROUTE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,324 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLE_MACLIFE_H
|
||||
#define OPENVPN_APPLE_MACLIFE_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <openvpn/log/logthread.hpp>
|
||||
#include <openvpn/applecrypto/cf/cftimer.hpp>
|
||||
#include <openvpn/applecrypto/cf/cfhelper.hpp>
|
||||
#include <openvpn/applecrypto/util/reachable.hpp>
|
||||
#include <openvpn/client/clilife.hpp>
|
||||
#include <openvpn/apple/runloop.hpp>
|
||||
#include <openvpn/apple/macsleep.hpp>
|
||||
#include <openvpn/apple/scdynstore.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class MacLifeCycle : public ClientLifeCycle, MacSleep, ReachabilityTracker
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(mac_lifecycle_error);
|
||||
|
||||
MacLifeCycle()
|
||||
: ReachabilityTracker(true, true),
|
||||
nc(nullptr),
|
||||
thread(nullptr),
|
||||
paused(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MacLifeCycle()
|
||||
{
|
||||
stop_thread();
|
||||
}
|
||||
|
||||
virtual bool network_available()
|
||||
{
|
||||
return net_up();
|
||||
}
|
||||
|
||||
virtual void start(NotifyCallback* nc_arg)
|
||||
{
|
||||
if (!thread && nc_arg)
|
||||
{
|
||||
nc = nc_arg;
|
||||
thread = new std::thread(&MacLifeCycle::thread_func, this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void stop()
|
||||
{
|
||||
stop_thread();
|
||||
}
|
||||
|
||||
private:
|
||||
struct State
|
||||
{
|
||||
State()
|
||||
: net_up(false),
|
||||
sleep(false)
|
||||
{
|
||||
}
|
||||
|
||||
State(bool net_up_arg, const std::string& iface_arg, bool sleep_arg)
|
||||
: net_up(net_up_arg),
|
||||
iface(iface_arg),
|
||||
sleep(sleep_arg)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const State& other) const
|
||||
{
|
||||
return net_up == other.net_up && iface == other.iface && sleep == other.sleep;
|
||||
}
|
||||
|
||||
bool operator!=(const State& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "[net_up=" << net_up << " iface=" << iface << " sleep=" << sleep << ']';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool net_up;
|
||||
std::string iface;
|
||||
bool sleep;
|
||||
};
|
||||
|
||||
void stop_thread()
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
if (runloop.defined())
|
||||
CFRunLoopStop(runloop());
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void thread_func()
|
||||
{
|
||||
runloop.reset(CFRunLoopGetCurrent(), CF::BORROW);
|
||||
Log::Context logctx(logwrap);
|
||||
try {
|
||||
// set up dynamic store query object
|
||||
dstore.reset(SCDynamicStoreCreate(kCFAllocatorDefault,
|
||||
CFSTR("OpenVPN_MacLifeCycle"),
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
// init state
|
||||
state = State(net_up(), primary_interface(), false);
|
||||
prev_state = state;
|
||||
paused = false;
|
||||
|
||||
// enable sleep/wakeup notifications
|
||||
mac_sleep_start();
|
||||
|
||||
// enable network reachability notifications
|
||||
reachability_tracker_schedule();
|
||||
|
||||
// enable interface change notifications
|
||||
iface_watch();
|
||||
|
||||
// process event loop until CFRunLoopStop is called from parent thread
|
||||
CFRunLoopRun();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle Exception: " << e.what());
|
||||
}
|
||||
|
||||
// cleanup
|
||||
cancel_action_timer();
|
||||
mac_sleep_stop();
|
||||
reachability_tracker_cancel();
|
||||
dstore.reset();
|
||||
}
|
||||
|
||||
std::string primary_interface()
|
||||
{
|
||||
CF::Dict dict(CF::DynamicStoreCopyDict(dstore, "State:/Network/Global/IPv4"));
|
||||
return CF::dict_get_str(dict, "PrimaryInterface");
|
||||
}
|
||||
|
||||
bool net_up()
|
||||
{
|
||||
ReachabilityViaWiFi r;
|
||||
return ReachabilityViaWiFi::status_from_flags(r.flags()) != ReachabilityInterface::NotReachable;
|
||||
}
|
||||
|
||||
void iface_watch()
|
||||
{
|
||||
SCDynamicStoreContext context = {0, this, nullptr, nullptr, nullptr};
|
||||
CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
|
||||
CFSTR("OpenVPN_MacLifeCycle_iface_watch"),
|
||||
iface_watch_callback_static,
|
||||
&context));
|
||||
if (!ds.defined())
|
||||
throw mac_lifecycle_error("SCDynamicStoreCreate");
|
||||
CF::MutableArray watched_keys(CF::mutable_array());
|
||||
CF::array_append_str(watched_keys, "State:/Network/Global/IPv4");
|
||||
//CF::array_append_str(watched_keys, "State:/Network/Global/IPv6");
|
||||
if (!watched_keys.defined())
|
||||
throw mac_lifecycle_error("watched_keys is undefined");
|
||||
if (!SCDynamicStoreSetNotificationKeys(ds(),
|
||||
watched_keys(),
|
||||
nullptr))
|
||||
throw mac_lifecycle_error("SCDynamicStoreSetNotificationKeys failed");
|
||||
CF::RunLoopSource rls(SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, ds(), 0));
|
||||
if (!rls.defined())
|
||||
throw mac_lifecycle_error("SCDynamicStoreCreateRunLoopSource failed");
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
static void iface_watch_callback_static(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
|
||||
{
|
||||
MacLifeCycle *self = (MacLifeCycle *)arg;
|
||||
self->iface_watch_callback(store, changedKeys);
|
||||
}
|
||||
|
||||
void iface_watch_callback(SCDynamicStoreRef store, CFArrayRef changedKeys)
|
||||
{
|
||||
state.iface = primary_interface();
|
||||
OPENVPN_LOG("MacLifeCycle NET_IFACE " << state.iface);
|
||||
schedule_action_timer(1);
|
||||
}
|
||||
|
||||
virtual void notify_sleep()
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle SLEEP");
|
||||
state.sleep = true;
|
||||
schedule_action_timer(0);
|
||||
}
|
||||
|
||||
virtual void notify_wakeup()
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle WAKEUP");
|
||||
state.sleep = false;
|
||||
schedule_action_timer(1);
|
||||
}
|
||||
|
||||
virtual void reachability_tracker_event(const ReachabilityBase& rb, SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
if (rb.vtype() == ReachabilityBase::WiFi)
|
||||
{
|
||||
state.net_up = (rb.vstatus(flags) != ReachabilityInterface::NotReachable);
|
||||
OPENVPN_LOG("MacLifeCycle NET_STATE " << state.net_up);
|
||||
schedule_action_timer(1);
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_action_timer(const int seconds)
|
||||
{
|
||||
cancel_action_timer();
|
||||
if (seconds)
|
||||
{
|
||||
CFRunLoopTimerContext context = { 0, this, nullptr, nullptr, nullptr };
|
||||
action_timer.reset(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + seconds, 0, 0, 0, action_timer_callback_static, &context));
|
||||
if (action_timer.defined())
|
||||
CFRunLoopAddTimer(CFRunLoopGetCurrent(), action_timer(), kCFRunLoopCommonModes);
|
||||
else
|
||||
OPENVPN_LOG("MacLifeCycle::schedule_action_timer: failed to create timer");
|
||||
}
|
||||
else
|
||||
action_timer_callback(nullptr);
|
||||
}
|
||||
|
||||
void cancel_action_timer()
|
||||
{
|
||||
if (action_timer.defined())
|
||||
{
|
||||
CFRunLoopTimerInvalidate(action_timer());
|
||||
action_timer.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void action_timer_callback_static(CFRunLoopTimerRef timer, void *info)
|
||||
{
|
||||
MacLifeCycle* self = (MacLifeCycle*)info;
|
||||
self->action_timer_callback(timer);
|
||||
}
|
||||
|
||||
void action_timer_callback(CFRunLoopTimerRef timer)
|
||||
{
|
||||
try {
|
||||
if (state != prev_state)
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle ACTION pause=" << paused << " state=" << state.to_string() << " prev=" << prev_state.to_string());
|
||||
if (paused)
|
||||
{
|
||||
if (!state.sleep && state.net_up)
|
||||
{
|
||||
nc->cln_resume();
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.sleep)
|
||||
{
|
||||
nc->cln_pause("sleep");
|
||||
paused = true;
|
||||
}
|
||||
else if (!state.net_up)
|
||||
{
|
||||
nc->cln_pause("network-unavailable");
|
||||
paused = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.iface != prev_state.iface)
|
||||
nc->cln_reconnect(0);
|
||||
}
|
||||
}
|
||||
prev_state = state;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle::action_timer_callback: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
NotifyCallback* nc;
|
||||
std::thread* thread;
|
||||
CF::RunLoop runloop; // run loop in thread
|
||||
CF::DynamicStore dstore;
|
||||
State state;
|
||||
State prev_state;
|
||||
bool paused;
|
||||
CF::Timer action_timer;
|
||||
Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLE_MACSLEEP_H
|
||||
#define OPENVPN_APPLE_MACSLEEP_H
|
||||
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_interface.h>
|
||||
#include <mach/mach_init.h>
|
||||
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class MacSleep
|
||||
{
|
||||
MacSleep(const MacSleep&) = delete;
|
||||
MacSleep& operator=(const MacSleep&) = delete;
|
||||
|
||||
public:
|
||||
MacSleep()
|
||||
: root_port(0),
|
||||
notifyPortRef(nullptr),
|
||||
notifierObject(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MacSleep()
|
||||
{
|
||||
mac_sleep_stop();
|
||||
}
|
||||
|
||||
bool mac_sleep_start()
|
||||
{
|
||||
if (!root_port)
|
||||
{
|
||||
root_port = IORegisterForSystemPower(this, ¬ifyPortRef, callback_static, ¬ifierObject);
|
||||
if (!root_port)
|
||||
return false;
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mac_sleep_stop()
|
||||
{
|
||||
if (root_port)
|
||||
{
|
||||
// remove the sleep notification port from the application runloop
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
|
||||
IONotificationPortGetRunLoopSource(notifyPortRef),
|
||||
kCFRunLoopCommonModes);
|
||||
|
||||
// deregister for system sleep notifications
|
||||
IODeregisterForSystemPower(¬ifierObject);
|
||||
|
||||
// IORegisterForSystemPower implicitly opens the Root Power Domain IOService
|
||||
// so we close it here
|
||||
IOServiceClose(root_port);
|
||||
|
||||
// destroy the notification port allocated by IORegisterForSystemPower
|
||||
IONotificationPortDestroy(notifyPortRef);
|
||||
|
||||
// reset object members
|
||||
root_port = 0;
|
||||
notifyPortRef = nullptr;
|
||||
notifierObject = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void notify_sleep() = 0;
|
||||
virtual void notify_wakeup() = 0;
|
||||
|
||||
private:
|
||||
static void callback_static(void* arg, io_service_t service, natural_t messageType, void *messageArgument)
|
||||
{
|
||||
MacSleep* self = (MacSleep*)arg;
|
||||
self->callback(service, messageType, messageArgument);
|
||||
}
|
||||
|
||||
void callback(io_service_t service, natural_t messageType, void *messageArgument)
|
||||
{
|
||||
switch (messageType)
|
||||
{
|
||||
case kIOMessageCanSystemSleep:
|
||||
IOAllowPowerChange(root_port, (long)messageArgument);
|
||||
break;
|
||||
case kIOMessageSystemWillSleep:
|
||||
notify_sleep();
|
||||
IOAllowPowerChange(root_port, (long)messageArgument);
|
||||
break;
|
||||
case kIOMessageSystemHasPoweredOn:
|
||||
notify_wakeup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// a reference to the Root Power Domain IOService
|
||||
io_connect_t root_port;
|
||||
|
||||
// notification port allocated by IORegisterForSystemPower
|
||||
IONotificationPortRef notifyPortRef;
|
||||
|
||||
// notifier object, used to deregister later
|
||||
io_object_t notifierObject;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLE_MACVER_H
|
||||
#define OPENVPN_APPLE_MACVER_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/apple/ver.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Mac {
|
||||
class Version : public AppleVersion
|
||||
{
|
||||
public:
|
||||
// Mac OS X versions
|
||||
// 15.x.x OS X 10.11.x El Capitan
|
||||
// 14.x.x OS X 10.10.x Yosemite
|
||||
// 13.x.x OS X 10.9.x Mavericks
|
||||
// 12.x.x OS X 10.8.x Mountain Lion
|
||||
// 11.x.x OS X 10.7.x Lion
|
||||
// 10.x.x OS X 10.6.x Snow Leopard
|
||||
// 9.x.x OS X 10.5.x Leopard
|
||||
// 8.x.x OS X 10.4.x Tiger
|
||||
// 7.x.x OS X 10.3.x Panther
|
||||
// 6.x.x OS X 10.2.x Jaguar
|
||||
// 5.x OS X 10.1.x Puma
|
||||
|
||||
enum {
|
||||
OSX_10_11=15,
|
||||
OSX_10_10=14,
|
||||
OSX_10_9=13,
|
||||
OSX_10_8=12,
|
||||
OSX_10_7=11,
|
||||
OSX_10_6=10,
|
||||
};
|
||||
|
||||
Version()
|
||||
{
|
||||
char str[256];
|
||||
size_t size = sizeof(str);
|
||||
int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0);
|
||||
if (!ret)
|
||||
init(std::string(str, size));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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-2016 OpenVPN Technologies, 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_APPLE_RUNLOOP_H
|
||||
#define OPENVPN_APPLE_RUNLOOP_H
|
||||
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
OPENVPN_CF_WRAP(RunLoop, runloop_cast, CFRunLoopRef, CFRunLoopGetTypeID);
|
||||
OPENVPN_CF_WRAP(RunLoopSource, runloop_source_cast, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLE_SCDYNSTORE_H
|
||||
#define OPENVPN_APPLE_SCDYNSTORE_H
|
||||
|
||||
#include <SystemConfiguration/SCDynamicStore.h>
|
||||
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
OPENVPN_CF_WRAP(DynamicStore, dynamic_store_cast, SCDynamicStoreRef, SCDynamicStoreGetTypeID)
|
||||
|
||||
template <typename RET, typename KEY>
|
||||
inline RET DynamicStoreCopy(const DynamicStore& ds, const KEY& key)
|
||||
{
|
||||
String keystr = string(key);
|
||||
return RET(RET::cast(SCDynamicStoreCopyValue(ds(), keystr())));
|
||||
}
|
||||
|
||||
template <typename KEY>
|
||||
inline Dict DynamicStoreCopyDict(const DynamicStore& ds, const KEY& key)
|
||||
{
|
||||
Dict dict = DynamicStoreCopy<Dict>(ds, key);
|
||||
if (dict.defined())
|
||||
return dict;
|
||||
else
|
||||
return CF::empty_dict();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLE_VER_H
|
||||
#define OPENVPN_APPLE_VER_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class AppleVersion
|
||||
{
|
||||
public:
|
||||
int major() const { return ver[0]; }
|
||||
int minor() const { return ver[1]; }
|
||||
int build() const { return ver[2]; }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << major() << '.' << minor() << '.' << build();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
AppleVersion()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// verstr should be in the form major.minor.build
|
||||
void init(const std::string& verstr)
|
||||
{
|
||||
typedef std::vector<std::string> StringList;
|
||||
reset();
|
||||
StringList sl;
|
||||
sl.reserve(3);
|
||||
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, verstr, '.');
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
if (i < sl.size())
|
||||
parse_number(sl[i], ver[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void reset()
|
||||
{
|
||||
ver[0] = ver[1] = ver[2] = -1;
|
||||
}
|
||||
|
||||
int ver[3];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,464 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CF_CF_H
|
||||
#define OPENVPN_APPLECRYPTO_CF_CF_H
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
// Wrapper classes for Apple Core Foundation objects.
|
||||
|
||||
#define OPENVPN_CF_WRAP(cls, castmeth, cftype, idmeth) \
|
||||
template <> \
|
||||
struct Type<cftype> \
|
||||
{ \
|
||||
static CFTypeRef cast(CFTypeRef obj) \
|
||||
{ \
|
||||
if (obj && CFGetTypeID(obj) == idmeth()) \
|
||||
return obj; \
|
||||
else \
|
||||
return nullptr; \
|
||||
} \
|
||||
}; \
|
||||
typedef Wrap<cftype> cls; \
|
||||
inline cls castmeth(CFTypeRef obj) \
|
||||
{ \
|
||||
CFTypeRef o = Type<cftype>::cast(obj); \
|
||||
if (o) \
|
||||
return cls(cftype(o), BORROW); \
|
||||
else \
|
||||
return cls(); \
|
||||
}
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF
|
||||
{
|
||||
enum Own {
|
||||
OWN,
|
||||
BORROW
|
||||
};
|
||||
|
||||
template <typename T> struct Type {};
|
||||
|
||||
template <typename T>
|
||||
class Wrap
|
||||
{
|
||||
public:
|
||||
Wrap() : obj_(nullptr) {}
|
||||
|
||||
// Set own=BORROW if we don't currently own the object
|
||||
explicit Wrap(T obj, const Own own=OWN)
|
||||
{
|
||||
if (own == BORROW && obj)
|
||||
CFRetain(obj);
|
||||
obj_ = obj;
|
||||
}
|
||||
|
||||
Wrap(const Wrap& other)
|
||||
{
|
||||
obj_ = other.obj_;
|
||||
if (obj_)
|
||||
CFRetain(obj_);
|
||||
}
|
||||
|
||||
Wrap& operator=(const Wrap& other)
|
||||
{
|
||||
if (other.obj_)
|
||||
CFRetain(other.obj_);
|
||||
if (obj_)
|
||||
CFRelease(obj_);
|
||||
obj_ = other.obj_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Wrap(Wrap&& other) noexcept
|
||||
{
|
||||
obj_ = other.obj_;
|
||||
other.obj_ = nullptr;
|
||||
}
|
||||
|
||||
Wrap& operator=(Wrap&& other) noexcept
|
||||
{
|
||||
if (obj_)
|
||||
CFRelease(obj_);
|
||||
obj_ = other.obj_;
|
||||
other.obj_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(Wrap& other)
|
||||
{
|
||||
std::swap(obj_, other.obj_);
|
||||
}
|
||||
|
||||
void reset(T obj=nullptr, const Own own=OWN)
|
||||
{
|
||||
if (own == BORROW && obj)
|
||||
CFRetain(obj);
|
||||
if (obj_)
|
||||
CFRelease(obj_);
|
||||
obj_ = obj;
|
||||
}
|
||||
|
||||
bool defined() const { return obj_ != nullptr; }
|
||||
|
||||
T operator()() const { return obj_; }
|
||||
|
||||
CFTypeRef generic() const { return (CFTypeRef)obj_; }
|
||||
|
||||
static T cast(CFTypeRef obj) { return T(Type<T>::cast(obj)); }
|
||||
|
||||
static Wrap from_generic(CFTypeRef obj, const Own own=OWN)
|
||||
{
|
||||
return Wrap(cast(obj), own);
|
||||
}
|
||||
|
||||
T release()
|
||||
{
|
||||
T ret = obj_;
|
||||
obj_ = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
CFTypeRef generic_release()
|
||||
{
|
||||
T ret = obj_;
|
||||
obj_ = nullptr;
|
||||
return (CFTypeRef)ret;
|
||||
}
|
||||
|
||||
// Intended for use with Core Foundation methods that require
|
||||
// a T* for saving a (non-borrowed) return value
|
||||
T* mod_ref()
|
||||
{
|
||||
if (obj_)
|
||||
{
|
||||
CFRelease(obj_);
|
||||
obj_ = nullptr;
|
||||
}
|
||||
return &obj_;
|
||||
}
|
||||
|
||||
void show() const
|
||||
{
|
||||
if (obj_)
|
||||
CFShow(obj_);
|
||||
else
|
||||
std::cerr << "CF_UNDEFINED" << std::endl;
|
||||
}
|
||||
|
||||
virtual ~Wrap()
|
||||
{
|
||||
if (obj_)
|
||||
CFRelease(obj_);
|
||||
}
|
||||
|
||||
private:
|
||||
Wrap& operator=(T obj); // prevent use because no way to pass ownership parameter
|
||||
|
||||
T obj_;
|
||||
};
|
||||
|
||||
// essentially a vector of void *, used as source for array and dictionary constructors
|
||||
typedef BufferAllocatedType<CFTypeRef> SrcList;
|
||||
|
||||
// common CF types
|
||||
|
||||
OPENVPN_CF_WRAP(String, string_cast, CFStringRef, CFStringGetTypeID)
|
||||
OPENVPN_CF_WRAP(Number, number_cast, CFNumberRef, CFNumberGetTypeID)
|
||||
OPENVPN_CF_WRAP(Bool, bool_cast, CFBooleanRef, CFBooleanGetTypeID)
|
||||
OPENVPN_CF_WRAP(Data, data_cast, CFDataRef, CFDataGetTypeID)
|
||||
OPENVPN_CF_WRAP(Array, array_cast, CFArrayRef, CFArrayGetTypeID)
|
||||
OPENVPN_CF_WRAP(MutableArray, mutable_array_cast, CFMutableArrayRef, CFArrayGetTypeID)
|
||||
OPENVPN_CF_WRAP(Dict, dict_cast, CFDictionaryRef, CFDictionaryGetTypeID)
|
||||
OPENVPN_CF_WRAP(MutableDict, mutable_dict_cast, CFMutableDictionaryRef, CFDictionaryGetTypeID)
|
||||
OPENVPN_CF_WRAP(Error, error_cast, CFErrorRef, CFErrorGetTypeID);
|
||||
|
||||
// generic CFTypeRef wrapper
|
||||
|
||||
typedef Wrap<CFTypeRef> Generic;
|
||||
|
||||
inline Generic generic_cast(CFTypeRef obj)
|
||||
{
|
||||
return Generic(obj, BORROW);
|
||||
}
|
||||
|
||||
// constructors
|
||||
|
||||
inline String string(const char *str)
|
||||
{
|
||||
return String(CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8));
|
||||
}
|
||||
|
||||
inline String string(CFStringRef str)
|
||||
{
|
||||
return String(str, BORROW);
|
||||
}
|
||||
|
||||
inline String string(const String& str)
|
||||
{
|
||||
return String(str);
|
||||
}
|
||||
|
||||
inline String string(const std::string& str)
|
||||
{
|
||||
return String(CFStringCreateWithCString(kCFAllocatorDefault, str.c_str(), kCFStringEncodingUTF8));
|
||||
}
|
||||
|
||||
inline String string(const std::string* str)
|
||||
{
|
||||
return String(CFStringCreateWithCString(kCFAllocatorDefault, str->c_str(), kCFStringEncodingUTF8));
|
||||
}
|
||||
|
||||
inline Number number_from_int(const int n)
|
||||
{
|
||||
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n));
|
||||
}
|
||||
|
||||
inline Number number_from_int32(const SInt32 n)
|
||||
{
|
||||
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &n));
|
||||
}
|
||||
|
||||
inline Number number_from_long_long(const long long n)
|
||||
{
|
||||
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &n));
|
||||
}
|
||||
|
||||
inline Number number_from_index(const CFIndex n)
|
||||
{
|
||||
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &n));
|
||||
}
|
||||
|
||||
inline Data data(const void *bytes, CFIndex length)
|
||||
{
|
||||
return Data(CFDataCreate(kCFAllocatorDefault, (const UInt8 *)bytes, length));
|
||||
}
|
||||
|
||||
inline Array array(const void **values, CFIndex numValues)
|
||||
{
|
||||
return Array(CFArrayCreate(kCFAllocatorDefault, values, numValues, &kCFTypeArrayCallBacks));
|
||||
}
|
||||
|
||||
inline Array array(const SrcList& values)
|
||||
{
|
||||
return array((const void **)values.c_data(), values.size());
|
||||
}
|
||||
|
||||
inline Dict dict(const void **keys, const void **values, CFIndex numValues)
|
||||
{
|
||||
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
|
||||
keys,
|
||||
values,
|
||||
numValues,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks));
|
||||
}
|
||||
|
||||
inline Dict dict(const SrcList& keys, const SrcList& values)
|
||||
{
|
||||
return dict((const void **)keys.c_data(), (const void **)values.c_data(), std::min(keys.size(), values.size()));
|
||||
}
|
||||
|
||||
inline Dict const_dict(MutableDict& mdict)
|
||||
{
|
||||
return Dict(mdict(), CF::BORROW);
|
||||
}
|
||||
|
||||
inline Array const_array(MutableArray& marray)
|
||||
{
|
||||
return Array(marray(), CF::BORROW);
|
||||
}
|
||||
|
||||
inline Dict empty_dict()
|
||||
{
|
||||
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks));
|
||||
}
|
||||
|
||||
inline MutableArray mutable_array(const CFIndex capacity=0)
|
||||
{
|
||||
return MutableArray(CFArrayCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeArrayCallBacks));
|
||||
}
|
||||
|
||||
inline MutableDict mutable_dict(const CFIndex capacity=0)
|
||||
{
|
||||
return MutableDict(CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
|
||||
}
|
||||
|
||||
template <typename DICT>
|
||||
inline MutableDict mutable_dict_copy(const DICT& dict, const CFIndex capacity=0)
|
||||
{
|
||||
if (dict.defined())
|
||||
return MutableDict(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity, dict()));
|
||||
else
|
||||
return mutable_dict(capacity);
|
||||
}
|
||||
|
||||
inline Error error(CFStringRef domain, CFIndex code, CFDictionaryRef userInfo)
|
||||
{
|
||||
return Error(CFErrorCreate(kCFAllocatorDefault, domain, code, userInfo));
|
||||
}
|
||||
|
||||
// accessors
|
||||
|
||||
template <typename ARRAY>
|
||||
inline CFIndex array_len(const ARRAY& array)
|
||||
{
|
||||
if (array.defined())
|
||||
return CFArrayGetCount(array());
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename DICT>
|
||||
inline CFIndex dict_len(const DICT& dict)
|
||||
{
|
||||
if (dict.defined())
|
||||
return CFDictionaryGetCount(dict());
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename ARRAY>
|
||||
inline CFTypeRef array_index(const ARRAY& array, const CFIndex idx)
|
||||
{
|
||||
if (array.defined() && CFArrayGetCount(array()) > idx)
|
||||
return CFArrayGetValueAtIndex(array(), idx);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename DICT, typename KEY>
|
||||
inline CFTypeRef dict_index(const DICT& dict, const KEY& key)
|
||||
{
|
||||
if (dict.defined())
|
||||
{
|
||||
String keystr = string(key);
|
||||
if (keystr.defined())
|
||||
return CFDictionaryGetValue(dict(), keystr());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// string methods
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(cppstring_error);
|
||||
|
||||
inline std::string cppstring(CFStringRef str)
|
||||
{
|
||||
const CFStringEncoding encoding = kCFStringEncodingUTF8;
|
||||
if (str)
|
||||
{
|
||||
const CFIndex len = CFStringGetLength(str);
|
||||
if (len > 0)
|
||||
{
|
||||
const CFIndex maxsize = CFStringGetMaximumSizeForEncoding(len, encoding);
|
||||
char *buf = new char[maxsize];
|
||||
const Boolean status = CFStringGetCString(str, buf, maxsize, encoding);
|
||||
if (status)
|
||||
{
|
||||
std::string ret(buf);
|
||||
delete [] buf;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete [] buf;
|
||||
throw cppstring_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
inline std::string cppstring(const String& str)
|
||||
{
|
||||
return cppstring(str());
|
||||
}
|
||||
|
||||
inline std::string description(CFTypeRef obj)
|
||||
{
|
||||
if (obj)
|
||||
{
|
||||
String s(CFCopyDescription(obj));
|
||||
return cppstring(s);
|
||||
}
|
||||
else
|
||||
return "UNDEF";
|
||||
}
|
||||
|
||||
// format an array of strings (non-string elements in array are ignored)
|
||||
template <typename ARRAY>
|
||||
inline std::string array_to_string(const ARRAY& array, const char delim=',')
|
||||
{
|
||||
std::ostringstream os;
|
||||
const CFIndex len = array_len(array);
|
||||
if (len)
|
||||
{
|
||||
bool sep = false;
|
||||
for (CFIndex i = 0; i < len; ++i)
|
||||
{
|
||||
const String v(string_cast(array_index(array, i)));
|
||||
if (v.defined())
|
||||
{
|
||||
if (sep)
|
||||
os << delim;
|
||||
os << cppstring(v);
|
||||
sep = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline bool string_equal(const String& s1, const String& s2, const CFStringCompareFlags compareOptions = 0)
|
||||
{
|
||||
return s1.defined() && s2.defined() && CFStringCompare(s1(), s2(), compareOptions) == kCFCompareEqualTo;
|
||||
}
|
||||
|
||||
// property lists
|
||||
inline Data plist(CFTypeRef obj)
|
||||
{
|
||||
return Data(CFPropertyListCreateData(kCFAllocatorDefault,
|
||||
obj,
|
||||
kCFPropertyListBinaryFormat_v1_0,
|
||||
0,
|
||||
nullptr));
|
||||
}
|
||||
|
||||
} // namespace CF
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_APPLECRYPTO_CF_CF_H
|
||||
@@ -0,0 +1,247 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CF_CFHELPER_H
|
||||
#define OPENVPN_APPLECRYPTO_CF_CFHELPER_H
|
||||
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
|
||||
// These methods build on the Wrapper classes for Apple Core Foundation objects
|
||||
// defined in cf.hpp. They add additional convenience methods, such as dictionary
|
||||
// lookup.
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
|
||||
inline CFTypeRef mutable_dict_new()
|
||||
{
|
||||
return CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
}
|
||||
|
||||
inline CFTypeRef mutable_array_new()
|
||||
{
|
||||
return CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
}
|
||||
|
||||
// Lookup or create (if absent) an item in a mutable dictionary.
|
||||
// Return the item, which will be owned by base.
|
||||
template <typename KEY>
|
||||
inline CFTypeRef dict_get_create(CFMutableDictionaryRef base,
|
||||
const KEY& key,
|
||||
CFTypeRef (*create_method)())
|
||||
{
|
||||
if (base)
|
||||
{
|
||||
String keystr = string(key);
|
||||
CFTypeRef ret = CFDictionaryGetValue(base, keystr()); // try lookup first
|
||||
if (!ret)
|
||||
{
|
||||
// doesn't exist, must create
|
||||
ret = (*create_method)();
|
||||
CFDictionaryAddValue(base, keystr(), ret);
|
||||
CFRelease(ret); // because ret is now owned by base
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// lookup a dict in another dict (base) and return or create if absent
|
||||
template <typename KEY>
|
||||
inline MutableDict dict_get_create_dict(MutableDict& base, const KEY& key)
|
||||
{
|
||||
String keystr = string(key);
|
||||
return mutable_dict_cast(dict_get_create(base(), keystr(), mutable_dict_new));
|
||||
}
|
||||
|
||||
// lookup an array in a dict (base) and return or create if absent
|
||||
template <typename KEY>
|
||||
inline MutableArray dict_get_create_array(MutableDict& base, const KEY& key)
|
||||
{
|
||||
String keystr = string(key);
|
||||
return mutable_array_cast(dict_get_create(base(), keystr(), mutable_array_new));
|
||||
}
|
||||
|
||||
// lookup an object in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline CFTypeRef dict_get_obj(const DICT& dict, const KEY& key)
|
||||
{
|
||||
return dict_index(dict, key);
|
||||
}
|
||||
|
||||
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline std::string dict_get_str(const DICT& dict, const KEY& key)
|
||||
{
|
||||
return cppstring(string_cast(dict_index(dict, key)));
|
||||
}
|
||||
|
||||
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline std::string dict_get_str(const DICT& dict, const KEY& key, const std::string& default_value)
|
||||
{
|
||||
String str(string_cast(dict_index(dict, key)));
|
||||
if (str.defined())
|
||||
return cppstring(str());
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// lookup an integer in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline int dict_get_int(const DICT& dict, const KEY& key, const int default_value)
|
||||
{
|
||||
int ret;
|
||||
Number num = number_cast(dict_index(dict, key));
|
||||
if (num.defined() && CFNumberGetValue(num(), kCFNumberIntType, &ret))
|
||||
return ret;
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// lookup a boolean in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline bool dict_get_bool(const DICT& dict, const KEY& key, const bool default_value)
|
||||
{
|
||||
Bool b = bool_cast(dict_index(dict, key));
|
||||
if (b.defined())
|
||||
{
|
||||
if (b() == kCFBooleanTrue)
|
||||
return true;
|
||||
else if (b() == kCFBooleanFalse)
|
||||
return false;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// like CFDictionarySetValue, but no-op if any args are NULL
|
||||
inline void dictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value)
|
||||
{
|
||||
if (theDict && key && value)
|
||||
CFDictionarySetValue(theDict, key, value);
|
||||
}
|
||||
|
||||
// like CFArrayAppendValue, but no-op if any args are NULL
|
||||
inline void arrayAppendValue(CFMutableArrayRef theArray, const void *value)
|
||||
{
|
||||
if (theArray && value)
|
||||
CFArrayAppendValue(theArray, value);
|
||||
}
|
||||
|
||||
// set a CFTypeRef in a mutable dictionary
|
||||
template <typename KEY>
|
||||
inline void dict_set_obj(MutableDict& dict, const KEY& key, CFTypeRef value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
dictionarySetValue(dict(), keystr(), value);
|
||||
}
|
||||
|
||||
// set a string in a mutable dictionary
|
||||
|
||||
template <typename KEY, typename VALUE>
|
||||
inline void dict_set_str(MutableDict& dict, const KEY& key, const VALUE& value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
String valstr = string(value);
|
||||
dictionarySetValue(dict(), keystr(), valstr());
|
||||
}
|
||||
|
||||
// set a number in a mutable dictionary
|
||||
|
||||
template <typename KEY>
|
||||
inline void dict_set_int(MutableDict& dict, const KEY& key, int value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
Number num = number_from_int(value);
|
||||
dictionarySetValue(dict(), keystr(), num());
|
||||
}
|
||||
|
||||
template <typename KEY>
|
||||
inline void dict_set_int32(MutableDict& dict, const KEY& key, SInt32 value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
Number num = number_from_int32(value);
|
||||
dictionarySetValue(dict(), keystr(), num());
|
||||
}
|
||||
|
||||
template <typename KEY>
|
||||
inline void dict_set_long_long(MutableDict& dict, const KEY& key, long long value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
Number num = number_from_long_long(value);
|
||||
dictionarySetValue(dict(), keystr(), num());
|
||||
}
|
||||
|
||||
template <typename KEY>
|
||||
inline void dict_set_index(MutableDict& dict, const KEY& key, CFIndex value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
Number num = number_from_index(value);
|
||||
dictionarySetValue((CFMutableDictionaryRef)dict(), keystr(), num());
|
||||
}
|
||||
|
||||
// set a boolean in a mutable dictionary
|
||||
|
||||
template <typename KEY>
|
||||
inline void dict_set_bool(MutableDict& dict, const KEY& key, bool value)
|
||||
{
|
||||
String keystr = string(key);
|
||||
CFBooleanRef boolref = value ? kCFBooleanTrue : kCFBooleanFalse;
|
||||
dictionarySetValue(dict(), keystr(), boolref);
|
||||
}
|
||||
|
||||
// append string to a mutable array
|
||||
|
||||
template <typename VALUE>
|
||||
inline void array_append_str(MutableArray& array, const VALUE& value)
|
||||
{
|
||||
String valstr = string(value);
|
||||
arrayAppendValue(array(), valstr());
|
||||
}
|
||||
|
||||
// append a number to a mutable array
|
||||
|
||||
inline void array_append_int(MutableArray& array, int value)
|
||||
{
|
||||
Number num = number_from_int(value);
|
||||
arrayAppendValue(array(), num());
|
||||
}
|
||||
|
||||
inline void array_append_int32(MutableArray& array, SInt32 value)
|
||||
{
|
||||
Number num = number_from_int32(value);
|
||||
arrayAppendValue(array(), num());
|
||||
}
|
||||
|
||||
inline void array_append_long_long(MutableArray& array, long long value)
|
||||
{
|
||||
Number num = number_from_long_long(value);
|
||||
arrayAppendValue(array(), num());
|
||||
}
|
||||
|
||||
inline void array_append_index(MutableArray& array, CFIndex value)
|
||||
{
|
||||
Number num = number_from_index(value);
|
||||
arrayAppendValue(array(), num());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CF_CFSEC_H
|
||||
#define OPENVPN_APPLECRYPTO_CF_CFSEC_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#include <Security/SecCertificate.h>
|
||||
#include <Security/SecIdentity.h>
|
||||
#include <Security/SecPolicy.h>
|
||||
#include <Security/SecTrust.h>
|
||||
|
||||
#ifndef OPENVPN_PLATFORM_IPHONE
|
||||
#include <Security/SecKeychain.h>
|
||||
#include <Security/SecAccess.h>
|
||||
#endif
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
|
||||
// Define C++ wrappings for Apple security-related objects.
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
OPENVPN_CF_WRAP(Cert, cert_cast, SecCertificateRef, SecCertificateGetTypeID)
|
||||
OPENVPN_CF_WRAP(Key, key_cast, SecKeyRef, SecKeyGetTypeID)
|
||||
OPENVPN_CF_WRAP(Identity, identity_cast, SecIdentityRef, SecIdentityGetTypeID)
|
||||
OPENVPN_CF_WRAP(Policy, policy_cast, SecPolicyRef, SecPolicyGetTypeID)
|
||||
OPENVPN_CF_WRAP(Trust, trust_cast, SecTrustRef, SecTrustGetTypeID)
|
||||
#ifndef OPENVPN_PLATFORM_IPHONE
|
||||
OPENVPN_CF_WRAP(Keychain, keychain_cast, SecKeychainRef, SecKeychainGetTypeID)
|
||||
OPENVPN_CF_WRAP(Access, access_cast, SecAccessRef, SecAccessGetTypeID)
|
||||
#endif
|
||||
} // namespace CF
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_APPLECRYPTO_CF_CFSEC_H
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CF_CFTIMER_H
|
||||
#define OPENVPN_APPLECRYPTO_CF_CFTIMER_H
|
||||
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
OPENVPN_CF_WRAP(Timer, timer_cast, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CF_ERROR_H
|
||||
#define OPENVPN_APPLECRYPTO_CF_ERROR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
// An exception object that encapsulates Apple Core Foundation errors.
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// string exception class
|
||||
class CFException : public std::exception
|
||||
{
|
||||
public:
|
||||
CFException(const std::string& text)
|
||||
{
|
||||
errtxt = text;
|
||||
}
|
||||
|
||||
CFException(const std::string& text, const OSStatus status)
|
||||
{
|
||||
set_errtxt(text, status);
|
||||
}
|
||||
|
||||
virtual const char* what() const throw() { return errtxt.c_str(); }
|
||||
std::string what_str() const { return errtxt; }
|
||||
|
||||
virtual ~CFException() throw() {}
|
||||
|
||||
private:
|
||||
void set_errtxt(const std::string& text, const OSStatus status)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << text << ": OSX Error code=" << status;
|
||||
errtxt = s.str();
|
||||
}
|
||||
|
||||
std::string errtxt;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_APPLECRYPTO_CF_ERROR_H
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CRYPTO_API_H
|
||||
#define OPENVPN_APPLECRYPTO_CRYPTO_API_H
|
||||
|
||||
#include <openvpn/applecrypto/crypto/cipher.hpp>
|
||||
#include <openvpn/applecrypto/crypto/ciphergcm.hpp>
|
||||
#include <openvpn/applecrypto/crypto/digest.hpp>
|
||||
#include <openvpn/applecrypto/crypto/hmac.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// type container for Apple Crypto-level API
|
||||
struct AppleCryptoAPI {
|
||||
// cipher
|
||||
typedef AppleCrypto::CipherContext CipherContext;
|
||||
typedef AppleCrypto::CipherContextGCM CipherContextGCM;
|
||||
|
||||
// digest
|
||||
typedef AppleCrypto::DigestContext DigestContext;
|
||||
|
||||
// HMAC
|
||||
typedef AppleCrypto::HMACContext HMACContext;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,200 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Wrap the Apple cipher API defined in <CommonCrypto/CommonCryptor.h> so
|
||||
// that it can be used as part of the crypto layer of the OpenVPN core.
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_CRYPTO_CIPHER_H
|
||||
#define OPENVPN_APPLECRYPTO_CRYPTO_CIPHER_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <CommonCrypto/CommonCryptor.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/crypto/static_key.hpp>
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/applecrypto/cf/error.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace AppleCrypto {
|
||||
class CipherContext
|
||||
{
|
||||
CipherContext(const CipherContext&) = delete;
|
||||
CipherContext& operator=(const CipherContext&) = delete;
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_mode_error);
|
||||
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_uninitialized);
|
||||
OPENVPN_EXCEPTION(apple_cipher_error);
|
||||
|
||||
// mode parameter for constructor
|
||||
enum {
|
||||
MODE_UNDEF = -1,
|
||||
ENCRYPT = kCCEncrypt,
|
||||
DECRYPT = kCCDecrypt
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_IV_LENGTH = 16,
|
||||
CIPH_CBC_MODE = 0
|
||||
};
|
||||
|
||||
CipherContext()
|
||||
: cinfo(nullptr), cref(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~CipherContext() { erase() ; }
|
||||
|
||||
void init(const CryptoAlgs::Type alg, const unsigned char *key, const int mode)
|
||||
{
|
||||
erase();
|
||||
|
||||
// check that mode is valid
|
||||
if (!(mode == ENCRYPT || mode == DECRYPT))
|
||||
throw apple_cipher_mode_error();
|
||||
|
||||
// initialize cipher context with cipher type
|
||||
const CCCryptorStatus status = CCCryptorCreate(mode,
|
||||
cipher_type(alg),
|
||||
kCCOptionPKCS7Padding,
|
||||
key,
|
||||
CryptoAlgs::key_length(alg),
|
||||
nullptr,
|
||||
&cref);
|
||||
if (status != kCCSuccess)
|
||||
throw CFException("CipherContext: CCCryptorCreate", status);
|
||||
|
||||
cinfo = CryptoAlgs::get_ptr(alg);
|
||||
}
|
||||
|
||||
void reset(const unsigned char *iv)
|
||||
{
|
||||
check_initialized();
|
||||
const CCCryptorStatus status = CCCryptorReset(cref, iv);
|
||||
if (status != kCCSuccess)
|
||||
throw CFException("CipherContext: CCCryptorReset", status);
|
||||
}
|
||||
|
||||
bool update(unsigned char *out, const size_t max_out_size,
|
||||
const unsigned char *in, const size_t in_size,
|
||||
size_t& out_acc)
|
||||
{
|
||||
check_initialized();
|
||||
size_t dataOutMoved;
|
||||
const CCCryptorStatus status = CCCryptorUpdate(cref, in, in_size, out, max_out_size, &dataOutMoved);
|
||||
if (status == kCCSuccess)
|
||||
{
|
||||
out_acc += dataOutMoved;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool final(unsigned char *out, const size_t max_out_size, size_t& out_acc)
|
||||
{
|
||||
check_initialized();
|
||||
size_t dataOutMoved;
|
||||
const CCCryptorStatus status = CCCryptorFinal(cref, out, max_out_size, &dataOutMoved);
|
||||
if (status == kCCSuccess)
|
||||
{
|
||||
out_acc += dataOutMoved;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_initialized() const { return cinfo != nullptr; }
|
||||
|
||||
size_t iv_length() const
|
||||
{
|
||||
check_initialized();
|
||||
return cinfo->iv_length();
|
||||
}
|
||||
|
||||
size_t block_size() const
|
||||
{
|
||||
check_initialized();
|
||||
return cinfo->block_size();
|
||||
}
|
||||
|
||||
// return cipher mode (such as CIPH_CBC_MODE, etc.)
|
||||
int cipher_mode() const
|
||||
{
|
||||
check_initialized();
|
||||
return CIPH_CBC_MODE;
|
||||
}
|
||||
|
||||
private:
|
||||
static CCAlgorithm cipher_type(const CryptoAlgs::Type alg)
|
||||
{
|
||||
switch (alg)
|
||||
{
|
||||
case CryptoAlgs::AES_128_CBC:
|
||||
case CryptoAlgs::AES_192_CBC:
|
||||
case CryptoAlgs::AES_256_CBC:
|
||||
return kCCAlgorithmAES128;
|
||||
case CryptoAlgs::DES_CBC:
|
||||
return kCCAlgorithmDES;
|
||||
case CryptoAlgs::DES_EDE3_CBC:
|
||||
return kCCAlgorithm3DES;
|
||||
#ifdef OPENVPN_PLATFORM_IPHONE
|
||||
case CryptoAlgs::BF_CBC:
|
||||
return kCCAlgorithmBlowfish;
|
||||
#endif
|
||||
default:
|
||||
OPENVPN_THROW(apple_cipher_error, CryptoAlgs::name(alg) << ": not usable");
|
||||
}
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (cinfo)
|
||||
{
|
||||
if (cref)
|
||||
CCCryptorRelease(cref);
|
||||
cref = nullptr;
|
||||
cinfo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void check_initialized() const
|
||||
{
|
||||
#ifdef OPENVPN_ENABLE_ASSERT
|
||||
if (!cinfo)
|
||||
throw apple_cipher_uninitialized();
|
||||
#endif
|
||||
}
|
||||
|
||||
const CryptoAlgs::Alg* cinfo;
|
||||
CCCryptorRef cref;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,255 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Wrap the Apple digest API defined in <CommonCrypto/CommonDigest.h>
|
||||
// so that it can be used as part of the crypto layer of the OpenVPN core.
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_CRYPTO_DIGEST_H
|
||||
#define OPENVPN_APPLECRYPTO_CRYPTO_DIGEST_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#include <CommonCrypto/CommonHMAC.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/applecrypto/cf/error.hpp>
|
||||
|
||||
#define OPENVPN_DIGEST_CONTEXT(TYPE) CC_##TYPE##_CTX TYPE##_ctx
|
||||
|
||||
#define OPENVPN_DIGEST_ALG_CLASS(TYPE) \
|
||||
class DigestAlgorithm##TYPE : public DigestAlgorithm \
|
||||
{ \
|
||||
public: \
|
||||
DigestAlgorithm##TYPE() {} \
|
||||
virtual int init(DigestCTX& ctx) const \
|
||||
{ \
|
||||
return CC_##TYPE##_Init(&ctx.u.TYPE##_ctx); \
|
||||
} \
|
||||
virtual int update(DigestCTX& ctx, const unsigned char *data, size_t size) const \
|
||||
{ \
|
||||
return CC_##TYPE##_Update(&ctx.u.TYPE##_ctx, data, size); \
|
||||
} \
|
||||
virtual int final(DigestCTX& ctx, unsigned char *md) const \
|
||||
{ \
|
||||
return CC_##TYPE##_Final(md, &ctx.u.TYPE##_ctx); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define OPENVPN_DIGEST_ALG_DECLARE(TYPE) const DigestAlgorithm##TYPE alg_##TYPE;
|
||||
|
||||
#define OPENVPN_DIGEST_INFO_DECLARE(TYPE) const DigestInfo info_##TYPE(CryptoAlgs::TYPE, &alg_##TYPE, kCCHmacAlg##TYPE)
|
||||
|
||||
#define OPENVPN_DIGEST_INFO_DECLARE_NO_HMAC(TYPE) const DigestInfo info_##TYPE(CryptoAlgs::TYPE, &alg_##TYPE, DigestInfo::NO_HMAC_ALG)
|
||||
|
||||
namespace openvpn {
|
||||
namespace AppleCrypto {
|
||||
typedef CC_SHA256_CTX CC_SHA224_CTX;
|
||||
typedef CC_SHA512_CTX CC_SHA384_CTX;
|
||||
|
||||
struct DigestCTX {
|
||||
union {
|
||||
OPENVPN_DIGEST_CONTEXT(MD4);
|
||||
OPENVPN_DIGEST_CONTEXT(MD5);
|
||||
OPENVPN_DIGEST_CONTEXT(SHA1);
|
||||
OPENVPN_DIGEST_CONTEXT(SHA224);
|
||||
OPENVPN_DIGEST_CONTEXT(SHA256);
|
||||
OPENVPN_DIGEST_CONTEXT(SHA384);
|
||||
OPENVPN_DIGEST_CONTEXT(SHA512);
|
||||
} u;
|
||||
};
|
||||
|
||||
struct DigestAlgorithm {
|
||||
virtual int init(DigestCTX& ctx) const = 0;
|
||||
virtual int update(DigestCTX& ctx, const unsigned char *data, size_t size) const = 0;
|
||||
virtual int final(DigestCTX& ctx, unsigned char *md) const = 0;
|
||||
};
|
||||
|
||||
// individual digest algorithm classes (each inherits from DigestAlgorithm)
|
||||
OPENVPN_DIGEST_ALG_CLASS(MD4);
|
||||
OPENVPN_DIGEST_ALG_CLASS(MD5);
|
||||
OPENVPN_DIGEST_ALG_CLASS(SHA1);
|
||||
OPENVPN_DIGEST_ALG_CLASS(SHA224);
|
||||
OPENVPN_DIGEST_ALG_CLASS(SHA256);
|
||||
OPENVPN_DIGEST_ALG_CLASS(SHA384);
|
||||
OPENVPN_DIGEST_ALG_CLASS(SHA512);
|
||||
|
||||
class DigestInfo
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
NO_HMAC_ALG = -1
|
||||
};
|
||||
|
||||
DigestInfo(CryptoAlgs::Type type,
|
||||
const DigestAlgorithm* digest_alg,
|
||||
const CCHmacAlgorithm hmac_alg)
|
||||
: type_(type),
|
||||
digest_alg_(digest_alg),
|
||||
hmac_alg_(hmac_alg) {}
|
||||
|
||||
CryptoAlgs::Type type() const { return type_; }
|
||||
const char *name() const { return CryptoAlgs::name(type_); }
|
||||
size_t size() const { return CryptoAlgs::size(type_); }
|
||||
const DigestAlgorithm* digest_alg() const { return digest_alg_; }
|
||||
CCHmacAlgorithm hmac_alg() const { return hmac_alg_; }
|
||||
|
||||
private:
|
||||
CryptoAlgs::Type type_;
|
||||
const DigestAlgorithm* digest_alg_;
|
||||
CCHmacAlgorithm hmac_alg_;
|
||||
};
|
||||
|
||||
// instantiate individual digest algorithm class instances (each inherits from DigestAlgorithm),
|
||||
// naming convention is alg_TYPE
|
||||
OPENVPN_DIGEST_ALG_DECLARE(MD4);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(MD5);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(SHA1);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(SHA224);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(SHA256);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(SHA384);
|
||||
OPENVPN_DIGEST_ALG_DECLARE(SHA512);
|
||||
|
||||
// instantiate individual digest info class instances (each is a DigestInfo),
|
||||
// naming convention is info_TYPE
|
||||
OPENVPN_DIGEST_INFO_DECLARE_NO_HMAC(MD4);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(MD5);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(SHA1);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(SHA224);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(SHA256);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(SHA384);
|
||||
OPENVPN_DIGEST_INFO_DECLARE(SHA512);
|
||||
|
||||
class HMACContext;
|
||||
|
||||
class DigestContext
|
||||
{
|
||||
DigestContext(const DigestContext&) = delete;
|
||||
DigestContext& operator=(const DigestContext&) = delete;
|
||||
|
||||
public:
|
||||
friend class HMACContext;
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(apple_digest_uninitialized);
|
||||
OPENVPN_SIMPLE_EXCEPTION(apple_digest_final_overflow);
|
||||
OPENVPN_EXCEPTION(apple_digest_error);
|
||||
|
||||
enum {
|
||||
MAX_DIGEST_SIZE = CC_SHA512_DIGEST_LENGTH // largest known is SHA512
|
||||
};
|
||||
|
||||
DigestContext()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
DigestContext(const CryptoAlgs::Type alg)
|
||||
{
|
||||
init(alg);
|
||||
}
|
||||
|
||||
void init(const CryptoAlgs::Type alg)
|
||||
{
|
||||
clear();
|
||||
info = digest_type(alg);
|
||||
meth = info->digest_alg();
|
||||
if (meth->init(ctx) != 1)
|
||||
throw apple_digest_error("init");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
check_initialized();
|
||||
if (meth->update(ctx, in, size) != 1)
|
||||
throw apple_digest_error("update");
|
||||
}
|
||||
|
||||
size_t final(unsigned char *out)
|
||||
{
|
||||
check_initialized();
|
||||
if (meth->final(ctx, out) != 1)
|
||||
throw apple_digest_error("final");
|
||||
return info->size();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
check_initialized();
|
||||
return info->size();
|
||||
}
|
||||
|
||||
bool is_initialized() const { return initialized; }
|
||||
|
||||
private:
|
||||
static const DigestInfo *digest_type(const CryptoAlgs::Type alg)
|
||||
{
|
||||
switch (alg)
|
||||
{
|
||||
case CryptoAlgs::MD4:
|
||||
return &info_MD4;
|
||||
case CryptoAlgs::MD5:
|
||||
return &info_MD5;
|
||||
case CryptoAlgs::SHA1:
|
||||
return &info_SHA1;
|
||||
case CryptoAlgs::SHA224:
|
||||
return &info_SHA224;
|
||||
case CryptoAlgs::SHA256:
|
||||
return &info_SHA256;
|
||||
case CryptoAlgs::SHA384:
|
||||
return &info_SHA384;
|
||||
case CryptoAlgs::SHA512:
|
||||
return &info_SHA512;
|
||||
default:
|
||||
OPENVPN_THROW(apple_digest_error, CryptoAlgs::name(alg) << ": not usable");
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void check_initialized() const
|
||||
{
|
||||
#ifdef OPENVPN_ENABLE_ASSERT
|
||||
if (!initialized)
|
||||
throw apple_digest_uninitialized();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
const DigestInfo *info;
|
||||
const DigestAlgorithm *meth;
|
||||
DigestCTX ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#undef OPENVPN_DIGEST_CONTEXT
|
||||
#undef OPENVPN_DIGEST_ALG_CLASS
|
||||
#undef OPENVPN_DIGEST_ALG_DECLARE
|
||||
#undef OPENVPN_DIGEST_INFO_DECLARE
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_CRYPTO_HMAC_H
|
||||
#define OPENVPN_APPLECRYPTO_CRYPTO_HMAC_H
|
||||
|
||||
// Wrap the Apple HMAC API defined in <CommonCrypto/CommonHMAC.h> so that
|
||||
// it can be used as part of the crypto layer of the OpenVPN core.
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <CommonCrypto/CommonHMAC.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/applecrypto/crypto/digest.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace AppleCrypto {
|
||||
class HMACContext
|
||||
{
|
||||
HMACContext(const HMACContext&) = delete;
|
||||
HMACContext& operator=(const HMACContext&) = delete;
|
||||
|
||||
public:
|
||||
OPENVPN_EXCEPTION(digest_cannot_be_used_with_hmac);
|
||||
OPENVPN_SIMPLE_EXCEPTION(hmac_uninitialized);
|
||||
OPENVPN_SIMPLE_EXCEPTION(hmac_keysize_error);
|
||||
|
||||
enum {
|
||||
MAX_HMAC_SIZE = DigestContext::MAX_DIGEST_SIZE,
|
||||
MAX_HMAC_KEY_SIZE = 128,
|
||||
};
|
||||
|
||||
HMACContext()
|
||||
{
|
||||
state = PRE;
|
||||
}
|
||||
|
||||
HMACContext(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
|
||||
{
|
||||
init(digest, key, key_size);
|
||||
}
|
||||
|
||||
~HMACContext()
|
||||
{
|
||||
}
|
||||
|
||||
void init(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
|
||||
{
|
||||
state = PRE;
|
||||
info = DigestContext::digest_type(digest);
|
||||
digest_size_ = CryptoAlgs::size(digest);
|
||||
hmac_alg = info->hmac_alg();
|
||||
if (hmac_alg == DigestInfo::NO_HMAC_ALG)
|
||||
throw digest_cannot_be_used_with_hmac(info->name());
|
||||
if (key_size > MAX_HMAC_KEY_SIZE)
|
||||
throw hmac_keysize_error();
|
||||
std::memcpy(key_, key, key_size_ = key_size);
|
||||
state = PARTIAL;
|
||||
}
|
||||
|
||||
void reset() // Apple HMAC API is missing reset method, so we have to reinit
|
||||
{
|
||||
cond_reset(true);
|
||||
}
|
||||
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
cond_reset(false);
|
||||
CCHmacUpdate(&ctx, in, size);
|
||||
}
|
||||
|
||||
size_t final(unsigned char *out)
|
||||
{
|
||||
cond_reset(false);
|
||||
CCHmacFinal(&ctx, out);
|
||||
return digest_size_;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
if (!is_initialized())
|
||||
throw hmac_uninitialized();
|
||||
return digest_size_;
|
||||
}
|
||||
|
||||
bool is_initialized() const
|
||||
{
|
||||
return state >= PARTIAL;
|
||||
}
|
||||
|
||||
private:
|
||||
void cond_reset(const bool force_init)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PRE:
|
||||
throw hmac_uninitialized();
|
||||
case READY:
|
||||
if (!force_init)
|
||||
return;
|
||||
case PARTIAL:
|
||||
CCHmacInit(&ctx, hmac_alg, key_, key_size_);
|
||||
state = READY;
|
||||
}
|
||||
}
|
||||
|
||||
enum State {
|
||||
PRE=0,
|
||||
PARTIAL,
|
||||
READY
|
||||
};
|
||||
int state;
|
||||
|
||||
const DigestInfo *info;
|
||||
CCHmacAlgorithm hmac_alg;
|
||||
size_t key_size_;
|
||||
size_t digest_size_;
|
||||
unsigned char key_[MAX_HMAC_KEY_SIZE];
|
||||
CCHmacContext ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,493 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Wrap the Apple SSL API as defined in <Security/SecureTransport.h>
|
||||
// so that it can be used as the SSL layer by the OpenVPN core.
|
||||
// NOTE: not used in production code.
|
||||
|
||||
// Note that the Apple SSL API is missing some functionality (as of
|
||||
// Mac OS X 10.8) that makes it difficult to use as a drop in replacement
|
||||
// for OpenSSL or PolarSSL. The biggest issue is that the API doesn't
|
||||
// allow an SSL context to be built out of PEM-based certificates and
|
||||
// keys. It requires an "Identity" in the Keychain that was imported
|
||||
// by the user as a PKCS#12 file.
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
|
||||
#define OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Security/SecImportExport.h>
|
||||
#include <Security/SecItem.h>
|
||||
#include <Security/SecureTransport.h>
|
||||
#include <Security/SecKey.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/mode.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/frame/memq_stream.hpp>
|
||||
#include <openvpn/pki/epkibase.hpp>
|
||||
#include <openvpn/applecrypto/cf/cfsec.hpp>
|
||||
#include <openvpn/applecrypto/cf/error.hpp>
|
||||
#include <openvpn/ssl/tlsver.hpp>
|
||||
#include <openvpn/ssl/sslconsts.hpp>
|
||||
#include <openvpn/ssl/sslapi.hpp>
|
||||
|
||||
// An SSL Context is essentially a configuration that can be used
|
||||
// to generate an arbitrary number of actual SSL connections objects.
|
||||
|
||||
// AppleSSLContext is an SSL Context implementation that uses the
|
||||
// Mac/iOS SSL library as a backend.
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Represents an SSL configuration that can be used
|
||||
// to instantiate actual SSL sessions.
|
||||
class AppleSSLContext : public SSLFactoryAPI
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<AppleSSLContext> Ptr;
|
||||
|
||||
enum {
|
||||
MAX_CIPHERTEXT_IN = 64
|
||||
};
|
||||
|
||||
// The data needed to construct an AppleSSLContext.
|
||||
class Config : public SSLConfigAPI
|
||||
{
|
||||
friend class AppleSSLContext;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Config> Ptr;
|
||||
|
||||
Config() {}
|
||||
|
||||
void load_identity(const std::string& subject_match)
|
||||
{
|
||||
identity = load_identity_(subject_match);
|
||||
if (!identity())
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity '" << subject_match << "' undefined");
|
||||
}
|
||||
|
||||
virtual SSLFactoryAPI::Ptr new_factory()
|
||||
{
|
||||
return SSLFactoryAPI::Ptr(new AppleSSLContext(this));
|
||||
}
|
||||
|
||||
virtual void set_mode(const Mode& mode_arg)
|
||||
{
|
||||
mode = mode_arg;
|
||||
}
|
||||
|
||||
virtual const Mode& get_mode() const
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
virtual void set_frame(const Frame::Ptr& frame_arg)
|
||||
{
|
||||
frame = frame_arg;
|
||||
}
|
||||
|
||||
virtual void load(const OptionList& opt, const unsigned int lflags)
|
||||
{
|
||||
// client/server
|
||||
if (lflags & LF_PARSE_MODE)
|
||||
mode = opt.exists("client") ? Mode(Mode::CLIENT) : Mode(Mode::SERVER);
|
||||
|
||||
// identity
|
||||
{
|
||||
const std::string& subject_match = opt.get("identity", 1, 256);
|
||||
load_identity(subject_match);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_external_pki_callback(ExternalPKIBase* external_pki_arg)
|
||||
{
|
||||
not_implemented("set_external_pki_callback");
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
return not_implemented("set_private_key_password");
|
||||
}
|
||||
|
||||
virtual void load_ca(const std::string& ca_txt, bool strict)
|
||||
{
|
||||
return not_implemented("load_ca");
|
||||
}
|
||||
|
||||
virtual void load_crl(const std::string& crl_txt)
|
||||
{
|
||||
return not_implemented("load_crl");
|
||||
}
|
||||
|
||||
virtual void load_cert(const std::string& cert_txt)
|
||||
{
|
||||
return not_implemented("load_cert");
|
||||
}
|
||||
|
||||
virtual void load_cert(const std::string& cert_txt, const std::string& extra_certs_txt)
|
||||
{
|
||||
return not_implemented("load_cert");
|
||||
}
|
||||
|
||||
virtual void load_private_key(const std::string& key_txt)
|
||||
{
|
||||
return not_implemented("load_private_key");
|
||||
}
|
||||
|
||||
virtual void load_dh(const std::string& dh_txt)
|
||||
{
|
||||
return not_implemented("load_dh");
|
||||
}
|
||||
|
||||
virtual void set_debug_level(const int debug_level)
|
||||
{
|
||||
return not_implemented("set_debug_level");
|
||||
}
|
||||
|
||||
virtual void set_flags(const unsigned int flags_arg)
|
||||
{
|
||||
return not_implemented("set_flags");
|
||||
}
|
||||
|
||||
virtual void set_ns_cert_type(const NSCert::Type ns_cert_type_arg)
|
||||
{
|
||||
return not_implemented("set_ns_cert_type");
|
||||
}
|
||||
|
||||
virtual void set_remote_cert_tls(const KUParse::TLSWebType wt)
|
||||
{
|
||||
return not_implemented("set_remote_cert_tls");
|
||||
}
|
||||
|
||||
virtual void set_tls_remote(const std::string& tls_remote_arg)
|
||||
{
|
||||
return not_implemented("set_tls_remote");
|
||||
}
|
||||
|
||||
virtual void set_tls_version_min(const TLSVersion::Type tvm)
|
||||
{
|
||||
return not_implemented("set_tls_version_min");
|
||||
}
|
||||
|
||||
virtual void set_local_cert_enabled(const bool v)
|
||||
{
|
||||
return not_implemented("set_local_cert_enabled");
|
||||
}
|
||||
|
||||
virtual void set_enable_renegotiation(const bool v)
|
||||
{
|
||||
return not_implemented("set_enable_renegotiation");
|
||||
}
|
||||
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v)
|
||||
{
|
||||
return not_implemented("set_force_aes_cbc_ciphersuites");
|
||||
}
|
||||
|
||||
virtual void set_rng(const RandomAPI::Ptr& rng_arg)
|
||||
{
|
||||
return not_implemented("set_rng");
|
||||
}
|
||||
|
||||
private:
|
||||
void not_implemented(const char *funcname)
|
||||
{
|
||||
OPENVPN_LOG("AppleSSL: " << funcname << " not implemented");
|
||||
}
|
||||
|
||||
Mode mode;
|
||||
CF::Array identity; // as returned by load_identity
|
||||
Frame::Ptr frame;
|
||||
};
|
||||
|
||||
// Represents an actual SSL session.
|
||||
// Normally instantiated by AppleSSLContext::ssl().
|
||||
class SSL : public SSLAPI
|
||||
{
|
||||
friend class AppleSSLContext;
|
||||
|
||||
public:
|
||||
typedef RCPtr<SSL> Ptr;
|
||||
|
||||
virtual void start_handshake()
|
||||
{
|
||||
SSLHandshake(ssl);
|
||||
}
|
||||
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
|
||||
{
|
||||
size_t actual = 0;
|
||||
const OSStatus status = SSLWrite(ssl, data, size, &actual);
|
||||
if (status < 0)
|
||||
{
|
||||
if (status == errSSLWouldBlock)
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
throw CFException("AppleSSLContext::SSL::write_cleartext failed", status);
|
||||
}
|
||||
else
|
||||
return actual;
|
||||
}
|
||||
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity)
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
size_t actual = 0;
|
||||
const OSStatus status = SSLRead(ssl, data, capacity, &actual);
|
||||
if (status < 0)
|
||||
{
|
||||
if (status == errSSLWouldBlock)
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
throw CFException("AppleSSLContext::SSL::read_cleartext failed", status);
|
||||
}
|
||||
else
|
||||
return actual;
|
||||
}
|
||||
else
|
||||
throw ssl_ciphertext_in_overflow();
|
||||
}
|
||||
|
||||
virtual bool read_cleartext_ready() const
|
||||
{
|
||||
// fixme: need to detect data buffered at SSL layer
|
||||
return !ct_in.empty();
|
||||
}
|
||||
|
||||
virtual void write_ciphertext(const BufferPtr& buf)
|
||||
{
|
||||
if (ct_in.size() < MAX_CIPHERTEXT_IN)
|
||||
ct_in.write_buf(buf);
|
||||
else
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual bool read_ciphertext_ready() const
|
||||
{
|
||||
return !ct_out.empty();
|
||||
}
|
||||
|
||||
virtual BufferPtr read_ciphertext()
|
||||
{
|
||||
return ct_out.read_buf();
|
||||
}
|
||||
|
||||
virtual std::string ssl_handshake_details() const // fixme -- code me
|
||||
{
|
||||
return "[AppleSSL not implemented]";
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() const
|
||||
{
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSL::SSL: auth_cert() not implemented");
|
||||
}
|
||||
|
||||
~SSL()
|
||||
{
|
||||
ssl_erase();
|
||||
}
|
||||
|
||||
private:
|
||||
SSL(const AppleSSLContext& ctx)
|
||||
{
|
||||
ssl_clear();
|
||||
try {
|
||||
OSStatus s;
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_IPHONE
|
||||
// init SSL object, select client or server mode
|
||||
if (ctx.mode().is_server())
|
||||
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
|
||||
else if (ctx.mode().is_client())
|
||||
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
|
||||
else
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
|
||||
if (ssl == nullptr)
|
||||
throw CFException("SSLCreateContext failed");
|
||||
|
||||
// use TLS v1
|
||||
s = SSLSetProtocolVersionMin(ssl, kTLSProtocol1);
|
||||
if (s)
|
||||
throw CFException("SSLSetProtocolVersionMin failed", s);
|
||||
#else
|
||||
// init SSL object, select client or server mode
|
||||
if (ctx.mode().is_server())
|
||||
s = SSLNewContext(true, &ssl);
|
||||
else if (ctx.mode().is_client())
|
||||
s = SSLNewContext(false, &ssl);
|
||||
else
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
|
||||
if (s)
|
||||
throw CFException("SSLNewContext failed", s);
|
||||
|
||||
// use TLS v1
|
||||
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol2, false);
|
||||
if (s)
|
||||
throw CFException("SSLSetProtocolVersionEnabled !S2 failed", s);
|
||||
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol3, false);
|
||||
if (s)
|
||||
throw CFException("SSLSetProtocolVersionEnabled !S3 failed", s);
|
||||
s = SSLSetProtocolVersionEnabled(ssl, kTLSProtocol1, true);
|
||||
if (s)
|
||||
throw CFException("SSLSetProtocolVersionEnabled T1 failed", s);
|
||||
#endif
|
||||
// configure cert, private key, and supporting CAs via identity wrapper
|
||||
s = SSLSetCertificate(ssl, ctx.identity()());
|
||||
if (s)
|
||||
throw CFException("SSLSetCertificate failed", s);
|
||||
|
||||
// configure ciphertext buffers
|
||||
ct_in.set_frame(ctx.frame());
|
||||
ct_out.set_frame(ctx.frame());
|
||||
|
||||
// configure the "connection" object to be self
|
||||
s = SSLSetConnection(ssl, this);
|
||||
if (s)
|
||||
throw CFException("SSLSetConnection", s);
|
||||
|
||||
// configure ciphertext read/write callbacks
|
||||
s = SSLSetIOFuncs(ssl, ct_read_func, ct_write_func);
|
||||
if (s)
|
||||
throw CFException("SSLSetIOFuncs failed", s);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ssl_erase();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
|
||||
{
|
||||
try {
|
||||
SSL *self = (SSL *)cref;
|
||||
const size_t actual = self->ct_in.read((unsigned char *)data, *length);
|
||||
const OSStatus ret = (*length == actual) ? 0 : errSSLWouldBlock;
|
||||
*length = actual;
|
||||
return ret;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return errSSLInternal;
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
|
||||
{
|
||||
try {
|
||||
SSL *self = (SSL *)cref;
|
||||
self->ct_out.write((const unsigned char *)data, *length);
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return errSSLInternal;
|
||||
}
|
||||
}
|
||||
|
||||
void ssl_clear()
|
||||
{
|
||||
ssl = nullptr;
|
||||
overflow = false;
|
||||
}
|
||||
|
||||
void ssl_erase()
|
||||
{
|
||||
if (ssl)
|
||||
{
|
||||
#ifdef OPENVPN_PLATFORM_IPHONE
|
||||
CFRelease(ssl);
|
||||
#else
|
||||
SSLDisposeContext(ssl);
|
||||
#endif
|
||||
}
|
||||
ssl_clear();
|
||||
}
|
||||
|
||||
SSLContextRef ssl; // underlying SSL connection object
|
||||
MemQStream ct_in; // write ciphertext to here
|
||||
MemQStream ct_out; // read ciphertext from here
|
||||
bool overflow;
|
||||
};
|
||||
|
||||
/////// start of main class implementation
|
||||
|
||||
// create a new SSL instance
|
||||
virtual SSLAPI::Ptr ssl()
|
||||
{
|
||||
return SSL::Ptr(new SSL(*this));
|
||||
}
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname)
|
||||
{
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: ssl session with CommonName and/or SubjectAltName verification not implemented");
|
||||
}
|
||||
|
||||
virtual const Mode& mode() const
|
||||
{
|
||||
return config_->mode;
|
||||
}
|
||||
|
||||
private:
|
||||
AppleSSLContext(Config* config)
|
||||
: config_(config)
|
||||
{
|
||||
if (!config_->identity())
|
||||
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity undefined");
|
||||
}
|
||||
|
||||
const Frame::Ptr& frame() const { return config_->frame; }
|
||||
const CF::Array& identity() const { return config_->identity; }
|
||||
|
||||
// load an identity from keychain, return as an array that can
|
||||
// be passed to SSLSetCertificate
|
||||
static CF::Array load_identity_(const std::string& subj_match)
|
||||
{
|
||||
const CF::String label = CF::string(subj_match);
|
||||
const void *keys[] = { kSecClass, kSecMatchSubjectContains, kSecMatchTrustedOnly, kSecReturnRef };
|
||||
const void *values[] = { kSecClassIdentity, label(), kCFBooleanTrue, kCFBooleanTrue };
|
||||
const CF::Dict query = CF::dict(keys, values, sizeof(keys)/sizeof(keys[0]));
|
||||
CF::Generic result;
|
||||
const OSStatus s = SecItemCopyMatching(query(), result.mod_ref());
|
||||
if (!s && result.defined())
|
||||
{
|
||||
const void *asrc[] = { result() };
|
||||
return CF::array(asrc, 1);
|
||||
}
|
||||
else
|
||||
return CF::Array(); // not found
|
||||
}
|
||||
|
||||
Config::Ptr config_;
|
||||
};
|
||||
|
||||
typedef AppleSSLContext::Ptr AppleSSLContextPtr;
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/applecrypto/util/reach.hpp>
|
||||
#include <openvpn/netconf/enumiface.hpp>
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_UTIL_IOSACTIVEIFACE_H
|
||||
#define OPENVPN_APPLECRYPTO_UTIL_IOSACTIVEIFACE_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class iOSActiveInterface : public ReachabilityInterface
|
||||
{
|
||||
public:
|
||||
virtual Status reachable() const
|
||||
{
|
||||
if (ei.iface_up("en0"))
|
||||
return ReachableViaWiFi;
|
||||
else if (ei.iface_up("pdp_ip0"))
|
||||
return ReachableViaWWAN;
|
||||
else
|
||||
return NotReachable;
|
||||
}
|
||||
|
||||
virtual bool reachableVia(const std::string& net_type) const
|
||||
{
|
||||
const Status r = reachable();
|
||||
if (net_type == "cellular")
|
||||
return r == ReachableViaWWAN;
|
||||
else if (net_type == "wifi")
|
||||
return r == ReachableViaWiFi;
|
||||
else
|
||||
return r != NotReachable;
|
||||
}
|
||||
|
||||
virtual std::string to_string() const
|
||||
{
|
||||
switch (reachable())
|
||||
{
|
||||
case ReachableViaWiFi:
|
||||
return "ReachableViaWiFi";
|
||||
case ReachableViaWWAN:
|
||||
return "ReachableViaWWAN";
|
||||
case NotReachable:
|
||||
return "NotReachable";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EnumIface ei;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Wrap the Apple Cryptographic Random API defined in <Security/SecRandom.h>
|
||||
// so that it can be used as the primary source of cryptographic entropy by
|
||||
// the OpenVPN core.
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_UTIL_RAND_H
|
||||
#define OPENVPN_APPLECRYPTO_UTIL_RAND_H
|
||||
|
||||
#include <Security/SecRandom.h>
|
||||
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class AppleRandom : public RandomAPI
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(rand_error_apple);
|
||||
|
||||
typedef RCPtr<AppleRandom> Ptr;
|
||||
|
||||
AppleRandom(const bool prng)
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string name() const
|
||||
{
|
||||
return "AppleRandom";
|
||||
}
|
||||
|
||||
// Fill buffer with random bytes
|
||||
virtual void rand_bytes(unsigned char *buf, size_t size)
|
||||
{
|
||||
if (!rndbytes(buf, size))
|
||||
throw rand_error_apple("rand_bytes");
|
||||
}
|
||||
|
||||
// Like rand_bytes, but don't throw exception.
|
||||
// Return true on successs, false on fail.
|
||||
virtual bool rand_bytes_noexcept(unsigned char *buf, size_t size)
|
||||
{
|
||||
return rndbytes(buf, size);
|
||||
}
|
||||
|
||||
private:
|
||||
bool rndbytes(unsigned char *buf, size_t size)
|
||||
{
|
||||
return SecRandomCopyBytes(kSecRandomDefault, size, buf) ? false : true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_APPLECRYPTO_UTIL_REACH_H
|
||||
#define OPENVPN_APPLECRYPTO_UTIL_REACH_H
|
||||
|
||||
// An interface to various network reachability implementations,
|
||||
// primarily for iOS.
|
||||
|
||||
namespace openvpn {
|
||||
struct ReachabilityInterface
|
||||
{
|
||||
enum Status {
|
||||
NotReachable,
|
||||
ReachableViaWiFi,
|
||||
ReachableViaWWAN
|
||||
};
|
||||
|
||||
virtual Status reachable() const = 0;
|
||||
virtual bool reachableVia(const std::string& net_type) const = 0;
|
||||
virtual std::string to_string() const = 0;
|
||||
virtual ~ReachabilityInterface() {}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,466 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
//
|
||||
// This code is derived from the Apple sample Reachability.m under
|
||||
// the following license.
|
||||
//
|
||||
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
// Inc. ("Apple") in consideration of your agreement to the following
|
||||
// terms, and your use, installation, modification or redistribution of
|
||||
// this Apple software constitutes acceptance of these terms. If you do
|
||||
// not agree with these terms, please do not use, install, modify or
|
||||
// redistribute this Apple software.
|
||||
//
|
||||
// In consideration of your agreement to abide by the following terms, and
|
||||
// subject to these terms, Apple grants you a personal, non-exclusive
|
||||
// license, under Apple's copyrights in this original Apple software (the
|
||||
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
// Software, with or without modifications, in source and/or binary forms;
|
||||
// provided that if you redistribute the Apple Software in its entirety and
|
||||
// without modifications, you must retain this notice and the following
|
||||
// text and disclaimers in all such redistributions of the Apple Software.
|
||||
// Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
// be used to endorse or promote products derived from the Apple Software
|
||||
// without specific prior written permission from Apple. Except as
|
||||
// expressly stated in this notice, no other rights or licenses, express or
|
||||
// implied, are granted by Apple herein, including but not limited to any
|
||||
// patent rights that may be infringed by your derivative works or by other
|
||||
// works in which the Apple Software may be incorporated.
|
||||
//
|
||||
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
//
|
||||
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
// Wrapper for Apple SCNetworkReachability methods.
|
||||
|
||||
#ifndef OPENVPN_APPLECRYPTO_UTIL_REACHABLE_H
|
||||
#define OPENVPN_APPLECRYPTO_UTIL_REACHABLE_H
|
||||
|
||||
#import "TargetConditionals.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <SystemConfiguration/SCNetworkReachability.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/applecrypto/cf/cf.hpp>
|
||||
#include <openvpn/applecrypto/util/reach.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace CF {
|
||||
OPENVPN_CF_WRAP(NetworkReachability, network_reachability_cast, SCNetworkReachabilityRef, SCNetworkReachabilityGetTypeID);
|
||||
}
|
||||
|
||||
class ReachabilityBase
|
||||
{
|
||||
public:
|
||||
typedef ReachabilityInterface::Status Status;
|
||||
|
||||
enum Type {
|
||||
Internet,
|
||||
WiFi,
|
||||
};
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return to_string(flags());
|
||||
}
|
||||
|
||||
std::string to_string(const SCNetworkReachabilityFlags f) const
|
||||
{
|
||||
const Status s = vstatus(f);
|
||||
const Type t = vtype();
|
||||
|
||||
std::string ret;
|
||||
ret += render_type(t);
|
||||
ret += ':';
|
||||
ret += render_status(s);
|
||||
ret += '/';
|
||||
ret += render_flags(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Status status() const
|
||||
{
|
||||
return vstatus(flags());
|
||||
}
|
||||
|
||||
SCNetworkReachabilityFlags flags() const
|
||||
{
|
||||
SCNetworkReachabilityFlags f = 0;
|
||||
if (SCNetworkReachabilityGetFlags(reach(), &f) == TRUE)
|
||||
return f;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::string render_type(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Internet:
|
||||
return "Internet";
|
||||
case WiFi:
|
||||
return "WiFi";
|
||||
default:
|
||||
return "Type???";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string render_status(const Status status)
|
||||
{
|
||||
switch (status) {
|
||||
case ReachabilityInterface::NotReachable:
|
||||
return "NotReachable";
|
||||
case ReachabilityInterface::ReachableViaWiFi:
|
||||
return "ReachableViaWiFi";
|
||||
case ReachabilityInterface::ReachableViaWWAN:
|
||||
return "ReachableViaWWAN";
|
||||
default:
|
||||
return "ReachableVia???";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string render_flags(const SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
std::string ret;
|
||||
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
|
||||
if (flags & kSCNetworkReachabilityFlagsIsWWAN)
|
||||
ret += 'W';
|
||||
else
|
||||
#endif
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsReachable)
|
||||
ret += 'R';
|
||||
else
|
||||
ret += '-';
|
||||
ret += ' ';
|
||||
if (flags & kSCNetworkReachabilityFlagsTransientConnection)
|
||||
ret += 't';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsConnectionRequired)
|
||||
ret += 'c';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)
|
||||
ret += 'C';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsInterventionRequired)
|
||||
ret += 'i';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)
|
||||
ret += 'D';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsIsLocalAddress)
|
||||
ret += 'l';
|
||||
else
|
||||
ret += '-';
|
||||
if (flags & kSCNetworkReachabilityFlagsIsDirect)
|
||||
ret += 'd';
|
||||
else
|
||||
ret += '-';
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual Type vtype() const = 0;
|
||||
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0;
|
||||
|
||||
CF::NetworkReachability reach;
|
||||
};
|
||||
|
||||
class ReachabilityViaInternet : public ReachabilityBase
|
||||
{
|
||||
public:
|
||||
ReachabilityViaInternet()
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_len = sizeof(addr);
|
||||
addr.sin_family = AF_INET;
|
||||
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&addr));
|
||||
}
|
||||
|
||||
virtual Type vtype() const
|
||||
{
|
||||
return Internet;
|
||||
}
|
||||
|
||||
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const
|
||||
{
|
||||
return status_from_flags(flags);
|
||||
}
|
||||
|
||||
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
|
||||
{
|
||||
// The target host is not reachable.
|
||||
return ReachabilityInterface::NotReachable;
|
||||
}
|
||||
|
||||
Status ret = ReachabilityInterface::NotReachable;
|
||||
|
||||
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
|
||||
{
|
||||
// If the target host is reachable and no connection is required then
|
||||
// we'll assume (for now) that you're on Wi-Fi...
|
||||
ret = ReachabilityInterface::ReachableViaWiFi;
|
||||
}
|
||||
|
||||
#if 0 // don't contaminate result by considering on-demand viability
|
||||
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
|
||||
{
|
||||
// ... and the connection is on-demand (or on-traffic) if the
|
||||
// calling application is using the CFSocketStream or higher APIs...
|
||||
|
||||
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
|
||||
{
|
||||
// ... and no [user] intervention is needed...
|
||||
ret = ReachabilityInterface::ReachableViaWiFi;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
|
||||
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
// ... but WWAN connections are OK if the calling application
|
||||
// is using the CFNetwork APIs.
|
||||
ret = ReachabilityInterface::ReachableViaWWAN;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class ReachabilityViaWiFi : public ReachabilityBase
|
||||
{
|
||||
public:
|
||||
ReachabilityViaWiFi()
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_len = sizeof(addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); // 169.254.0.0.
|
||||
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&addr));
|
||||
}
|
||||
|
||||
virtual Type vtype() const
|
||||
{
|
||||
return WiFi;
|
||||
}
|
||||
|
||||
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const
|
||||
{
|
||||
return status_from_flags(flags);
|
||||
}
|
||||
|
||||
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
|
||||
{
|
||||
Status ret = ReachabilityInterface::NotReachable;
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
|
||||
ret = ReachabilityInterface::ReachableViaWiFi;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class Reachability : public ReachabilityInterface
|
||||
{
|
||||
public:
|
||||
Reachability(const bool enable_internet, const bool enable_wifi)
|
||||
{
|
||||
if (enable_internet)
|
||||
internet.reset(new ReachabilityViaInternet);
|
||||
if (enable_wifi)
|
||||
wifi.reset(new ReachabilityViaWiFi);
|
||||
}
|
||||
|
||||
bool reachableViaWiFi() const {
|
||||
if (internet)
|
||||
{
|
||||
if (wifi)
|
||||
return internet->status() == ReachableViaWiFi && wifi->status() == ReachableViaWiFi;
|
||||
else
|
||||
return internet->status() == ReachableViaWiFi;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wifi)
|
||||
return wifi->status() == ReachableViaWiFi;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool reachableViaCellular() const
|
||||
{
|
||||
if (internet)
|
||||
return internet->status() == ReachableViaWWAN;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Status reachable() const
|
||||
{
|
||||
if (reachableViaWiFi())
|
||||
return ReachableViaWiFi;
|
||||
else if (reachableViaCellular())
|
||||
return ReachableViaWWAN;
|
||||
else
|
||||
return NotReachable;
|
||||
}
|
||||
|
||||
virtual bool reachableVia(const std::string& net_type) const
|
||||
{
|
||||
if (net_type == "cellular")
|
||||
return reachableViaCellular();
|
||||
else if (net_type == "wifi")
|
||||
return reachableViaWiFi();
|
||||
else
|
||||
return reachableViaWiFi() || reachableViaCellular();
|
||||
}
|
||||
|
||||
virtual std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
if (internet)
|
||||
ret += internet->to_string();
|
||||
if (internet && wifi)
|
||||
ret += ' ';
|
||||
if (wifi)
|
||||
ret += wifi->to_string();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<ReachabilityViaInternet> internet;
|
||||
std::unique_ptr<ReachabilityViaWiFi> wifi;
|
||||
};
|
||||
|
||||
class ReachabilityTracker
|
||||
{
|
||||
public:
|
||||
ReachabilityTracker(const bool enable_internet, const bool enable_wifi)
|
||||
: reachability(enable_internet, enable_wifi),
|
||||
scheduled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void reachability_tracker_schedule()
|
||||
{
|
||||
if (!scheduled)
|
||||
{
|
||||
if (reachability.internet)
|
||||
schedule(*reachability.internet, internet_callback_static);
|
||||
if (reachability.wifi)
|
||||
schedule(*reachability.wifi, wifi_callback_static);
|
||||
scheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void reachability_tracker_cancel()
|
||||
{
|
||||
if (scheduled)
|
||||
{
|
||||
if (reachability.internet)
|
||||
cancel(*reachability.internet);
|
||||
if (reachability.wifi)
|
||||
cancel(*reachability.wifi);
|
||||
scheduled = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void reachability_tracker_event(const ReachabilityBase& rb, SCNetworkReachabilityFlags flags) = 0;
|
||||
|
||||
virtual ~ReachabilityTracker()
|
||||
{
|
||||
reachability_tracker_cancel();
|
||||
}
|
||||
|
||||
private:
|
||||
bool schedule(ReachabilityBase& rb, SCNetworkReachabilityCallBack cb)
|
||||
{
|
||||
SCNetworkReachabilityContext context = { 0, this, nullptr, nullptr, nullptr };
|
||||
if (rb.reach.defined())
|
||||
{
|
||||
if (SCNetworkReachabilitySetCallback(rb.reach(),
|
||||
cb,
|
||||
&context) == FALSE)
|
||||
return false;
|
||||
if (SCNetworkReachabilityScheduleWithRunLoop(rb.reach(),
|
||||
CFRunLoopGetCurrent(),
|
||||
kCFRunLoopCommonModes) == FALSE)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void cancel(ReachabilityBase& rb)
|
||||
{
|
||||
if (rb.reach.defined())
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(rb.reach(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
static void internet_callback_static(SCNetworkReachabilityRef target,
|
||||
SCNetworkReachabilityFlags flags,
|
||||
void *info)
|
||||
{
|
||||
ReachabilityTracker* self = (ReachabilityTracker*)info;
|
||||
self->reachability_tracker_event(*self->reachability.internet, flags);
|
||||
}
|
||||
|
||||
static void wifi_callback_static(SCNetworkReachabilityRef target,
|
||||
SCNetworkReachabilityFlags flags,
|
||||
void *info)
|
||||
{
|
||||
ReachabilityTracker* self = (ReachabilityTracker*)info;
|
||||
self->reachability_tracker_event(*self->reachability.wifi, flags);
|
||||
}
|
||||
|
||||
Reachability reachability;
|
||||
bool scheduled;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,239 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_AUTH_AUTHCERT_H
|
||||
#define OPENVPN_AUTH_AUTHCERT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/binprefix.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/pki/x509track.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class OpenSSLContext;
|
||||
class PolarSSLContext;
|
||||
|
||||
struct AuthCert : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
// AuthCert needs to friend SSL implementation classes
|
||||
friend class OpenSSLContext;
|
||||
friend class PolarSSLContext;
|
||||
|
||||
typedef RCPtr<AuthCert> Ptr;
|
||||
|
||||
class Fail
|
||||
{
|
||||
public:
|
||||
// ordered by priority
|
||||
enum Type {
|
||||
OK=0, // OK MUST be 0
|
||||
OTHER,
|
||||
BAD_CERT_TYPE,
|
||||
EXPIRED,
|
||||
N
|
||||
};
|
||||
|
||||
void add_fail(const size_t depth, const Type new_code, const char *reason)
|
||||
{
|
||||
if (new_code > code)
|
||||
code = new_code;
|
||||
while (errors.size() <= depth)
|
||||
errors.emplace_back();
|
||||
std::string& err = errors[depth];
|
||||
if (err.empty())
|
||||
err = reason;
|
||||
else if (err.find(reason) == std::string::npos)
|
||||
{
|
||||
err += ", ";
|
||||
err += reason;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_fail() const
|
||||
{
|
||||
return code != OK;
|
||||
}
|
||||
|
||||
Type get_code() const
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string to_string(const bool use_prefix) const
|
||||
{
|
||||
std::string ret;
|
||||
if (use_prefix)
|
||||
{
|
||||
ret += render_code(code);
|
||||
ret += ": ";
|
||||
}
|
||||
bool notfirst = false;
|
||||
for (size_t i = 0; i < errors.size(); ++i)
|
||||
{
|
||||
if (errors[i].empty())
|
||||
continue;
|
||||
if (notfirst)
|
||||
ret += ", ";
|
||||
notfirst = true;
|
||||
ret += errors[i];
|
||||
ret += " [";
|
||||
ret += openvpn::to_string(i);
|
||||
ret += ']';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *render_code(const Type code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case OK:
|
||||
return "OK";
|
||||
case OTHER:
|
||||
default:
|
||||
return "CERT_FAIL";
|
||||
case BAD_CERT_TYPE:
|
||||
return "BAD_CERT_TYPE";
|
||||
case EXPIRED:
|
||||
return "EXPIRED";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Type code{OK}; // highest-valued cert fail code
|
||||
std::vector<std::string> errors; // human-readable cert errors by depth
|
||||
};
|
||||
|
||||
AuthCert()
|
||||
{
|
||||
std::memset(issuer_fp, 0, sizeof(issuer_fp));
|
||||
sn = -1;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return sn >= 0;
|
||||
}
|
||||
|
||||
bool cn_defined() const
|
||||
{
|
||||
return !cn.empty();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T issuer_fp_prefix() const
|
||||
{
|
||||
return bin_prefix<T>(issuer_fp);
|
||||
}
|
||||
|
||||
bool operator==(const AuthCert& other) const
|
||||
{
|
||||
return cn == other.cn && sn == other.sn && !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp));
|
||||
}
|
||||
|
||||
bool operator!=(const AuthCert& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "CN=" << cn
|
||||
<< " SN=" << sn
|
||||
<< " ISSUER_FP=" << issuer_fp_str(false);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string issuer_fp_str(const bool openssl_fmt) const
|
||||
{
|
||||
if (openssl_fmt)
|
||||
return render_hex_sep(issuer_fp, sizeof(issuer_fp), ':', true);
|
||||
else
|
||||
return render_hex(issuer_fp, sizeof(issuer_fp), false);
|
||||
}
|
||||
|
||||
std::string normalize_cn() const // remove trailing "_AUTOLOGIN" from AS certs
|
||||
{
|
||||
if (string::ends_with(cn, "_AUTOLOGIN"))
|
||||
return cn.substr(0, cn.length() - 10);
|
||||
else
|
||||
return cn;
|
||||
}
|
||||
|
||||
const std::string& get_cn() const
|
||||
{
|
||||
return cn;
|
||||
}
|
||||
|
||||
long get_sn() const
|
||||
{
|
||||
return sn;
|
||||
}
|
||||
|
||||
const X509Track::Set* x509_track_get() const
|
||||
{
|
||||
return x509_track.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<X509Track::Set> x509_track_take_ownership()
|
||||
{
|
||||
return std::move(x509_track);
|
||||
}
|
||||
|
||||
void add_fail(const size_t depth, const Fail::Type new_code, const char *reason)
|
||||
{
|
||||
if (!fail)
|
||||
fail.reset(new Fail());
|
||||
fail->add_fail(depth, new_code, reason);
|
||||
}
|
||||
|
||||
bool is_fail() const
|
||||
{
|
||||
return fail && fail->is_fail();
|
||||
}
|
||||
|
||||
const Fail* get_fail() const
|
||||
{
|
||||
return fail.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string cn; // common name
|
||||
long sn; // serial number
|
||||
unsigned char issuer_fp[20]; // issuer cert fingerprint
|
||||
|
||||
std::unique_ptr<Fail> fail;
|
||||
std::unique_ptr<X509Track::Set> x509_track;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_AUTH_AUTHCREDS
|
||||
#define OPENVPN_AUTH_AUTHCREDS
|
||||
|
||||
#include <utility> // for std::move
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/buffer/safestr.hpp>
|
||||
#include <openvpn/auth/validatecreds.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class AuthCreds : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<AuthCreds> Ptr;
|
||||
|
||||
AuthCreds(std::string&& username_arg,
|
||||
SafeString&& password_arg,
|
||||
const std::string& peer_info_str)
|
||||
: username(std::move(username_arg)),
|
||||
password(std::move(password_arg))
|
||||
{
|
||||
peer_info.parse_from_peer_info(peer_info_str, nullptr);
|
||||
peer_info.update_map();
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return !username.empty();
|
||||
}
|
||||
|
||||
bool is_valid_user_pass() const
|
||||
{
|
||||
return validate_auth_cred(username) && validate_auth_cred(password);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return defined() && is_valid_user_pass();
|
||||
}
|
||||
|
||||
void wipe_password()
|
||||
{
|
||||
password.wipe();
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "*** AuthCreds ***" << std::endl;
|
||||
os << "user: '" << username << "'" << std::endl;
|
||||
//os << "pass: '" << password << "'" << std::endl;
|
||||
os << "peer info:" << std::endl;
|
||||
os << peer_info.render(Option::RENDER_BRACKET|Option::RENDER_NUMBER);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string username;
|
||||
SafeString password;
|
||||
OptionList peer_info;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Encapsulate the state of a static or dynamic authentication challenge.
|
||||
|
||||
#ifndef OPENVPN_AUTH_CR_H
|
||||
#define OPENVPN_AUTH_CR_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
// Static Challenge response:
|
||||
// SCRV1:<BASE64_PASSWORD>:<BASE64_RESPONSE>
|
||||
//
|
||||
// Dynamic Challenge:
|
||||
// CRV1:<FLAGS>:<STATE_ID>:<BASE64_USERNAME>:<CHALLENGE_TEXT>
|
||||
// FLAGS is a comma-separated list of options:
|
||||
// E -- echo
|
||||
// R -- response required
|
||||
//
|
||||
// Dynamic Challenge response:
|
||||
// Username: [username decoded from username_base64]
|
||||
// Password: CRV1::<STATE_ID>::<RESPONSE_TEXT>
|
||||
|
||||
namespace openvpn {
|
||||
class ChallengeResponse : public RC<thread_unsafe_refcount> {
|
||||
public:
|
||||
typedef RCPtr<ChallengeResponse> Ptr;
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(dynamic_challenge_parse_error);
|
||||
OPENVPN_SIMPLE_EXCEPTION(static_challenge_parse_error);
|
||||
|
||||
ChallengeResponse()
|
||||
: echo(false), response_required(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ChallengeResponse(const std::string& cookie)
|
||||
: echo(false), response_required(false)
|
||||
{
|
||||
init(cookie);
|
||||
}
|
||||
|
||||
ChallengeResponse(const std::string& cookie, const std::string& user)
|
||||
: echo(false), response_required(false)
|
||||
{
|
||||
if (!is_dynamic(cookie) && cookie.find_first_of(':') == std::string::npos)
|
||||
{
|
||||
state_id = cookie;
|
||||
username = user;
|
||||
}
|
||||
else
|
||||
init(cookie);
|
||||
}
|
||||
|
||||
void init(const std::string& cookie)
|
||||
{
|
||||
typedef std::vector<std::string> StringList;
|
||||
StringList sl;
|
||||
sl.reserve(5);
|
||||
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':', 0, 4);
|
||||
if (sl.size() != 5)
|
||||
throw dynamic_challenge_parse_error();
|
||||
if (sl[0] != "CRV1")
|
||||
throw dynamic_challenge_parse_error();
|
||||
|
||||
// parse options
|
||||
{
|
||||
StringList opt;
|
||||
opt.reserve(2);
|
||||
Split::by_char_void<StringList, NullLex, Split::NullLimit>(opt, sl[1], ',');
|
||||
for (StringList::const_iterator i = opt.begin(); i != opt.end(); ++i)
|
||||
{
|
||||
if (*i == "E")
|
||||
echo = true;
|
||||
else if (*i == "R")
|
||||
response_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
// save state ID
|
||||
state_id = sl[2];
|
||||
|
||||
// save username
|
||||
try {
|
||||
username = base64->decode(sl[3]);
|
||||
}
|
||||
catch (const Base64::base64_decode_error&)
|
||||
{
|
||||
throw dynamic_challenge_parse_error();
|
||||
}
|
||||
|
||||
// save challenge
|
||||
challenge_text = sl[4];
|
||||
}
|
||||
|
||||
static bool is_dynamic(const std::string& s)
|
||||
{
|
||||
return string::starts_with(s, "CRV1:");
|
||||
}
|
||||
|
||||
static bool is_static(const std::string& s)
|
||||
{
|
||||
return string::starts_with(s, "SCRV1:");
|
||||
}
|
||||
|
||||
static void validate_dynamic(const std::string& cookie)
|
||||
{
|
||||
ChallengeResponse cr(cookie);
|
||||
}
|
||||
|
||||
std::string construct_dynamic_password(const std::string& response) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "CRV1::" << state_id << "::" << response;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static std::string construct_static_password(const std::string& password,
|
||||
const std::string& response)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "SCRV1:" << base64->encode(password) << ':' << base64->encode(response);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static void parse_static_cookie(const std::string& cookie,
|
||||
std::string& password,
|
||||
std::string& response)
|
||||
{
|
||||
typedef std::vector<std::string> StringList;
|
||||
StringList sl;
|
||||
sl.reserve(3);
|
||||
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':');
|
||||
if (sl.size() != 3)
|
||||
throw static_challenge_parse_error();
|
||||
if (sl[0] != "SCRV1")
|
||||
throw static_challenge_parse_error();
|
||||
|
||||
// get password
|
||||
try {
|
||||
password = base64->decode(sl[1]);
|
||||
}
|
||||
catch (const Base64::base64_decode_error&)
|
||||
{
|
||||
throw static_challenge_parse_error();
|
||||
}
|
||||
|
||||
// get response
|
||||
try {
|
||||
response = base64->decode(sl[2]);
|
||||
}
|
||||
catch (const Base64::base64_decode_error&)
|
||||
{
|
||||
throw static_challenge_parse_error();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string generate_dynamic_challenge(const std::string& session_token,
|
||||
const std::string& username,
|
||||
const std::string& challenge,
|
||||
const bool echo,
|
||||
const bool response_required)
|
||||
{
|
||||
std::ostringstream os;
|
||||
bool comma = false;
|
||||
os << "CRV1:";
|
||||
if (echo)
|
||||
{
|
||||
if (comma)
|
||||
os << ",";
|
||||
os << "E";
|
||||
comma = true;
|
||||
}
|
||||
if (response_required)
|
||||
{
|
||||
if (comma)
|
||||
os << ",";
|
||||
os << "R";
|
||||
comma = true;
|
||||
}
|
||||
os << ':' << session_token;
|
||||
os << ':' << base64->encode(username);
|
||||
os << ':' << challenge;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
const std::string& get_state_id() const { return state_id; }
|
||||
const std::string& get_username() const { return username; }
|
||||
bool get_echo() const { return echo; }
|
||||
bool get_response_required() const { return response_required; }
|
||||
const std::string& get_challenge_text() const { return challenge_text; }
|
||||
|
||||
private:
|
||||
bool echo;
|
||||
bool response_required;
|
||||
std::string state_id;
|
||||
std::string username;
|
||||
std::string challenge_text;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_AUTH_VALIDATE_CREDS_H
|
||||
#define OPENVPN_AUTH_VALIDATE_CREDS_H
|
||||
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// Authentication credential (username, password, or response) must
|
||||
// satisfy these constraints:
|
||||
//
|
||||
// 1. must be a valid UTF-8 string
|
||||
// 2. must not contain control or space characters
|
||||
// 3. length must be <= 256 unicode characters
|
||||
//
|
||||
// Note that we don't check that string is non-empty here,
|
||||
// callers should do this themselves if necessary.
|
||||
template <typename STRING>
|
||||
inline bool validate_auth_cred(const STRING& cred)
|
||||
{
|
||||
return Unicode::is_valid_utf8(cred, 256 | Unicode::UTF8_NO_CTRL | Unicode::UTF8_NO_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_ASIOBUF_H
|
||||
#define OPENVPN_BUFFER_ASIOBUF_H
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class AsioConstBufferSeq2
|
||||
{
|
||||
public:
|
||||
AsioConstBufferSeq2(const Buffer& b1, const Buffer& b2)
|
||||
: buf({{b1.c_data(), b1.size()},
|
||||
{b2.c_data(), b2.size()}})
|
||||
{
|
||||
}
|
||||
|
||||
// Implement the ConstBufferSequence requirements.
|
||||
typedef asio::const_buffer value_type;
|
||||
typedef const asio::const_buffer* const_iterator;
|
||||
const asio::const_buffer* begin() const { return buf; }
|
||||
const asio::const_buffer* end() const { return buf + 2; }
|
||||
|
||||
const size_t size() const
|
||||
{
|
||||
return asio::buffer_size(buf[0])
|
||||
+ asio::buffer_size(buf[1]);
|
||||
}
|
||||
|
||||
private:
|
||||
const asio::const_buffer buf[2];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// For debugging, reduce effective buffer size for I/O.
|
||||
// Enable by defining OPENVPN_BUF_CLAMP_READ and/or OPENVPN_BUF_CLAMP_WRITE
|
||||
|
||||
#ifndef OPENVPN_BUFFER_BUFCLAMP_H
|
||||
#define OPENVPN_BUFFER_BUFCLAMP_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline size_t buf_clamp_read(const size_t size)
|
||||
{
|
||||
#ifdef OPENVPN_BUF_CLAMP_READ
|
||||
return std::min(size, size_t(OPENVPN_BUF_CLAMP_READ));
|
||||
#else
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline size_t buf_clamp_write(const size_t size)
|
||||
{
|
||||
#ifdef OPENVPN_BUF_CLAMP_WRITE
|
||||
return std::min(size, size_t(OPENVPN_BUF_CLAMP_WRITE));
|
||||
#else
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_BUFCOMPLETE_H
|
||||
#define OPENVPN_BUFFER_BUFCOMPLETE_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, uint16_t, uint8_t
|
||||
#include <algorithm> // for std::min
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class BufferComplete
|
||||
{
|
||||
public:
|
||||
/* each advance/get method returns false if message is incomplete */
|
||||
|
||||
bool advance(size_t size)
|
||||
{
|
||||
while (size)
|
||||
{
|
||||
if (!fetch_buffer())
|
||||
return false;
|
||||
const size_t s = std::min(size, buf.size());
|
||||
buf.advance(s);
|
||||
size -= s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// assumes embedded big-endian uint16_t length in the stream
|
||||
bool advance_string()
|
||||
{
|
||||
std::uint8_t h, l;
|
||||
if (!get(h))
|
||||
return false;
|
||||
if (!get(l))
|
||||
return false;
|
||||
return advance(size_t(h) << 8 | size_t(l));
|
||||
}
|
||||
|
||||
bool advance_to_null()
|
||||
{
|
||||
std::uint8_t c;
|
||||
while (get(c))
|
||||
{
|
||||
if (!c)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get(std::uint8_t& c)
|
||||
{
|
||||
if (!fetch_buffer())
|
||||
return false;
|
||||
c = buf.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return buf.defined();
|
||||
}
|
||||
|
||||
protected:
|
||||
void reset_buf(const Buffer& buf_arg)
|
||||
{
|
||||
buf = buf_arg;
|
||||
}
|
||||
|
||||
void reset_buf()
|
||||
{
|
||||
buf.reset_content();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void next_buffer() = 0;
|
||||
|
||||
bool fetch_buffer()
|
||||
{
|
||||
if (buf.defined())
|
||||
return true;
|
||||
next_buffer();
|
||||
return buf.defined();
|
||||
}
|
||||
|
||||
Buffer buf;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_BUFCOMPOSED_H
|
||||
#define OPENVPN_BUFFER_BUFCOMPOSED_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/buffer/bufcomplete.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class BufferComposed
|
||||
{
|
||||
public:
|
||||
class Complete : public BufferComplete
|
||||
{
|
||||
public:
|
||||
BufferPtr get()
|
||||
{
|
||||
#if 0 // don't include for production
|
||||
if (iter_defined())
|
||||
throw Exception("BufferComposed::Complete: residual data");
|
||||
#endif
|
||||
BufferPtr ret = bc.bv.join();
|
||||
bc.bv.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BufferComposed;
|
||||
|
||||
Complete(BufferComposed& bc_arg)
|
||||
: bc(bc_arg),
|
||||
iter(bc.bv.cbegin())
|
||||
{
|
||||
next_buffer();
|
||||
}
|
||||
|
||||
bool iter_defined()
|
||||
{
|
||||
return iter != bc.bv.end();
|
||||
}
|
||||
|
||||
virtual void next_buffer() override
|
||||
{
|
||||
if (iter_defined())
|
||||
reset_buf(**iter++);
|
||||
else
|
||||
reset_buf();
|
||||
}
|
||||
|
||||
BufferComposed& bc;
|
||||
BufferVector::const_iterator iter;
|
||||
};
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return bv.join_size();
|
||||
}
|
||||
|
||||
void put(BufferPtr bp)
|
||||
{
|
||||
bv.push_back(std::move(bp));
|
||||
}
|
||||
|
||||
Complete complete()
|
||||
{
|
||||
return Complete(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
BufferVector bv;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,834 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// These templates define the fundamental data buffer classes used by the
|
||||
// OpenVPN core. Normally OpenVPN uses buffers of unsigned chars, but the
|
||||
// templatization of the classes would allow buffers of other types to
|
||||
// be defined.
|
||||
//
|
||||
// Fundamentally a buffer is an object with 4 fields:
|
||||
//
|
||||
// 1. a pointer to underlying data array
|
||||
// 2. the capacity of the underlying data array
|
||||
// 3. an offset into the data array
|
||||
// 4. the size of the referenced data within the array
|
||||
//
|
||||
// The BufferType template is the lowest-level buffer class template. It refers
|
||||
// to a buffer but without any notion of ownership of the underlying data.
|
||||
//
|
||||
// The BufferAllocatedType template is a higher-level template that inherits
|
||||
// from BufferType but which asserts ownership over the resources of the buffer --
|
||||
// for example, it will free the underlying buffer in its destructor.
|
||||
//
|
||||
// Since most of the time, we want our buffers to be made out of unsigned chars,
|
||||
// some typedefs at the end of the file define common instantations for the
|
||||
// BufferType and BufferAllocatedType templates.
|
||||
//
|
||||
// Buffer : a simple buffer of unsigned char without ownership semantics
|
||||
// ConstBuffer : like buffer but where the data pointed to by the buffer is const
|
||||
// BufferAllocated : an allocated Buffer with ownership semantics
|
||||
// BufferPtr : a smart, reference-counted pointer to a BufferAllocated
|
||||
|
||||
#ifndef OPENVPN_BUFFER_BUFFER_H
|
||||
#define OPENVPN_BUFFER_BUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <type_traits> // for std::is_nothrow_move_constructible
|
||||
|
||||
#ifndef NO_ASIO
|
||||
#include <asio.hpp>
|
||||
#endif
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/abort.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/buffer/bufclamp.hpp>
|
||||
|
||||
#ifdef OPENVPN_BUFFER_ABORT
|
||||
#define OPENVPN_BUFFER_THROW(exc) { std::abort(); }
|
||||
#else
|
||||
#define OPENVPN_BUFFER_THROW(exc) { throw BufferException(BufferException::exc); }
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// special-purpose exception class for Buffer classes
|
||||
class BufferException : public std::exception
|
||||
{
|
||||
public:
|
||||
enum Status {
|
||||
buffer_full,
|
||||
buffer_headroom,
|
||||
buffer_underflow,
|
||||
buffer_overflow,
|
||||
buffer_index,
|
||||
buffer_const_index,
|
||||
buffer_push_front_headroom,
|
||||
buffer_no_reset_impl,
|
||||
buffer_pop_back,
|
||||
buffer_set_size,
|
||||
buffer_range,
|
||||
};
|
||||
|
||||
BufferException(Status status)
|
||||
: status_(status) {}
|
||||
|
||||
Status status() const { return status_; }
|
||||
|
||||
const char *status_string() const
|
||||
{
|
||||
switch (status_)
|
||||
{
|
||||
case buffer_full:
|
||||
return "buffer_full";
|
||||
case buffer_headroom:
|
||||
return "buffer_headroom";
|
||||
case buffer_underflow:
|
||||
return "buffer_underflow";
|
||||
case buffer_overflow:
|
||||
return "buffer_overflow";
|
||||
case buffer_index:
|
||||
return "buffer_index";
|
||||
case buffer_const_index:
|
||||
return "buffer_const_index";
|
||||
case buffer_push_front_headroom:
|
||||
return "buffer_push_front_headroom";
|
||||
case buffer_no_reset_impl:
|
||||
return "buffer_no_reset_impl";
|
||||
case buffer_pop_back:
|
||||
return "buffer_pop_back";
|
||||
case buffer_set_size:
|
||||
return "buffer_set_size";
|
||||
case buffer_range:
|
||||
return "buffer_range";
|
||||
default:
|
||||
return "buffer_???";
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return status_string();
|
||||
}
|
||||
virtual ~BufferException() throw() {}
|
||||
|
||||
private:
|
||||
Status status_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class BufferType {
|
||||
public:
|
||||
typedef T* type;
|
||||
typedef const T* const_type;
|
||||
|
||||
BufferType()
|
||||
{
|
||||
static_assert(std::is_nothrow_move_constructible<BufferType>::value, "class BufferType not noexcept move constructable");
|
||||
data_ = nullptr;
|
||||
offset_ = size_ = capacity_ = 0;
|
||||
}
|
||||
|
||||
BufferType(T* data, const size_t size, const bool filled)
|
||||
{
|
||||
data_ = data;
|
||||
offset_ = 0;
|
||||
capacity_ = size;
|
||||
size_ = filled ? size : 0;
|
||||
}
|
||||
|
||||
void reserve(const size_t n)
|
||||
{
|
||||
if (n > capacity_)
|
||||
resize(n);
|
||||
}
|
||||
|
||||
void init_headroom(const size_t headroom)
|
||||
{
|
||||
if (headroom > capacity_)
|
||||
OPENVPN_BUFFER_THROW(buffer_headroom);
|
||||
offset_ = headroom;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void reset_size()
|
||||
{
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void reset_content()
|
||||
{
|
||||
offset_ = size_ = 0;
|
||||
}
|
||||
|
||||
// std::string compatible methods
|
||||
const T* c_str() const { return c_data(); }
|
||||
size_t length() const { return size(); }
|
||||
|
||||
// return a const pointer to start of array
|
||||
const T* c_data() const { return data_ + offset_; }
|
||||
|
||||
// return a mutable pointer to start of array
|
||||
T* data() { return data_ + offset_; }
|
||||
|
||||
// return a const pointer to end of array
|
||||
const T* c_data_end() const { return data_ + offset_ + size_; }
|
||||
|
||||
// return a mutable pointer to end of array
|
||||
T* data_end() { return data_ + offset_ + size_; }
|
||||
|
||||
// return a const pointer to start of raw data
|
||||
const T* c_data_raw() const { return data_; }
|
||||
|
||||
// return a mutable pointer to start of raw data
|
||||
T* data_raw() { return data_; }
|
||||
|
||||
// return size of array in T objects
|
||||
size_t size() const { return size_; }
|
||||
|
||||
// return raw size of allocated buffer in T objects
|
||||
size_t capacity() const { return capacity_; }
|
||||
|
||||
// return current offset (headroom) into buffer
|
||||
size_t offset() const { return offset_; }
|
||||
|
||||
// return true if array is not empty
|
||||
bool defined() const { return size_ > 0; }
|
||||
|
||||
// return true if data memory is defined
|
||||
bool allocated() const { return data_ != nullptr; }
|
||||
|
||||
// return true if array is empty
|
||||
bool empty() const { return !size_; }
|
||||
|
||||
// return the number of additional T objects that can be added before capacity is reached (without considering resize)
|
||||
size_t remaining(const size_t tailroom = 0) const {
|
||||
const size_t r = capacity_ - (offset_ + size_ + tailroom);
|
||||
return r <= capacity_ ? r : 0;
|
||||
}
|
||||
|
||||
// return the maximum allowable size value in T objects given the current offset (without considering resize)
|
||||
size_t max_size() const {
|
||||
const size_t r = capacity_ - offset_;
|
||||
return r <= capacity_ ? r : 0;
|
||||
}
|
||||
|
||||
// like max_size, but take tailroom into account
|
||||
size_t max_size_tailroom(const size_t tailroom) const {
|
||||
const size_t r = capacity_ - (offset_ + tailroom);
|
||||
return r <= capacity_ ? r : 0;
|
||||
}
|
||||
|
||||
// After an external method, operating on the array as
|
||||
// a mutable unsigned char buffer, has written data to the
|
||||
// array, use this method to set the array length in terms
|
||||
// of T objects.
|
||||
void set_size(const size_t size)
|
||||
{
|
||||
if (size > max_size())
|
||||
OPENVPN_BUFFER_THROW(buffer_set_size);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
// Increment size (usually used in a similar context
|
||||
// to set_size such as after mutable_buffers_1_append).
|
||||
void inc_size(const size_t delta)
|
||||
{
|
||||
set_size(size_ + delta);
|
||||
}
|
||||
|
||||
// append a T object to array, with possible resize
|
||||
void push_back(const T& value)
|
||||
{
|
||||
if (!remaining())
|
||||
resize(offset_ + size_ + 1);
|
||||
*(data()+size_++) = value;
|
||||
}
|
||||
|
||||
// append a T object to array, with possible resize
|
||||
void push_front(const T& value)
|
||||
{
|
||||
if (!offset_)
|
||||
OPENVPN_BUFFER_THROW(buffer_push_front_headroom);
|
||||
--offset_;
|
||||
++size_;
|
||||
*data() = value;
|
||||
}
|
||||
|
||||
T pop_back()
|
||||
{
|
||||
if (!size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_pop_back);
|
||||
return *(data()+(--size_));
|
||||
}
|
||||
|
||||
T pop_front()
|
||||
{
|
||||
T ret = (*this)[0];
|
||||
++offset_;
|
||||
--size_;
|
||||
return ret;
|
||||
}
|
||||
|
||||
T front()
|
||||
{
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
T back()
|
||||
{
|
||||
return (*this)[size_-1];
|
||||
}
|
||||
|
||||
// Place a T object after the last object in the
|
||||
// array, with possible resize to contain it,
|
||||
// however don't actually change the size of the
|
||||
// array to reflect the added object. Useful
|
||||
// for maintaining null-terminated strings.
|
||||
void set_trailer(const T& value)
|
||||
{
|
||||
if (!remaining())
|
||||
resize(offset_ + size_ + 1);
|
||||
*(data()+size_) = value;
|
||||
}
|
||||
|
||||
void null_terminate()
|
||||
{
|
||||
if (empty() || back())
|
||||
push_back(0);
|
||||
}
|
||||
|
||||
void advance(const size_t delta)
|
||||
{
|
||||
if (delta > size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_overflow);
|
||||
offset_ += delta;
|
||||
size_ -= delta;
|
||||
}
|
||||
|
||||
bool contains_null() const
|
||||
{
|
||||
const T* end = c_data_end();
|
||||
for (const T* p = c_data(); p < end; ++p)
|
||||
{
|
||||
if (!*p)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// mutable index into array
|
||||
T& operator[](const size_t index)
|
||||
{
|
||||
if (index >= size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_index);
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
// const index into array
|
||||
const T& operator[](const size_t index) const
|
||||
{
|
||||
if (index >= size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_const_index);
|
||||
return c_data()[index];
|
||||
}
|
||||
|
||||
// mutable index into array
|
||||
T* index(const size_t index)
|
||||
{
|
||||
if (index >= size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_index);
|
||||
return &data()[index];
|
||||
}
|
||||
|
||||
// const index into array
|
||||
const T* c_index(const size_t index) const
|
||||
{
|
||||
if (index >= size_)
|
||||
OPENVPN_BUFFER_THROW(buffer_const_index);
|
||||
return &c_data()[index];
|
||||
}
|
||||
|
||||
bool operator==(const BufferType& other) const
|
||||
{
|
||||
if (size_ != other.size_)
|
||||
return false;
|
||||
return std::memcmp(c_data(), other.c_data(), size_) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const BufferType& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
#ifndef NO_ASIO
|
||||
// return a asio::mutable_buffers_1 object used by
|
||||
// asio read methods, starting from data()
|
||||
asio::mutable_buffers_1 mutable_buffers_1(const size_t tailroom = 0)
|
||||
{
|
||||
return asio::mutable_buffers_1(data(), max_size_tailroom(tailroom));
|
||||
}
|
||||
|
||||
// return a asio::mutable_buffers_1 object used by
|
||||
// asio read methods, starting from data_end()
|
||||
asio::mutable_buffers_1 mutable_buffers_1_append(const size_t tailroom = 0)
|
||||
{
|
||||
return asio::mutable_buffers_1(data_end(), remaining(tailroom));
|
||||
}
|
||||
|
||||
// return a asio::const_buffers_1 object used by
|
||||
// asio write methods.
|
||||
asio::const_buffers_1 const_buffers_1() const
|
||||
{
|
||||
return asio::const_buffers_1(c_data(), size());
|
||||
}
|
||||
|
||||
// clamped versions of mutable_buffers_1(), mutable_buffers_1_append(),
|
||||
// and const_buffers_1()
|
||||
|
||||
asio::mutable_buffers_1 mutable_buffers_1_clamp(const size_t tailroom = 0)
|
||||
{
|
||||
return asio::mutable_buffers_1(data(), buf_clamp_read(max_size_tailroom(tailroom)));
|
||||
}
|
||||
|
||||
asio::mutable_buffers_1 mutable_buffers_1_append_clamp(const size_t tailroom = 0)
|
||||
{
|
||||
return asio::mutable_buffers_1(data_end(), buf_clamp_read(remaining(tailroom)));
|
||||
}
|
||||
|
||||
asio::const_buffers_1 const_buffers_1_clamp() const
|
||||
{
|
||||
return asio::const_buffers_1(c_data(), buf_clamp_write(size()));
|
||||
}
|
||||
|
||||
asio::const_buffers_1 const_buffers_1_limit(const size_t limit) const
|
||||
{
|
||||
return asio::const_buffers_1(c_data(), std::min(buf_clamp_write(size()), limit));
|
||||
}
|
||||
#endif
|
||||
|
||||
void realign(size_t headroom)
|
||||
{
|
||||
if (headroom != offset_)
|
||||
{
|
||||
if (headroom + size_ > capacity_)
|
||||
OPENVPN_BUFFER_THROW(buffer_headroom);
|
||||
std::memmove(data_ + headroom, data_ + offset_, size_);
|
||||
offset_ = headroom;
|
||||
}
|
||||
}
|
||||
|
||||
void write(const T* data, const size_t size)
|
||||
{
|
||||
std::memcpy(write_alloc(size), data, size * sizeof(T));
|
||||
}
|
||||
|
||||
void prepend(const T* data, const size_t size)
|
||||
{
|
||||
std::memcpy(prepend_alloc(size), data, size * sizeof(T));
|
||||
}
|
||||
|
||||
void read(T* data, const size_t size)
|
||||
{
|
||||
std::memcpy(data, read_alloc(size), size * sizeof(T));
|
||||
}
|
||||
|
||||
T* write_alloc(const size_t size)
|
||||
{
|
||||
if (size > remaining())
|
||||
resize(offset_ + size_ + size);
|
||||
T* ret = data() + size_;
|
||||
size_ += size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
T* prepend_alloc(const size_t size)
|
||||
{
|
||||
if (size <= offset_)
|
||||
{
|
||||
offset_ -= size;
|
||||
size_ += size;
|
||||
return data();
|
||||
}
|
||||
else
|
||||
OPENVPN_BUFFER_THROW(buffer_headroom);
|
||||
}
|
||||
|
||||
T* read_alloc(const size_t size)
|
||||
{
|
||||
if (size <= size_)
|
||||
{
|
||||
T* ret = data();
|
||||
offset_ += size;
|
||||
size_ -= size;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
OPENVPN_BUFFER_THROW(buffer_underflow);
|
||||
}
|
||||
|
||||
void reset(const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
if (min_capacity > capacity_)
|
||||
reset_impl(min_capacity, flags);
|
||||
}
|
||||
|
||||
void reset(const size_t headroom, const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
reset(min_capacity, flags);
|
||||
init_headroom(headroom);
|
||||
}
|
||||
|
||||
void append(const BufferType& other)
|
||||
{
|
||||
write(other.c_data(), other.size());
|
||||
}
|
||||
|
||||
BufferType range(size_t offset, size_t len) const
|
||||
{
|
||||
if (offset + len > size())
|
||||
{
|
||||
if (offset < size())
|
||||
len = size() - offset;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
return BufferType(datac(), offset, len, len);
|
||||
}
|
||||
|
||||
protected:
|
||||
BufferType(T* data, const size_t offset, const size_t size, const size_t capacity)
|
||||
: data_(data), offset_(offset), size_(size), capacity_(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
// return a mutable pointer to start of array but
|
||||
// remain const with respect to *this.
|
||||
T* datac() const { return data_ + offset_; }
|
||||
|
||||
// Called when reset method needs to expand the buffer size
|
||||
virtual void reset_impl(const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
OPENVPN_BUFFER_THROW(buffer_no_reset_impl);
|
||||
}
|
||||
|
||||
// Derived classes can implement buffer growing semantics
|
||||
// by overloading this method. In the default implementation,
|
||||
// buffers are non-growable, so we throw an exception.
|
||||
virtual void resize(const size_t new_capacity)
|
||||
{
|
||||
if (new_capacity > capacity_)
|
||||
{
|
||||
OPENVPN_BUFFER_THROW(buffer_full);
|
||||
}
|
||||
}
|
||||
|
||||
T* data_; // pointer to data
|
||||
size_t offset_; // offset from data_ of beginning of T array (to allow for headroom)
|
||||
size_t size_; // number of T objects in array starting at data_ + offset_
|
||||
size_t capacity_; // maximum number of array objects of type T for which memory is allocated, starting at data_
|
||||
};
|
||||
|
||||
template <typename T, typename R = thread_unsafe_refcount>
|
||||
class BufferAllocatedType : public BufferType<T>, public RC<R>
|
||||
{
|
||||
using BufferType<T>::data_;
|
||||
using BufferType<T>::offset_;
|
||||
using BufferType<T>::size_;
|
||||
using BufferType<T>::capacity_;
|
||||
|
||||
public:
|
||||
enum {
|
||||
CONSTRUCT_ZERO = (1<<0), // if enabled, constructors/init will zero allocated space
|
||||
DESTRUCT_ZERO = (1<<1), // if enabled, destructor will zero data before deletion
|
||||
GROW = (1<<2), // if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
|
||||
ARRAY = (1<<3), // if enabled, use as array
|
||||
};
|
||||
|
||||
BufferAllocatedType()
|
||||
{
|
||||
static_assert(std::is_nothrow_move_constructible<BufferAllocatedType>::value, "class BufferAllocatedType not noexcept move constructable");
|
||||
flags_ = 0;
|
||||
}
|
||||
|
||||
BufferAllocatedType(const size_t capacity, const unsigned int flags)
|
||||
{
|
||||
flags_ = flags;
|
||||
capacity_ = capacity;
|
||||
if (capacity)
|
||||
{
|
||||
data_ = new T[capacity];
|
||||
if (flags & CONSTRUCT_ZERO)
|
||||
std::memset(data_, 0, capacity * sizeof(T));
|
||||
if (flags & ARRAY)
|
||||
size_ = capacity;
|
||||
}
|
||||
}
|
||||
|
||||
BufferAllocatedType(const T* data, const size_t size, const unsigned int flags)
|
||||
{
|
||||
flags_ = flags;
|
||||
size_ = capacity_ = size;
|
||||
if (size)
|
||||
{
|
||||
data_ = new T[size];
|
||||
std::memcpy(data_, data, size * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
BufferAllocatedType(const BufferAllocatedType& other)
|
||||
{
|
||||
offset_ = other.offset_;
|
||||
size_ = other.size_;
|
||||
capacity_ = other.capacity_;
|
||||
flags_ = other.flags_;
|
||||
if (capacity_)
|
||||
{
|
||||
data_ = new T[capacity_];
|
||||
if (size_)
|
||||
std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OT>
|
||||
BufferAllocatedType(const BufferType<OT>& other, const unsigned int flags)
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(OT), "size inconsistency");
|
||||
offset_ = other.offset();
|
||||
size_ = other.size();
|
||||
capacity_ = other.capacity();
|
||||
flags_ = flags;
|
||||
if (capacity_)
|
||||
{
|
||||
data_ = new T[capacity_];
|
||||
if (size_)
|
||||
std::memcpy(data_ + offset_, other.c_data(), size_ * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void operator=(const BufferAllocatedType& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
offset_ = size_ = 0;
|
||||
if (capacity_ != other.capacity_)
|
||||
{
|
||||
erase_();
|
||||
if (other.capacity_)
|
||||
data_ = new T[other.capacity_];
|
||||
capacity_ = other.capacity_;
|
||||
}
|
||||
offset_ = other.offset_;
|
||||
size_ = other.size_;
|
||||
flags_ = other.flags_;
|
||||
if (size_)
|
||||
std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void init(const size_t capacity, const unsigned int flags)
|
||||
{
|
||||
offset_ = size_ = 0;
|
||||
flags_ = flags;
|
||||
if (capacity_ != capacity)
|
||||
{
|
||||
erase_();
|
||||
if (capacity)
|
||||
{
|
||||
data_ = new T[capacity];
|
||||
}
|
||||
capacity_ = capacity;
|
||||
}
|
||||
if ((flags & CONSTRUCT_ZERO) && capacity)
|
||||
std::memset(data_, 0, capacity * sizeof(T));
|
||||
if (flags & ARRAY)
|
||||
size_ = capacity;
|
||||
}
|
||||
|
||||
void init(const T* data, const size_t size, const unsigned int flags)
|
||||
{
|
||||
offset_ = size_ = 0;
|
||||
flags_ = flags;
|
||||
if (size != capacity_)
|
||||
{
|
||||
erase_();
|
||||
if (size)
|
||||
data_ = new T[size];
|
||||
capacity_ = size;
|
||||
}
|
||||
size_ = size;
|
||||
std::memcpy(data_, data, size * sizeof(T));
|
||||
}
|
||||
|
||||
void realloc(const size_t newcap)
|
||||
{
|
||||
if (newcap > capacity_)
|
||||
realloc_(newcap);
|
||||
}
|
||||
|
||||
void reset(const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
if (min_capacity > capacity_)
|
||||
init (min_capacity, flags);
|
||||
}
|
||||
|
||||
void reset(const size_t headroom, const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
reset(min_capacity, flags);
|
||||
BufferType<T>::init_headroom(headroom);
|
||||
}
|
||||
|
||||
void move(BufferAllocatedType& other)
|
||||
{
|
||||
if (data_)
|
||||
delete_(data_, capacity_, flags_);
|
||||
move_(other);
|
||||
}
|
||||
|
||||
RCPtr<BufferAllocatedType<T>> move_to_ptr()
|
||||
{
|
||||
RCPtr<BufferAllocatedType<T>> bp = new BufferAllocatedType<T>();
|
||||
bp->move(*this);
|
||||
return bp;
|
||||
}
|
||||
|
||||
void swap(BufferAllocatedType& other)
|
||||
{
|
||||
std::swap(data_, other.data_);
|
||||
std::swap(offset_, other.offset_);
|
||||
std::swap(size_, other.size_);
|
||||
std::swap(capacity_, other.capacity_);
|
||||
std::swap(flags_, other.flags_);
|
||||
}
|
||||
|
||||
BufferAllocatedType(BufferAllocatedType&& other) noexcept
|
||||
{
|
||||
move_(other);
|
||||
}
|
||||
|
||||
BufferAllocatedType& operator=(BufferAllocatedType&& other) noexcept
|
||||
{
|
||||
move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
erase_();
|
||||
flags_ = 0;
|
||||
size_ = offset_ = 0;
|
||||
}
|
||||
|
||||
void or_flags(const unsigned int flags)
|
||||
{
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
void and_flags(const unsigned int flags)
|
||||
{
|
||||
flags_ &= flags;
|
||||
}
|
||||
|
||||
~BufferAllocatedType()
|
||||
{
|
||||
if (data_)
|
||||
delete_(data_, capacity_, flags_);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Called when reset method needs to expand the buffer size
|
||||
virtual void reset_impl(const size_t min_capacity, const unsigned int flags)
|
||||
{
|
||||
init(min_capacity, flags);
|
||||
}
|
||||
|
||||
// Set current capacity to at least new_capacity.
|
||||
virtual void resize(const size_t new_capacity)
|
||||
{
|
||||
const size_t newcap = std::max(new_capacity, capacity_ * 2);
|
||||
if (newcap > capacity_)
|
||||
{
|
||||
if (flags_ & GROW)
|
||||
realloc_(newcap);
|
||||
else
|
||||
OPENVPN_BUFFER_THROW(buffer_full);
|
||||
}
|
||||
}
|
||||
|
||||
void realloc_(const size_t newcap)
|
||||
{
|
||||
T* data = new T[newcap];
|
||||
if (size_)
|
||||
std::memcpy(data + offset_, data_ + offset_, size_ * sizeof(T));
|
||||
delete_(data_, capacity_, flags_);
|
||||
data_ = data;
|
||||
//std::cout << "*** RESIZE " << capacity_ << " -> " << newcap << std::endl; // fixme
|
||||
capacity_ = newcap;
|
||||
}
|
||||
|
||||
void move_(BufferAllocatedType& other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
offset_ = other.offset_;
|
||||
size_ = other.size_;
|
||||
capacity_ = other.capacity_;
|
||||
flags_ = other.flags_;
|
||||
|
||||
other.data_ = nullptr;
|
||||
other.offset_ = other.size_ = other.capacity_ = 0;
|
||||
}
|
||||
|
||||
void erase_()
|
||||
{
|
||||
if (data_)
|
||||
{
|
||||
delete_(data_, capacity_, flags_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
static void delete_(T* data, const size_t size, const unsigned int flags)
|
||||
{
|
||||
if (size && (flags & DESTRUCT_ZERO))
|
||||
std::memset(data, 0, size * sizeof(T));
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
unsigned int flags_;
|
||||
};
|
||||
|
||||
typedef BufferType<unsigned char> Buffer;
|
||||
typedef BufferType<const unsigned char> ConstBuffer;
|
||||
typedef BufferAllocatedType<unsigned char> BufferAllocated;
|
||||
typedef RCPtr<BufferAllocated> BufferPtr;
|
||||
|
||||
template <typename T>
|
||||
inline BufferType<const T>& const_buffer_ref(BufferType<T>& src)
|
||||
{
|
||||
return (BufferType<const T>&)src;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_BUFFER_BUFFER_H
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_BUFHEX_H
|
||||
#define OPENVPN_BUFFER_BUFHEX_H
|
||||
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace BufHex {
|
||||
|
||||
OPENVPN_EXCEPTION(buf_hex);
|
||||
|
||||
template <typename T>
|
||||
inline std::string render(const T obj)
|
||||
{
|
||||
const ConstBuffer buf((const unsigned char *)&obj, sizeof(obj), true);
|
||||
return render_hex_generic(buf);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parse(const std::string& hex, const std::string& title)
|
||||
{
|
||||
T obj;
|
||||
Buffer buf((unsigned char *)&obj, sizeof(obj), false);
|
||||
try {
|
||||
parse_hex(buf, hex);
|
||||
}
|
||||
catch (const BufferException& e)
|
||||
{
|
||||
OPENVPN_THROW(buf_hex, title << ": buffer issue: " << e.what());
|
||||
}
|
||||
if (buf.size() != sizeof(obj))
|
||||
OPENVPN_THROW(buf_hex, title << ": unexpected size");
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#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-2016 OpenVPN Technologies, 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_BUFFER_BUFLIMIT_H
|
||||
#define OPENVPN_BUFFER_BUFLIMIT_H
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
class BufferLimit
|
||||
{
|
||||
public:
|
||||
BufferLimit()
|
||||
{
|
||||
set_max(0, 0);
|
||||
reset();
|
||||
}
|
||||
|
||||
BufferLimit(const T max_lines_arg,
|
||||
const T max_bytes_arg)
|
||||
{
|
||||
set_max(max_lines_arg, max_bytes_arg);
|
||||
reset();
|
||||
}
|
||||
|
||||
void set_max(const T max_lines_arg,
|
||||
const T max_bytes_arg)
|
||||
{
|
||||
max_lines = max_lines_arg;
|
||||
max_bytes = max_bytes_arg;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
n_bytes = n_lines = 0;
|
||||
}
|
||||
|
||||
void add(const Buffer& buf)
|
||||
{
|
||||
T size = (T)buf.size();
|
||||
n_bytes += size;
|
||||
if (max_bytes && n_bytes > max_bytes)
|
||||
bytes_exceeded();
|
||||
if (max_lines)
|
||||
{
|
||||
const unsigned char *p = buf.c_data();
|
||||
while (size--)
|
||||
{
|
||||
const unsigned char c = *p++;
|
||||
if (c == '\n')
|
||||
{
|
||||
++n_lines;
|
||||
if (n_lines > max_lines)
|
||||
lines_exceeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void bytes_exceeded() = 0;
|
||||
virtual void lines_exceeded() = 0;
|
||||
|
||||
protected:
|
||||
T max_lines;
|
||||
T max_bytes;
|
||||
T n_bytes;
|
||||
T n_lines;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_BUFLIST_H
|
||||
#define OPENVPN_BUFFER_BUFLIST_H
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <template <typename...> class COLLECTION>
|
||||
struct BufferCollection : public COLLECTION<BufferPtr>
|
||||
{
|
||||
using COLLECTION<BufferPtr>::size;
|
||||
using COLLECTION<BufferPtr>::front;
|
||||
using COLLECTION<BufferPtr>::empty;
|
||||
using COLLECTION<BufferPtr>::back;
|
||||
using COLLECTION<BufferPtr>::emplace_back;
|
||||
|
||||
BufferPtr join(const size_t headroom,
|
||||
const size_t tailroom,
|
||||
const bool size_1_optim) const
|
||||
{
|
||||
// special optimization if list contains
|
||||
// a single element that satisfies our
|
||||
// headroom/tailroom constraints.
|
||||
if (size_1_optim
|
||||
&& size() == 1
|
||||
&& front()->offset() >= headroom
|
||||
&& front()->remaining() >= tailroom)
|
||||
return front();
|
||||
|
||||
// first pass -- measure total size
|
||||
const size_t size = join_size();
|
||||
|
||||
// allocate buffer
|
||||
BufferPtr big = new BufferAllocated(size + headroom + tailroom, 0);
|
||||
big->init_headroom(headroom);
|
||||
|
||||
// second pass -- copy data
|
||||
for (auto &b : *this)
|
||||
big->write(b->c_data(), b->size());
|
||||
|
||||
return big;
|
||||
}
|
||||
|
||||
BufferPtr join() const
|
||||
{
|
||||
return join(0, 0, true);
|
||||
}
|
||||
|
||||
size_t join_size() const
|
||||
{
|
||||
size_t size = 0;
|
||||
for (auto &b : *this)
|
||||
size += b->size();
|
||||
return size;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
BufferPtr bp = join();
|
||||
return buf_to_string(*bp);
|
||||
}
|
||||
|
||||
BufferCollection copy() const
|
||||
{
|
||||
BufferCollection ret;
|
||||
for (auto &b : *this)
|
||||
ret.emplace_back(new BufferAllocated(*b));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void put_consume(BufferAllocated& buf, const size_t tailroom = 0)
|
||||
{
|
||||
const size_t s = buf.size();
|
||||
if (!s)
|
||||
return;
|
||||
if (!empty())
|
||||
{
|
||||
// special optimization if buf data fits in
|
||||
// back() unused tail capacity -- if so, append
|
||||
// buf to existing back().
|
||||
BufferPtr& b = back();
|
||||
const size_t r = b->remaining(tailroom);
|
||||
if (s < r)
|
||||
{
|
||||
b->write(buf.read_alloc(s), s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
emplace_back(new BufferAllocated(std::move(buf)));
|
||||
}
|
||||
};
|
||||
|
||||
typedef BufferCollection<std::list> BufferList;
|
||||
typedef BufferCollection<std::vector> BufferVector;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// String methods on Buffer objects
|
||||
|
||||
#ifndef OPENVPN_BUFFER_BUFSTR_H
|
||||
#define OPENVPN_BUFFER_BUFSTR_H
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// return contents of Buffer as a std::string
|
||||
inline std::string buf_to_string(const Buffer& buf)
|
||||
{
|
||||
return std::string((const char *)buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
// return contents of ConstBuffer as a std::string
|
||||
inline std::string buf_to_string(const ConstBuffer& buf)
|
||||
{
|
||||
return std::string((const char *)buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
// write std::string to Buffer
|
||||
inline void buf_write_string(Buffer& buf, const std::string& str)
|
||||
{
|
||||
buf.write((unsigned char *)str.c_str(), str.length());
|
||||
}
|
||||
|
||||
// write C string to buffer
|
||||
inline void buf_write_string(Buffer& buf, const char *str)
|
||||
{
|
||||
buf.write((unsigned char *)str, std::strlen(str));
|
||||
}
|
||||
|
||||
// return BufferPtr from std::string
|
||||
inline BufferPtr buf_from_string(const std::string& str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
BufferPtr buf(new BufferAllocated(len, 0));
|
||||
buf->write((unsigned char *)str.c_str(), len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// return BufferPtr from C string
|
||||
inline BufferPtr buf_from_string(const char *str)
|
||||
{
|
||||
const size_t len = std::strlen(str);
|
||||
BufferPtr buf(new BufferAllocated(len, 0));
|
||||
buf->write((unsigned char *)str, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// return BufferAllocated from std::string
|
||||
inline BufferAllocated buf_alloc_from_string(const std::string& str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
BufferAllocated buf(len, 0);
|
||||
buf.write((unsigned char *)str.c_str(), len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// return BufferAllocated from C string
|
||||
inline BufferAllocated buf_alloc_from_string(const char *str)
|
||||
{
|
||||
const size_t len = std::strlen(str);
|
||||
BufferAllocated buf(len, 0);
|
||||
buf.write((unsigned char *)str, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// append str to buf
|
||||
inline void buf_append_string(Buffer& buf, const std::string& str)
|
||||
{
|
||||
buf.write((unsigned char *)str.c_str(), str.length());
|
||||
}
|
||||
|
||||
// append str to buf
|
||||
inline void buf_append_string(Buffer& buf, const char *str)
|
||||
{
|
||||
buf.write((unsigned char *)str, std::strlen(str));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_BUFSTREAM_H
|
||||
#define OPENVPN_BUFFER_BUFSTREAM_H
|
||||
|
||||
#include <streambuf>
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class BufferStream : public std::streambuf
|
||||
{
|
||||
public:
|
||||
BufferStream(Buffer& buffer) : buf(buffer) {}
|
||||
|
||||
protected:
|
||||
#if 0 // not implemented yet
|
||||
// input
|
||||
virtual std::streamsize showmanyc();
|
||||
virtual std::streamsize xsgetn(char* s, std::streamsize n);
|
||||
virtual int underflow();
|
||||
virtual int uflow();
|
||||
virtual int pbackfail(int c = EOF);
|
||||
#endif
|
||||
|
||||
// output
|
||||
virtual std::streamsize xsputn(const char* s, std::streamsize n)
|
||||
{
|
||||
buf.write((unsigned char *)s, (size_t)n);
|
||||
return n;
|
||||
}
|
||||
|
||||
virtual int overflow(int c = EOF)
|
||||
{
|
||||
if (c != EOF)
|
||||
{
|
||||
unsigned char uc = (unsigned char)c;
|
||||
buf.push_back(uc);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer& buf;
|
||||
};
|
||||
|
||||
class BufferStreamOut : public std::ostream
|
||||
{
|
||||
public:
|
||||
BufferStreamOut(Buffer& buffer)
|
||||
: std::ostream(new BufferStream(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
~BufferStreamOut()
|
||||
{
|
||||
delete rdbuf();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// A queue of buffers, implemented as std::deque<BufferPtr>.
|
||||
|
||||
#ifndef OPENVPN_BUFFER_MEMQ_H
|
||||
#define OPENVPN_BUFFER_MEMQ_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class MemQBase
|
||||
{
|
||||
public:
|
||||
MemQBase() : length(0) {}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return q.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return q.empty();
|
||||
}
|
||||
|
||||
size_t total_length() const { return length; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
while (!q.empty())
|
||||
q.pop_back();
|
||||
length = 0;
|
||||
}
|
||||
|
||||
void write_buf(const BufferPtr& bp)
|
||||
{
|
||||
q.push_back(bp);
|
||||
length += bp->size();
|
||||
}
|
||||
|
||||
BufferPtr read_buf()
|
||||
{
|
||||
BufferPtr ret = q.front();
|
||||
q.pop_front();
|
||||
length -= ret->size();
|
||||
return ret;
|
||||
}
|
||||
|
||||
BufferPtr& peek()
|
||||
{
|
||||
return q.front();
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
length -= q.front()->size();
|
||||
q.pop_front();
|
||||
}
|
||||
|
||||
void resize(const size_t cap)
|
||||
{
|
||||
q.resize(cap);
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef std::deque<BufferPtr> q_type;
|
||||
size_t length;
|
||||
q_type q;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_BUFFER_MEMQ_H
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_SAFESTR_H
|
||||
#define OPENVPN_BUFFER_SAFESTR_H
|
||||
|
||||
#include <cstring> // for std::strlen, and std::memset
|
||||
#include <ostream>
|
||||
|
||||
#include <openvpn/common/memneq.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class SafeString
|
||||
{
|
||||
static constexpr size_t INITIAL_CAPACITY = 32;
|
||||
static constexpr unsigned int BUF_FLAGS = BufferAllocated::DESTRUCT_ZERO|BufferAllocated::GROW;
|
||||
|
||||
public:
|
||||
SafeString()
|
||||
{
|
||||
}
|
||||
|
||||
SafeString(const char *str, const size_t size)
|
||||
: data(size+1, BUF_FLAGS)
|
||||
{
|
||||
data.write((unsigned char *)str, size);
|
||||
trail();
|
||||
}
|
||||
|
||||
SafeString(const char *str)
|
||||
: SafeString(str, std::strlen(str))
|
||||
{
|
||||
}
|
||||
|
||||
SafeString(const std::string& str)
|
||||
: SafeString(str.c_str(), str.length())
|
||||
{
|
||||
}
|
||||
|
||||
const char *c_str() const
|
||||
{
|
||||
if (data.defined())
|
||||
return (const char *)data.c_data();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
// Note: unsafe because of conversion to std::string
|
||||
std::string to_string() const
|
||||
{
|
||||
return buf_to_string(data);
|
||||
}
|
||||
|
||||
const size_t length() const
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
const bool empty() const
|
||||
{
|
||||
return !length();
|
||||
}
|
||||
|
||||
char& operator[](size_t pos)
|
||||
{
|
||||
return *reinterpret_cast<char *>(data.index(pos));
|
||||
}
|
||||
|
||||
const char& operator[](size_t pos) const
|
||||
{
|
||||
return *reinterpret_cast<const char *>(data.c_index(pos));
|
||||
}
|
||||
|
||||
bool operator==(const char *str) const
|
||||
{
|
||||
const size_t len = std::strlen(str);
|
||||
if (len != length())
|
||||
return false;
|
||||
return !crypto::memneq(str, c_str(), len);
|
||||
}
|
||||
|
||||
bool operator!=(const char *str) const
|
||||
{
|
||||
return !operator==(str);
|
||||
}
|
||||
|
||||
SafeString& operator+=(char c)
|
||||
{
|
||||
alloc();
|
||||
data.push_back((unsigned char)c);
|
||||
trail();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SafeString& operator+=(const char* s)
|
||||
{
|
||||
return append(s);
|
||||
}
|
||||
|
||||
SafeString& operator+=(const SafeString& str)
|
||||
{
|
||||
return append(str);
|
||||
}
|
||||
|
||||
SafeString& append(const char* s)
|
||||
{
|
||||
alloc();
|
||||
data.write((unsigned char *)s, std::strlen(s));
|
||||
trail();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SafeString& append(const SafeString& str)
|
||||
{
|
||||
alloc();
|
||||
data.append(str.data);
|
||||
trail();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SafeString& append(const SafeString& str, size_t subpos, size_t sublen)
|
||||
{
|
||||
alloc();
|
||||
data.append(str.data.range(subpos, sublen));
|
||||
trail();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reserve(const size_t n)
|
||||
{
|
||||
if (data.allocated())
|
||||
data.reserve(n+1);
|
||||
else
|
||||
data.init(n+1, BUF_FLAGS);
|
||||
}
|
||||
|
||||
void wipe()
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
void alloc()
|
||||
{
|
||||
if (!data.allocated())
|
||||
data.init(INITIAL_CAPACITY, BUF_FLAGS);
|
||||
}
|
||||
|
||||
void trail()
|
||||
{
|
||||
data.set_trailer(0);
|
||||
}
|
||||
|
||||
BufferAllocated data;
|
||||
};
|
||||
|
||||
template <typename Elem, typename Traits>
|
||||
std::basic_ostream<Elem, Traits>& operator<<(
|
||||
std::basic_ostream<Elem, Traits>& os, const SafeString& ss)
|
||||
{
|
||||
os << ss.c_str();
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_BUFFER_ZLIB_H
|
||||
#define OPENVPN_BUFFER_ZLIB_H
|
||||
|
||||
#ifdef OPENVPN_GZIP_DEBUG
|
||||
#define OPENVPN_GZIP_VERBOSE true
|
||||
#else
|
||||
#define OPENVPN_GZIP_VERBOSE false
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
|
||||
#include <cstring> // for std::memset
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace ZLib {
|
||||
OPENVPN_EXCEPTION(zlib_error);
|
||||
|
||||
class ZStreamBase // used internally by compress_gzip/decompress_gzip
|
||||
{
|
||||
public:
|
||||
z_stream s;
|
||||
protected:
|
||||
ZStreamBase() { std::memset(&s, 0, sizeof(s)); }
|
||||
private:
|
||||
ZStreamBase(const ZStreamBase&) = delete;
|
||||
ZStreamBase& operator=(const ZStreamBase&) = delete;
|
||||
};
|
||||
|
||||
inline BufferPtr compress_gzip(BufferPtr src,
|
||||
const size_t headroom,
|
||||
const size_t tailroom,
|
||||
const int level,
|
||||
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
||||
const int window_bits=15,
|
||||
const int mem_level=8)
|
||||
{
|
||||
constexpr int GZIP_ENCODING = 16;
|
||||
|
||||
struct ZStream : public ZStreamBase {
|
||||
~ZStream() { ::deflateEnd(&s); }
|
||||
};
|
||||
|
||||
if (src)
|
||||
{
|
||||
int status;
|
||||
ZStream zs;
|
||||
zs.s.next_in = src->data();
|
||||
zs.s.avail_in = src->size();
|
||||
status = ::deflateInit2(&zs.s,
|
||||
level,
|
||||
Z_DEFLATED,
|
||||
GZIP_ENCODING + window_bits,
|
||||
mem_level,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (status != Z_OK)
|
||||
OPENVPN_THROW(zlib_error, "zlib deflateinit2 failed, error=" << status);
|
||||
const uLong outcap = ::deflateBound(&zs.s, src->size());
|
||||
BufferPtr b = new BufferAllocated(outcap + headroom + tailroom, 0);
|
||||
b->init_headroom(headroom);
|
||||
zs.s.next_out = b->data();
|
||||
zs.s.avail_out = outcap;
|
||||
status = ::deflate(&zs.s, Z_FINISH);
|
||||
if (status != Z_STREAM_END)
|
||||
OPENVPN_THROW(zlib_error, "zlib deflate failed, error=" << status);
|
||||
b->set_size(zs.s.total_out);
|
||||
if (verbose)
|
||||
OPENVPN_LOG("*** COMPRESS " << src->size() << " -> " << b->size());
|
||||
return b;
|
||||
}
|
||||
else
|
||||
return BufferPtr();
|
||||
}
|
||||
|
||||
inline BufferPtr decompress_gzip(BufferPtr src,
|
||||
const size_t headroom,
|
||||
const size_t tailroom,
|
||||
const size_t max_size,
|
||||
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
||||
const size_t block_size=4096,
|
||||
const int window_bits=15)
|
||||
{
|
||||
constexpr int GZIP_ENCODING = 16;
|
||||
|
||||
struct ZStream : public ZStreamBase {
|
||||
~ZStream() { ::inflateEnd(&s); }
|
||||
};
|
||||
|
||||
if (src)
|
||||
{
|
||||
int status;
|
||||
ZStream zs;
|
||||
zs.s.next_in = src->data();
|
||||
zs.s.avail_in = src->size();
|
||||
status = ::inflateInit2(&zs.s, GZIP_ENCODING + window_bits);
|
||||
if (status != Z_OK)
|
||||
OPENVPN_THROW(zlib_error, "zlib inflateinit2 failed, error=" << status);
|
||||
|
||||
BufferList blist;
|
||||
size_t hr = headroom;
|
||||
size_t tr = tailroom;
|
||||
do {
|
||||
// use headroom/tailroom on first block to take advantage
|
||||
// of BufferList::join() optimization for one-block lists
|
||||
BufferPtr b = new BufferAllocated(block_size + hr + tr, 0);
|
||||
b->init_headroom(hr);
|
||||
const size_t avail = b->remaining(tr);
|
||||
zs.s.next_out = b->data();
|
||||
zs.s.avail_out = avail;
|
||||
status = ::inflate(&zs.s, Z_SYNC_FLUSH);
|
||||
if (status != Z_OK && status != Z_STREAM_END)
|
||||
OPENVPN_THROW(zlib_error, "zlib inflate failed, error=" << status);
|
||||
b->set_size(avail - zs.s.avail_out);
|
||||
blist.push_back(std::move(b));
|
||||
if (max_size && zs.s.total_out > max_size)
|
||||
OPENVPN_THROW(zlib_error, "zlib inflate max_size " << max_size << " exceeded");
|
||||
hr = tr = 0;
|
||||
} while (status == Z_OK);
|
||||
if (verbose)
|
||||
OPENVPN_LOG("*** DECOMPRESS " << src->size() << " -> " << blist.join_size());
|
||||
return blist.join(headroom, tailroom, true);
|
||||
}
|
||||
else
|
||||
return BufferPtr();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,601 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// This file implements the top-level connection logic for an OpenVPN client
|
||||
// connection. It is concerned with starting, stopping, pausing, and resuming
|
||||
// OpenVPN client connections. It deals with retrying a connection and handles
|
||||
// the connection timeout. It also deals with connection exceptions and understands
|
||||
// the difference between an exception that should halt any further reconnection
|
||||
// attempts (such as AUTH_FAILED), and other exceptions such as network errors
|
||||
// that would justify a retry.
|
||||
//
|
||||
// Some of the methods in the class (such as stop, pause, and reconnect) are often
|
||||
// called by another thread that is controlling the connection, therefore
|
||||
// thread-safe methods are provided where the thread-safe function posts a message
|
||||
// to the actual connection thread.
|
||||
//
|
||||
// In an OpenVPN client connection, the following object stack would be used:
|
||||
//
|
||||
// 1. class ClientConnect --
|
||||
// The top level object in an OpenVPN client connection.
|
||||
// 2. class ClientProto::Session --
|
||||
// The OpenVPN client protocol object.
|
||||
// 3. class ProtoContext --
|
||||
// The core OpenVPN protocol implementation that is common to both
|
||||
// client and server.
|
||||
// 4. ProtoStackBase<Packet> --
|
||||
// The lowest-level class that implements the basic functionality of
|
||||
// tunneling a protocol over a reliable or unreliable transport
|
||||
// layer, but isn't specific to OpenVPN per-se.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLICONNECT_H
|
||||
#define OPENVPN_CLIENT_CLICONNECT_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/client/cliopt.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/client/clilife.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// ClientConnect implements an "always-try-to-reconnect" approach, with remote
|
||||
// list rotation. Only gives up on auth failure or other fatal errors that
|
||||
// cannot be remedied by retrying.
|
||||
class ClientConnect : ClientProto::NotifyCallback,
|
||||
RemoteList::PreResolve::NotifyCallback,
|
||||
ClientLifeCycle::NotifyCallback,
|
||||
public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConnect> Ptr;
|
||||
typedef ClientOptions::Client Client;
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(client_connect_unhandled_exception);
|
||||
|
||||
ClientConnect(asio::io_context& io_context_arg,
|
||||
const ClientOptions::Ptr& client_options_arg)
|
||||
: generation(0),
|
||||
halt(false),
|
||||
paused(false),
|
||||
client_finalized(false),
|
||||
dont_restart_(false),
|
||||
lifecycle_started(false),
|
||||
conn_timeout(client_options_arg->conn_timeout()),
|
||||
io_context(io_context_arg),
|
||||
client_options(client_options_arg),
|
||||
server_poll_timer(io_context_arg),
|
||||
restart_wait_timer(io_context_arg),
|
||||
conn_timer(io_context_arg),
|
||||
conn_timer_pending(false)
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (!client && !halt)
|
||||
{
|
||||
if (!test_network())
|
||||
throw ErrorCode(Error::NETWORK_UNAVAILABLE, true, "Network Unavailable");
|
||||
|
||||
RemoteList::Ptr remote_list = client_options->remote_list_precache();
|
||||
RemoteList::PreResolve::Ptr preres(new RemoteList::PreResolve(io_context,
|
||||
remote_list,
|
||||
client_options->stats_ptr()));
|
||||
if (preres->work_available())
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Resolve();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
pre_resolve = preres;
|
||||
pre_resolve->start(this); // asynchronous -- will call back to pre_resolve_done
|
||||
}
|
||||
else
|
||||
new_client();
|
||||
}
|
||||
}
|
||||
|
||||
void send_explicit_exit_notify()
|
||||
{
|
||||
if (!halt && client)
|
||||
client->send_explicit_exit_notify();
|
||||
}
|
||||
|
||||
void graceful_stop()
|
||||
{
|
||||
send_explicit_exit_notify();
|
||||
//sleep(5); // simulate slow stop (comment out for production)
|
||||
stop();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
if (pre_resolve)
|
||||
pre_resolve->cancel();
|
||||
if (client)
|
||||
{
|
||||
client->tun_set_disconnect();
|
||||
client->stop(false);
|
||||
}
|
||||
cancel_timers();
|
||||
asio_work.reset();
|
||||
|
||||
client_options->finalize(true);
|
||||
|
||||
if (lifecycle_started)
|
||||
{
|
||||
ClientLifeCycle* lc = client_options->lifecycle();
|
||||
if (lc)
|
||||
lc->stop();
|
||||
}
|
||||
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Disconnected();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
}
|
||||
}
|
||||
|
||||
void stop_on_signal(const asio::error_code& error, int signal_number)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
// like stop() but may be safely called by another thread
|
||||
void thread_safe_stop()
|
||||
{
|
||||
if (!halt)
|
||||
asio::post(io_context, [self=Ptr(this)]()
|
||||
{
|
||||
self->graceful_stop();
|
||||
});
|
||||
}
|
||||
|
||||
void pause(const std::string& reason)
|
||||
{
|
||||
if (!halt && !paused)
|
||||
{
|
||||
paused = true;
|
||||
if (client)
|
||||
{
|
||||
client->send_explicit_exit_notify();
|
||||
client->stop(false);
|
||||
interim_finalize();
|
||||
}
|
||||
cancel_timers();
|
||||
asio_work.reset(new asio::io_context::work(io_context));
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Pause(reason);
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::N_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
if (!halt && paused)
|
||||
{
|
||||
paused = false;
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Resume();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->remote_reset_cache_item();
|
||||
new_client();
|
||||
}
|
||||
}
|
||||
|
||||
void reconnect(int seconds)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (seconds < 0)
|
||||
seconds = 0;
|
||||
OPENVPN_LOG("Client terminated, reconnecting in " << seconds << "...");
|
||||
server_poll_timer.cancel();
|
||||
client_options->remote_reset_cache_item();
|
||||
restart_wait_timer.expires_at(Time::now() + Time::Duration::seconds(seconds));
|
||||
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const asio::error_code& error)
|
||||
{
|
||||
self->restart_wait_callback(gen, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void thread_safe_pause(const std::string& reason)
|
||||
{
|
||||
if (!halt)
|
||||
asio::post(io_context, [self=Ptr(this), reason]()
|
||||
{
|
||||
self->pause(reason);
|
||||
});
|
||||
}
|
||||
|
||||
void thread_safe_resume()
|
||||
{
|
||||
if (!halt)
|
||||
asio::post(io_context, [self=Ptr(this)]()
|
||||
{
|
||||
self->resume();
|
||||
});
|
||||
}
|
||||
|
||||
void thread_safe_reconnect(int seconds)
|
||||
{
|
||||
if (!halt)
|
||||
asio::post(io_context, [self=Ptr(this), seconds]()
|
||||
{
|
||||
self->reconnect(seconds);
|
||||
});
|
||||
}
|
||||
|
||||
void dont_restart()
|
||||
{
|
||||
dont_restart_ = true;
|
||||
}
|
||||
|
||||
~ClientConnect()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
void interim_finalize()
|
||||
{
|
||||
if (!client_finalized)
|
||||
{
|
||||
client_options->finalize(false);
|
||||
client_finalized = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void pre_resolve_done()
|
||||
{
|
||||
if (!halt)
|
||||
new_client();
|
||||
}
|
||||
|
||||
void cancel_timers()
|
||||
{
|
||||
restart_wait_timer.cancel();
|
||||
server_poll_timer.cancel();
|
||||
conn_timer.cancel();
|
||||
conn_timer_pending = false;
|
||||
}
|
||||
|
||||
void restart_wait_callback(unsigned int gen, const asio::error_code& e)
|
||||
{
|
||||
if (!e && gen == generation && !halt)
|
||||
{
|
||||
if (paused)
|
||||
resume();
|
||||
else
|
||||
{
|
||||
if (client)
|
||||
client->send_explicit_exit_notify();
|
||||
new_client();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void server_poll_callback(unsigned int gen, const asio::error_code& e)
|
||||
{
|
||||
if (!e && gen == generation && !halt && !client->first_packet_received())
|
||||
{
|
||||
OPENVPN_LOG("Server poll timeout, trying next remote entry...");
|
||||
new_client();
|
||||
}
|
||||
}
|
||||
|
||||
void conn_timer_callback(unsigned int gen, const asio::error_code& e)
|
||||
{
|
||||
if (!e && !halt)
|
||||
{
|
||||
client_options->stats().error(Error::CONNECTION_TIMEOUT);
|
||||
if (!paused && client_options->pause_on_connection_timeout())
|
||||
{
|
||||
// go into pause state instead of disconnect
|
||||
pause("");
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::ConnectionTimeout();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void conn_timer_start()
|
||||
{
|
||||
if (!conn_timer_pending && conn_timeout > 0)
|
||||
{
|
||||
conn_timer.expires_at(Time::now() + Time::Duration::seconds(conn_timeout));
|
||||
conn_timer.async_wait([self=Ptr(this), gen=generation](const asio::error_code& error)
|
||||
{
|
||||
self->conn_timer_callback(gen, error);
|
||||
});
|
||||
conn_timer_pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_network() const
|
||||
{
|
||||
ClientLifeCycle* lc = client_options->lifecycle();
|
||||
if (lc)
|
||||
{
|
||||
if (!lc->network_available())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void client_proto_connected()
|
||||
{
|
||||
conn_timer.cancel();
|
||||
conn_timer_pending = false;
|
||||
|
||||
// Monitor connection lifecycle notifications, such as sleep,
|
||||
// wakeup, network-unavailable, and network-available.
|
||||
// Not all platforms define a lifecycle object. Some platforms
|
||||
// such as Android and iOS manage lifecycle notifications
|
||||
// in the UI, and they call pause(), resume(), reconnect(), etc.
|
||||
// as needed using the main ovpncli API.
|
||||
if (!lifecycle_started)
|
||||
{
|
||||
ClientLifeCycle* lc = client_options->lifecycle(); // lifecycle is defined by platform, and may be NULL
|
||||
if (lc)
|
||||
{
|
||||
lc->start(this);
|
||||
lifecycle_started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queue_restart(const unsigned int delay = 2)
|
||||
{
|
||||
OPENVPN_LOG("Client terminated, restarting in " << delay << "...");
|
||||
server_poll_timer.cancel();
|
||||
interim_finalize();
|
||||
client_options->remote_reset_cache_item();
|
||||
restart_wait_timer.expires_at(Time::now() + Time::Duration::seconds(delay));
|
||||
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const asio::error_code& error)
|
||||
{
|
||||
self->restart_wait_callback(gen, error);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void client_proto_terminate()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (dont_restart_)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (client->fatal())
|
||||
{
|
||||
case Error::UNDEF: // means that there wasn't a fatal error
|
||||
queue_restart();
|
||||
break;
|
||||
|
||||
// Errors below will cause the client to NOT retry the connection,
|
||||
// or otherwise give the error special handling.
|
||||
|
||||
case Error::AUTH_FAILED:
|
||||
{
|
||||
const std::string& reason = client->fatal_reason();
|
||||
if (ChallengeResponse::is_dynamic(reason)) // dynamic challenge/response?
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::DynamicChallenge(reason);
|
||||
client_options->events().add_event(std::move(ev));
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::AuthFailed(reason);
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::AUTH_FAILED);
|
||||
}
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::TUN_SETUP_FAILED:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TunSetupFailed(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TUN_SETUP_FAILED);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::TUN_IFACE_CREATE:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TunIfaceCreate(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TUN_IFACE_CREATE);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::TUN_IFACE_DISABLED:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TunIfaceDisabled(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TUN_IFACE_DISABLED);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::PROXY_ERROR:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::ProxyError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::PROXY_ERROR);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::PROXY_NEED_CREDS:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::ProxyNeedCreds(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::PROXY_NEED_CREDS);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::CERT_VERIFY_FAIL:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::CertVerifyFail(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::CERT_VERIFY_FAIL);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::TLS_VERSION_MIN:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TLSVersionMinFail();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TLS_VERSION_MIN);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::CLIENT_HALT:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::ClientHalt(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::CLIENT_HALT);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::CLIENT_RESTART:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::ClientRestart(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::CLIENT_RESTART);
|
||||
queue_restart();
|
||||
}
|
||||
break;
|
||||
case Error::INACTIVE_TIMEOUT:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::InactiveTimeout();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::INACTIVE_TIMEOUT);
|
||||
graceful_stop();
|
||||
}
|
||||
break;
|
||||
case Error::TRANSPORT_ERROR:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TransportError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TRANSPORT_ERROR);
|
||||
queue_restart(5); // use a larger timeout to allow preemption from higher levels
|
||||
}
|
||||
break;
|
||||
case Error::TUN_ERROR:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TunError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TUN_ERROR);
|
||||
queue_restart(5);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw client_connect_unhandled_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void new_client()
|
||||
{
|
||||
++generation;
|
||||
asio_work.reset();
|
||||
if (client)
|
||||
{
|
||||
client->stop(false);
|
||||
interim_finalize();
|
||||
}
|
||||
if (generation > 1)
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Reconnecting();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::N_RECONNECT);
|
||||
if (!(client && client->reached_connected_state()))
|
||||
client_options->next();
|
||||
}
|
||||
Client::Config::Ptr cli_config = client_options->client_config(); // client_config in cliopt.hpp
|
||||
client.reset(new Client(io_context, *cli_config, this)); // build ClientProto::Session from cliproto.hpp
|
||||
client_finalized = false;
|
||||
|
||||
restart_wait_timer.cancel();
|
||||
if (client_options->server_poll_timeout_enabled())
|
||||
{
|
||||
server_poll_timer.expires_at(Time::now() + client_options->server_poll_timeout());
|
||||
server_poll_timer.async_wait([self=Ptr(this), gen=generation](const asio::error_code& error)
|
||||
{
|
||||
self->server_poll_callback(gen, error);
|
||||
});
|
||||
}
|
||||
conn_timer_start();
|
||||
client->start();
|
||||
}
|
||||
|
||||
// ClientLifeCycle::NotifyCallback callbacks
|
||||
|
||||
virtual void cln_stop()
|
||||
{
|
||||
thread_safe_stop();
|
||||
}
|
||||
|
||||
virtual void cln_pause(const std::string& reason)
|
||||
{
|
||||
thread_safe_pause(reason);
|
||||
}
|
||||
|
||||
virtual void cln_resume()
|
||||
{
|
||||
thread_safe_resume();
|
||||
}
|
||||
|
||||
virtual void cln_reconnect(int seconds)
|
||||
{
|
||||
thread_safe_reconnect(seconds);
|
||||
}
|
||||
|
||||
unsigned int generation;
|
||||
bool halt;
|
||||
bool paused;
|
||||
bool client_finalized;
|
||||
bool dont_restart_;
|
||||
bool lifecycle_started;
|
||||
int conn_timeout;
|
||||
asio::io_context& io_context;
|
||||
ClientOptions::Ptr client_options;
|
||||
Client::Ptr client;
|
||||
AsioTimer server_poll_timer;
|
||||
AsioTimer restart_wait_timer;
|
||||
AsioTimer conn_timer;
|
||||
bool conn_timer_pending;
|
||||
std::unique_ptr<asio::io_context::work> asio_work;
|
||||
RemoteList::PreResolve::Ptr pre_resolve;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_CLIENT_CLICONSTANTS_H
|
||||
#define OPENVPN_CLIENT_CLICONSTANTS_H
|
||||
|
||||
// Various sanity checks for different limits on OpenVPN clients
|
||||
|
||||
namespace openvpn {
|
||||
namespace ProfileParseLimits {
|
||||
enum {
|
||||
MAX_PROFILE_SIZE=262144, // maximum size of an OpenVPN configuration file
|
||||
MAX_PUSH_SIZE=262144, // maximum size of aggregate data that can be pushed to a client
|
||||
MAX_LINE_SIZE=512, // maximum size of an OpenVPN configuration file line
|
||||
MAX_DIRECTIVE_SIZE=64, // maximum number of chars in an OpenVPN directive
|
||||
OPT_OVERHEAD=64, // bytes overhead of one option/directive, for accounting purposes
|
||||
TERM_OVERHEAD=16, // bytes overhead of one argument in an option, for accounting purposes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,216 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// This class encapsulates the state of authentication credentials
|
||||
// maintained by an OpenVPN client. It understands dynamic
|
||||
// challenge/response cookies, and Session Token IDs (where the
|
||||
// password in the object is wiped and replaced by a token used
|
||||
// for further authentications).
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLICREDS_H
|
||||
#define OPENVPN_CLIENT_CLICREDS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
#include <openvpn/auth/cr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ClientCreds : public RC<thread_unsafe_refcount> {
|
||||
public:
|
||||
typedef RCPtr<ClientCreds> Ptr;
|
||||
|
||||
ClientCreds() : allow_cache_password(false),
|
||||
password_save_defined(false),
|
||||
replace_password_with_session_id(false),
|
||||
did_replace_password_with_session_id(false) {}
|
||||
|
||||
void set_username(const std::string& username_arg)
|
||||
{
|
||||
username = username_arg;
|
||||
}
|
||||
|
||||
void set_password(const std::string& password_arg)
|
||||
{
|
||||
password = password_arg;
|
||||
did_replace_password_with_session_id = false;
|
||||
}
|
||||
|
||||
void set_response(const std::string& response_arg)
|
||||
{
|
||||
response = response_arg;
|
||||
}
|
||||
|
||||
void set_dynamic_challenge_cookie(const std::string& cookie, const std::string& username)
|
||||
{
|
||||
if (!cookie.empty())
|
||||
dynamic_challenge.reset(new ChallengeResponse(cookie, username));
|
||||
}
|
||||
|
||||
void set_replace_password_with_session_id(const bool value)
|
||||
{
|
||||
replace_password_with_session_id = value;
|
||||
}
|
||||
|
||||
void enable_password_cache(const bool value)
|
||||
{
|
||||
allow_cache_password = value;
|
||||
}
|
||||
|
||||
bool get_replace_password_with_session_id() const
|
||||
{
|
||||
return replace_password_with_session_id;
|
||||
}
|
||||
|
||||
void set_session_id(const std::string& user, const std::string& sess_id)
|
||||
{
|
||||
// force Session ID use if dynamic challenge is enabled
|
||||
if (dynamic_challenge && !replace_password_with_session_id)
|
||||
replace_password_with_session_id = true;
|
||||
|
||||
if (replace_password_with_session_id)
|
||||
{
|
||||
if (allow_cache_password && !password_save_defined)
|
||||
{
|
||||
password_save = password;
|
||||
password_save_defined = true;
|
||||
}
|
||||
password = sess_id;
|
||||
response = "";
|
||||
if (dynamic_challenge)
|
||||
{
|
||||
username = dynamic_challenge->get_username();
|
||||
dynamic_challenge.reset();
|
||||
}
|
||||
else if (!user.empty())
|
||||
username = user;
|
||||
did_replace_password_with_session_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_username() const
|
||||
{
|
||||
if (dynamic_challenge)
|
||||
return dynamic_challenge->get_username();
|
||||
else
|
||||
return username;
|
||||
}
|
||||
|
||||
std::string get_password() const
|
||||
{
|
||||
if (dynamic_challenge)
|
||||
return dynamic_challenge->construct_dynamic_password(response);
|
||||
else if (response.empty())
|
||||
return password;
|
||||
else
|
||||
return ChallengeResponse::construct_static_password(password, response);
|
||||
}
|
||||
|
||||
bool username_defined() const
|
||||
{
|
||||
return !username.empty();
|
||||
}
|
||||
|
||||
bool password_defined() const
|
||||
{
|
||||
return !password.empty();
|
||||
}
|
||||
|
||||
bool session_id_defined() const
|
||||
{
|
||||
return did_replace_password_with_session_id;
|
||||
}
|
||||
|
||||
bool can_retry_auth_with_cached_password()
|
||||
{
|
||||
if (password_save_defined)
|
||||
{
|
||||
password = password_save;
|
||||
password_save = "";
|
||||
password_save_defined = false;
|
||||
did_replace_password_with_session_id = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string auth_info() const
|
||||
{
|
||||
std::string ret;
|
||||
if (dynamic_challenge)
|
||||
{
|
||||
ret = "DynamicChallenge";
|
||||
}
|
||||
else if (response.empty())
|
||||
{
|
||||
if (!username.empty())
|
||||
ret += "Username";
|
||||
else
|
||||
ret += "UsernameEmpty";
|
||||
ret += '/';
|
||||
if (!password.empty())
|
||||
{
|
||||
if (did_replace_password_with_session_id)
|
||||
ret += "SessionID";
|
||||
else
|
||||
ret += "Password";
|
||||
}
|
||||
else
|
||||
ret += "PasswordEmpty";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "StaticChallenge";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
// Standard credentials
|
||||
std::string username;
|
||||
std::string password;
|
||||
|
||||
// Password caching
|
||||
bool allow_cache_password;
|
||||
bool password_save_defined;
|
||||
std::string password_save;
|
||||
|
||||
// Response to challenge
|
||||
std::string response;
|
||||
|
||||
// Info describing a dynamic challenge
|
||||
ChallengeResponse::Ptr dynamic_challenge;
|
||||
|
||||
// If true, on successful connect, we will replace the password
|
||||
// with the session ID we receive from the server.
|
||||
bool replace_password_with_session_id;
|
||||
|
||||
// true if password has been replaced with Session ID
|
||||
bool did_replace_password_with_session_id;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Emulate Excluded Routes implementation (needed by Android)
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLIEMUEXR_H
|
||||
#define OPENVPN_CLIENT_CLIEMUEXR_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/tun/client/emuexr.hpp>
|
||||
#include <openvpn/addr/routeinv.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class EmulateExcludeRouteImpl : public EmulateExcludeRoute
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(emulate_exclude_route_error);
|
||||
|
||||
typedef RCPtr<EmulateExcludeRouteImpl> Ptr;
|
||||
|
||||
EmulateExcludeRouteImpl(const bool exclude_server_address)
|
||||
: exclude_server_address_(exclude_server_address)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len)
|
||||
{
|
||||
(add ? include : exclude).emplace_back(addr, prefix_len);
|
||||
}
|
||||
|
||||
virtual bool enabled(const IPVerFlags& ipv) const
|
||||
{
|
||||
return exclude.size() && (ipv.rgv4() || ipv.rgv6());
|
||||
}
|
||||
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const
|
||||
{
|
||||
const unsigned int rg_ver_flags = ipv.rg_ver_flags();
|
||||
if (exclude.size() && rg_ver_flags)
|
||||
{
|
||||
IP::RouteList rl;
|
||||
rl.reserve(include.size() + exclude.size());
|
||||
rl.insert(rl.end(), include.begin(), include.end());
|
||||
rl.insert(rl.end(), exclude.begin(), exclude.end());
|
||||
|
||||
if (exclude_server_address_ && (server_addr.version_mask() & rg_ver_flags))
|
||||
rl.emplace_back(server_addr, server_addr.size());
|
||||
|
||||
const IP::RouteInverter ri(rl, rg_ver_flags);
|
||||
OPENVPN_LOG("Exclude routes emulation:\n" << ri);
|
||||
for (IP::RouteInverter::const_iterator i = ri.begin(); i != ri.end(); ++i)
|
||||
{
|
||||
const IP::Route& r = *i;
|
||||
if (!tb->tun_builder_add_route(r.addr.to_string(), r.prefix_len, -1, r.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
|
||||
ipv.set_emulate_exclude_routes();
|
||||
}
|
||||
}
|
||||
|
||||
const bool exclude_server_address_;
|
||||
IP::RouteList include;
|
||||
IP::RouteList exclude;
|
||||
};
|
||||
|
||||
class EmulateExcludeRouteFactoryImpl : public EmulateExcludeRouteFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<EmulateExcludeRouteFactoryImpl> Ptr;
|
||||
|
||||
EmulateExcludeRouteFactoryImpl(const bool exclude_server_address)
|
||||
: exclude_server_address_(exclude_server_address)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual EmulateExcludeRoute::Ptr new_obj() const
|
||||
{
|
||||
return EmulateExcludeRoute::Ptr(new EmulateExcludeRouteImpl(exclude_server_address_));
|
||||
}
|
||||
|
||||
const bool exclude_server_address_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,394 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// This file describes the basic set of OpenVPN client events, including the
|
||||
// normal events leading up to a connection as well as error events.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLIEVENT_H
|
||||
#define OPENVPN_CLIENT_CLIEVENT_H
|
||||
|
||||
#include <sstream>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace ClientEvent {
|
||||
enum Type {
|
||||
// normal events including disconnected, connected, and other transitional events
|
||||
DISCONNECTED=0,
|
||||
CONNECTED,
|
||||
RECONNECTING,
|
||||
RESOLVE,
|
||||
WAIT,
|
||||
WAIT_PROXY,
|
||||
CONNECTING,
|
||||
GET_CONFIG,
|
||||
ASSIGN_IP,
|
||||
ADD_ROUTES,
|
||||
ECHO_OPT,
|
||||
INFO,
|
||||
PAUSE,
|
||||
RESUME,
|
||||
|
||||
// start of nonfatal errors, must be marked by NONFATAL_ERROR_START below
|
||||
TRANSPORT_ERROR,
|
||||
TUN_ERROR,
|
||||
CLIENT_RESTART,
|
||||
|
||||
// start of errors, must be marked by FATAL_ERROR_START below
|
||||
AUTH_FAILED,
|
||||
CERT_VERIFY_FAIL,
|
||||
TLS_VERSION_MIN,
|
||||
CLIENT_HALT,
|
||||
CONNECTION_TIMEOUT,
|
||||
INACTIVE_TIMEOUT,
|
||||
DYNAMIC_CHALLENGE,
|
||||
PROXY_NEED_CREDS,
|
||||
PROXY_ERROR,
|
||||
TUN_SETUP_FAILED,
|
||||
TUN_IFACE_CREATE,
|
||||
TUN_IFACE_DISABLED,
|
||||
EPKI_ERROR, // EPKI refers to External PKI errors, i.e. errors in accessing external
|
||||
EPKI_INVALID_ALIAS, // certificates or keys.
|
||||
|
||||
N_TYPES
|
||||
};
|
||||
|
||||
enum {
|
||||
NONFATAL_ERROR_START = TRANSPORT_ERROR, // start of nonfatal errors that automatically reconnect
|
||||
FATAL_ERROR_START = AUTH_FAILED, // start of fatal errors
|
||||
};
|
||||
|
||||
inline const char *event_name(const Type type)
|
||||
{
|
||||
static const char *names[] = {
|
||||
"DISCONNECTED",
|
||||
"CONNECTED",
|
||||
"RECONNECTING",
|
||||
"RESOLVE",
|
||||
"WAIT",
|
||||
"WAIT_PROXY",
|
||||
"CONNECTING",
|
||||
"GET_CONFIG",
|
||||
"ASSIGN_IP",
|
||||
"ADD_ROUTES",
|
||||
"ECHO",
|
||||
"INFO",
|
||||
"PAUSE",
|
||||
"RESUME",
|
||||
|
||||
// nonfatal errors
|
||||
"TRANSPORT_ERROR",
|
||||
"TUN_ERROR",
|
||||
"CLIENT_RESTART",
|
||||
|
||||
// fatal errors
|
||||
"AUTH_FAILED",
|
||||
"CERT_VERIFY_FAIL",
|
||||
"TLS_VERSION_MIN",
|
||||
"CLIENT_HALT",
|
||||
"CONNECTION_TIMEOUT",
|
||||
"INACTIVE_TIMEOUT",
|
||||
"DYNAMIC_CHALLENGE",
|
||||
"PROXY_NEED_CREDS",
|
||||
"PROXY_ERROR",
|
||||
"TUN_SETUP_FAILED",
|
||||
"TUN_IFACE_CREATE",
|
||||
"TUN_IFACE_DISABLED",
|
||||
"EPKI_ERROR",
|
||||
"EPKI_INVALID_ALIAS",
|
||||
};
|
||||
|
||||
static_assert(N_TYPES == array_size(names), "event names array inconsistency");
|
||||
if (type < N_TYPES)
|
||||
return names[type];
|
||||
else
|
||||
return "UNKNOWN_EVENT_TYPE";
|
||||
}
|
||||
|
||||
struct Connected;
|
||||
|
||||
// The base class for all events.
|
||||
class Base : public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Base> Ptr;
|
||||
Base(Type id) : id_(id) {}
|
||||
|
||||
Type id() const { return id_; }
|
||||
|
||||
const char *name() const
|
||||
{
|
||||
return event_name(id_);
|
||||
}
|
||||
|
||||
bool is_error() const
|
||||
{
|
||||
return int(id_) >= NONFATAL_ERROR_START;
|
||||
}
|
||||
|
||||
bool is_fatal() const
|
||||
{
|
||||
return int(id_) >= FATAL_ERROR_START;
|
||||
}
|
||||
|
||||
virtual std::string render() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
const Connected* connected_cast() const
|
||||
{
|
||||
if (id_ == CONNECTED)
|
||||
return (const Connected*)this;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Type id_;
|
||||
};
|
||||
|
||||
// Specific client events. Some events have no additional data attached to them,
|
||||
// while other events (such as Connected) have many additional data fields.
|
||||
|
||||
struct Resolve : public Base
|
||||
{
|
||||
Resolve() : Base(RESOLVE) {}
|
||||
};
|
||||
|
||||
struct Wait : public Base
|
||||
{
|
||||
Wait() : Base(WAIT) {}
|
||||
};
|
||||
|
||||
struct WaitProxy : public Base
|
||||
{
|
||||
WaitProxy() : Base(WAIT_PROXY) {}
|
||||
};
|
||||
|
||||
struct Connecting : public Base
|
||||
{
|
||||
Connecting() : Base(CONNECTING) {}
|
||||
};
|
||||
|
||||
struct Reconnecting : public Base
|
||||
{
|
||||
Reconnecting() : Base(RECONNECTING) {}
|
||||
};
|
||||
|
||||
struct GetConfig : public Base
|
||||
{
|
||||
GetConfig() : Base(GET_CONFIG) {}
|
||||
};
|
||||
|
||||
struct AssignIP : public Base
|
||||
{
|
||||
AssignIP() : Base(ASSIGN_IP) {}
|
||||
};
|
||||
|
||||
struct AddRoutes : public Base
|
||||
{
|
||||
AddRoutes() : Base(ADD_ROUTES) {}
|
||||
};
|
||||
|
||||
struct Resume : public Base
|
||||
{
|
||||
Resume() : Base(RESUME) {}
|
||||
};
|
||||
|
||||
struct Disconnected : public Base
|
||||
{
|
||||
Disconnected() : Base(DISCONNECTED) {}
|
||||
};
|
||||
|
||||
struct ConnectionTimeout : public Base
|
||||
{
|
||||
ConnectionTimeout() : Base(CONNECTION_TIMEOUT) {}
|
||||
};
|
||||
|
||||
struct InactiveTimeout : public Base
|
||||
{
|
||||
InactiveTimeout() : Base(INACTIVE_TIMEOUT) {}
|
||||
};
|
||||
|
||||
struct TLSVersionMinFail : public Base
|
||||
{
|
||||
TLSVersionMinFail() : Base(TLS_VERSION_MIN) {}
|
||||
};
|
||||
|
||||
struct Connected : public Base
|
||||
{
|
||||
typedef RCPtr<Connected> Ptr;
|
||||
|
||||
Connected() : Base(CONNECTED) {}
|
||||
|
||||
std::string user;
|
||||
std::string server_host;
|
||||
std::string server_port;
|
||||
std::string server_proto;
|
||||
std::string server_ip;
|
||||
std::string vpn_ip4;
|
||||
std::string vpn_ip6;
|
||||
std::string vpn_gw4;
|
||||
std::string vpn_gw6;
|
||||
std::string client_ip;
|
||||
std::string tun_name;
|
||||
|
||||
virtual std::string render() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
// eg. "godot@foo.bar.gov:443 (1.2.3.4) via TCPv4 on tun0/5.5.1.1"
|
||||
out << user << '@';
|
||||
if (server_host.find_first_of(':') == std::string::npos)
|
||||
out << server_host;
|
||||
else
|
||||
out << '[' << server_host << ']';
|
||||
out << ':' << server_port
|
||||
<< " (" << server_ip << ") via " << client_ip << '/' << server_proto
|
||||
<< " on " << tun_name << '/' << vpn_ip4 << '/' << vpn_ip6
|
||||
<< " gw=[" << vpn_gw4 << '/' << vpn_gw6 << ']';
|
||||
return out.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct ReasonBase : public Base {
|
||||
ReasonBase(const Type id, const std::string& reason_arg)
|
||||
: Base(id),
|
||||
reason(reason_arg)
|
||||
{
|
||||
}
|
||||
|
||||
ReasonBase(const Type id, std::string&& reason_arg)
|
||||
: Base(id),
|
||||
reason(std::move(reason_arg))
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string render() const
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
struct AuthFailed : public ReasonBase
|
||||
{
|
||||
AuthFailed(std::string reason) : ReasonBase(AUTH_FAILED, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct CertVerifyFail : public ReasonBase
|
||||
{
|
||||
CertVerifyFail(std::string reason) : ReasonBase(CERT_VERIFY_FAIL, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct ClientHalt : public ReasonBase
|
||||
{
|
||||
ClientHalt(std::string reason) : ReasonBase(CLIENT_HALT, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct ClientRestart : public ReasonBase
|
||||
{
|
||||
ClientRestart(std::string reason) : ReasonBase(CLIENT_RESTART, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct DynamicChallenge : public ReasonBase
|
||||
{
|
||||
DynamicChallenge(std::string reason) : ReasonBase(DYNAMIC_CHALLENGE, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct Pause : public ReasonBase
|
||||
{
|
||||
Pause(std::string reason) : ReasonBase(PAUSE, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct ProxyError : public ReasonBase
|
||||
{
|
||||
ProxyError(std::string reason) : ReasonBase(PROXY_ERROR, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct ProxyNeedCreds : public ReasonBase
|
||||
{
|
||||
ProxyNeedCreds(std::string reason) : ReasonBase(PROXY_NEED_CREDS, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct TransportError : public ReasonBase
|
||||
{
|
||||
TransportError(std::string reason) : ReasonBase(TRANSPORT_ERROR, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct TunSetupFailed : public ReasonBase
|
||||
{
|
||||
TunSetupFailed(std::string reason) : ReasonBase(TUN_SETUP_FAILED, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct TunIfaceCreate : public ReasonBase
|
||||
{
|
||||
TunIfaceCreate(std::string reason) : ReasonBase(TUN_IFACE_CREATE, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct TunIfaceDisabled : public ReasonBase
|
||||
{
|
||||
TunIfaceDisabled(std::string reason) : ReasonBase(TUN_IFACE_DISABLED, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct TunError : public ReasonBase
|
||||
{
|
||||
TunError(std::string reason) : ReasonBase(TUN_ERROR, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct EpkiError : public ReasonBase
|
||||
{
|
||||
EpkiError(std::string reason) : ReasonBase(EPKI_ERROR, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct EpkiInvalidAlias : public ReasonBase
|
||||
{
|
||||
EpkiInvalidAlias(std::string reason) : ReasonBase(EPKI_INVALID_ALIAS, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct Echo : public ReasonBase
|
||||
{
|
||||
Echo(std::string value) : ReasonBase(ECHO_OPT, std::move(value)) {}
|
||||
};
|
||||
|
||||
struct Info : public ReasonBase
|
||||
{
|
||||
Info(std::string value) : ReasonBase(INFO, std::move(value)) {}
|
||||
};
|
||||
|
||||
class Queue : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Queue> Ptr;
|
||||
|
||||
virtual void add_event(Base::Ptr event) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OPENVPN_CLIENT_CLIEVENT_H
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_CLIENT_CLIHALT_H
|
||||
#define OPENVPN_CLIENT_CLIHALT_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
// Process halt/restart messages from server:
|
||||
// HALT,<client_reason> -> disconnect
|
||||
// RESTART,<client_reason> -> restart with reason, don't preserve session ID
|
||||
// RESTART,[P]:<client_reason> -> restart with reason, do preserve session ID
|
||||
|
||||
namespace openvpn {
|
||||
class ClientHalt
|
||||
{
|
||||
typedef std::vector<std::string> StringList;
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(client_halt_error);
|
||||
|
||||
ClientHalt(const std::string& msg, const bool unicode_filter)
|
||||
{
|
||||
// get operator (halt or restart)
|
||||
StringList sl;
|
||||
parse_msg(sl, msg);
|
||||
if (is_halt(sl))
|
||||
;
|
||||
else if (is_restart(sl))
|
||||
restart_ = true;
|
||||
else
|
||||
throw client_halt_error();
|
||||
|
||||
// get flags and reason
|
||||
if (sl.size() >= 2)
|
||||
{
|
||||
size_t reason_pos = 0;
|
||||
if (restart_ && string::starts_with(sl[1], "[P]:"))
|
||||
{
|
||||
psid_ = true;
|
||||
reason_pos = 4;
|
||||
}
|
||||
reason_ = sl[1].substr(reason_pos);
|
||||
if (unicode_filter)
|
||||
reason_ = Unicode::utf8_printable(reason_, 256);
|
||||
}
|
||||
}
|
||||
|
||||
static bool match(const std::string& msg)
|
||||
{
|
||||
StringList sl;
|
||||
parse_msg(sl, msg);
|
||||
return is_halt(sl) || is_restart(sl);
|
||||
}
|
||||
|
||||
// returns true for restart, false for halt
|
||||
bool restart() const { return restart_; }
|
||||
|
||||
// returns true if session ID should be preserved
|
||||
bool psid() const { return psid_; }
|
||||
|
||||
// returns user-visible reason string
|
||||
const std::string& reason() const { return reason_; }
|
||||
|
||||
std::string render() const {
|
||||
std::ostringstream os;
|
||||
os << (restart_ ? "RESTART" : "HALT") << " psid=" << psid_ << " reason='" << reason_ << '\'';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
static void parse_msg(StringList& sl, const std::string& msg)
|
||||
{
|
||||
sl.reserve(2);
|
||||
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, msg, ',', 0, 1);
|
||||
}
|
||||
|
||||
static bool is_halt(const StringList& sl)
|
||||
{
|
||||
return sl.size() >= 1 && sl[0] == "HALT";
|
||||
}
|
||||
|
||||
static bool is_restart(const StringList& sl)
|
||||
{
|
||||
return sl.size() >= 1 && sl[0] == "RESTART";
|
||||
}
|
||||
|
||||
bool restart_ = false;
|
||||
bool psid_ = false;
|
||||
std::string reason_;
|
||||
};
|
||||
}
|
||||
|
||||
#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-2016 OpenVPN Technologies, 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_CLIENT_CLILIFE_H
|
||||
#define OPENVPN_CLIENT_CLILIFE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// Base class for managing connection lifecycle notifications,
|
||||
// such as sleep, wakeup, network-unavailable, network-available.
|
||||
class ClientLifeCycle : public RC<thread_unsafe_refcount> {
|
||||
public:
|
||||
struct NotifyCallback {
|
||||
virtual void cln_stop() = 0;
|
||||
virtual void cln_pause(const std::string& reason) = 0;
|
||||
virtual void cln_resume() = 0;
|
||||
virtual void cln_reconnect(int seconds) = 0;
|
||||
};
|
||||
|
||||
typedef RCPtr<ClientLifeCycle> Ptr;
|
||||
|
||||
virtual bool network_available() = 0;
|
||||
|
||||
virtual void start(NotifyCallback*) = 0;
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,768 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// These classes encapsulate the basic setup of the various objects needed to
|
||||
// create an OpenVPN client session. The basic idea here is to look at both
|
||||
// compile time settings (i.e. crypto/SSL/random libraries), and run-time
|
||||
// (such as transport layer using UDP, TCP, or HTTP-proxy), and
|
||||
// build the actual objects that will be used to construct a client session.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLIOPT_H
|
||||
#define OPENVPN_CLIENT_CLIOPT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/error/excode.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/frame/frame_init.hpp>
|
||||
#include <openvpn/pki/epkibase.hpp>
|
||||
#include <openvpn/crypto/cryptodcsel.hpp>
|
||||
#include <openvpn/ssl/mssparms.hpp>
|
||||
#include <openvpn/tun/tunmtu.hpp>
|
||||
#include <openvpn/tun/ipv6_setting.hpp>
|
||||
#include <openvpn/netconf/hwaddr.hpp>
|
||||
|
||||
#include <openvpn/transport/socket_protect.hpp>
|
||||
#include <openvpn/transport/reconnect_notify.hpp>
|
||||
#include <openvpn/transport/client/udpcli.hpp>
|
||||
#include <openvpn/transport/client/tcpcli.hpp>
|
||||
#include <openvpn/transport/client/httpcli.hpp>
|
||||
#include <openvpn/transport/altproxy.hpp>
|
||||
#include <openvpn/transport/dco.hpp>
|
||||
#include <openvpn/client/cliproto.hpp>
|
||||
#include <openvpn/client/cliopthelper.hpp>
|
||||
#include <openvpn/client/optfilt.hpp>
|
||||
#include <openvpn/client/clilife.hpp>
|
||||
|
||||
#include <openvpn/ssl/sslchoose.hpp>
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
#include <openvpn/transport/gremlin.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_ANDROID)
|
||||
#include <openvpn/client/cliemuexr.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_CUSTOM_TUN_FACTORY)
|
||||
#include <openvpn/tun/ios/client/tuncli.hpp>
|
||||
#elif defined(USE_TUN_BUILDER)
|
||||
#include <openvpn/tun/builder/client.hpp>
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
#include <openvpn/tun/linux/client/tuncli.hpp>
|
||||
#ifdef OPENVPN_COMMAND_AGENT
|
||||
#include <openvpn/client/unix/cmdagent.hpp>
|
||||
#endif
|
||||
#elif defined(OPENVPN_PLATFORM_MAC) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
#include <openvpn/tun/mac/client/tuncli.hpp>
|
||||
#include <openvpn/apple/maclife.hpp>
|
||||
#ifdef OPENVPN_COMMAND_AGENT
|
||||
#include <openvpn/client/unix/cmdagent.hpp>
|
||||
#endif
|
||||
#elif defined(OPENVPN_PLATFORM_WIN) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
#include <openvpn/tun/win/client/tuncli.hpp>
|
||||
#ifdef OPENVPN_COMMAND_AGENT
|
||||
#include <openvpn/client/win/cmdagent.hpp>
|
||||
#endif
|
||||
#else
|
||||
#include <openvpn/tun/client/tunnull.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef PRIVATE_TUNNEL_PROXY
|
||||
#include <openvpn/pt/ptproxy.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_DCO)
|
||||
#include <openvpn/dco/dcocli.hpp>
|
||||
#endif
|
||||
|
||||
#ifndef OPENVPN_UNUSED_OPTIONS
|
||||
#define OPENVPN_UNUSED_OPTIONS "UNUSED OPTIONS"
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ClientOptions : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientOptions> Ptr;
|
||||
|
||||
typedef ClientProto::Session Client;
|
||||
|
||||
struct Config
|
||||
{
|
||||
std::string gui_version;
|
||||
std::string server_override;
|
||||
Protocol proto_override;
|
||||
IPv6Setting ipv6;
|
||||
int conn_timeout = 0;
|
||||
SessionStats::Ptr cli_stats;
|
||||
ClientEvent::Queue::Ptr cli_events;
|
||||
ProtoContextOptions::Ptr proto_context_options;
|
||||
HTTPProxyTransport::Options::Ptr http_proxy_options;
|
||||
bool alt_proxy = false;
|
||||
bool dco = false;
|
||||
bool echo = false;
|
||||
bool info = false;
|
||||
bool tun_persist = false;
|
||||
bool google_dns_fallback = false;
|
||||
std::string private_key_password;
|
||||
bool disable_client_cert = false;
|
||||
int ssl_debug_level = 0;
|
||||
int default_key_direction = -1;
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool autologin_sessions = false;
|
||||
std::string tls_version_min_override;
|
||||
PeerInfo::Set::Ptr extra_peer_info;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
Gremlin::Config::Ptr gremlin_config;
|
||||
#endif
|
||||
Stop* stop = nullptr;
|
||||
|
||||
// callbacks -- must remain in scope for lifetime of ClientOptions object
|
||||
ExternalPKIBase* external_pki = nullptr;
|
||||
SocketProtect* socket_protect = nullptr;
|
||||
ReconnectNotify* reconnect_notify = nullptr;
|
||||
RemoteList::RemoteOverride* remote_override = nullptr;
|
||||
|
||||
#if defined(USE_TUN_BUILDER)
|
||||
TunBuilderBase* builder = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
ClientOptions(const OptionList& opt, // only needs to remain in scope for duration of constructor call
|
||||
const Config& config)
|
||||
: server_addr_float(false),
|
||||
socket_protect(config.socket_protect),
|
||||
reconnect_notify(config.reconnect_notify),
|
||||
cli_stats(config.cli_stats),
|
||||
cli_events(config.cli_events),
|
||||
server_poll_timeout_(10),
|
||||
server_override(config.server_override),
|
||||
proto_override(config.proto_override),
|
||||
conn_timeout_(config.conn_timeout),
|
||||
tcp_queue_limit(64),
|
||||
proto_context_options(config.proto_context_options),
|
||||
http_proxy_options(config.http_proxy_options),
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
gremlin_config(config.gremlin_config),
|
||||
#endif
|
||||
echo(config.echo),
|
||||
info(config.info),
|
||||
autologin(false),
|
||||
autologin_sessions(false),
|
||||
creds_locked(false)
|
||||
{
|
||||
// parse general client options
|
||||
const ParseClientConfig pcc(opt);
|
||||
|
||||
// creds
|
||||
userlocked_username = pcc.userlockedUsername();
|
||||
autologin = pcc.autologin();
|
||||
autologin_sessions = (autologin && config.autologin_sessions);
|
||||
|
||||
// digest factory
|
||||
DigestFactory::Ptr digest_factory(new CryptoDigestFactory<SSLLib::CryptoAPI>());
|
||||
|
||||
// initialize RNG/PRNG
|
||||
rng.reset(new SSLLib::RandomAPI(false));
|
||||
prng.reset(new SSLLib::RandomAPI(true));
|
||||
|
||||
#if defined(ENABLE_DCO) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_CUSTOM_TUN_FACTORY)
|
||||
if (config.dco)
|
||||
dco = DCOTransport::new_controller();
|
||||
#else
|
||||
if (config.dco)
|
||||
throw option_error("DCO not enabled in this build");
|
||||
#endif
|
||||
|
||||
// frame
|
||||
const unsigned int tun_mtu = parse_tun_mtu(opt, 0); // get tun-mtu parameter from config
|
||||
const MSSCtrlParms mc(opt);
|
||||
frame = frame_init(true, tun_mtu, mc.mssfix_ctrl, true);
|
||||
|
||||
// TCP queue limit
|
||||
tcp_queue_limit = opt.get_num<decltype(tcp_queue_limit)>("tcp-queue-limit", 1, tcp_queue_limit, 1, 65536);
|
||||
|
||||
// route-nopull
|
||||
pushed_options_filter.reset(new PushedOptionsFilter(opt.exists("route-nopull")));
|
||||
|
||||
// client SSL config
|
||||
SSLLib::SSLAPI::Config::Ptr cc(new SSLLib::SSLAPI::Config());
|
||||
cc->set_external_pki_callback(config.external_pki);
|
||||
cc->set_frame(frame);
|
||||
cc->set_flags(SSLConst::LOG_VERIFY_STATUS);
|
||||
cc->set_debug_level(config.ssl_debug_level);
|
||||
cc->set_rng(rng);
|
||||
cc->set_local_cert_enabled(pcc.clientCertEnabled() && !config.disable_client_cert);
|
||||
cc->set_private_key_password(config.private_key_password);
|
||||
cc->set_force_aes_cbc_ciphersuites(config.force_aes_cbc_ciphersuites);
|
||||
cc->load(opt, SSLConfigAPI::LF_PARSE_MODE);
|
||||
cc->set_tls_version_min_override(config.tls_version_min_override);
|
||||
if (!cc->get_mode().is_client())
|
||||
throw option_error("only client configuration supported");
|
||||
|
||||
// client ProtoContext config
|
||||
cp.reset(new Client::ProtoConfig());
|
||||
cp->dc.set_factory(new CryptoDCSelect<SSLLib::CryptoAPI>(frame, cli_stats, prng));
|
||||
cp->dc_deferred = true; // defer data channel setup until after options pull
|
||||
cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<SSLLib::CryptoAPI>());
|
||||
cp->tlsprf_factory.reset(new CryptoTLSPRFFactory<SSLLib::CryptoAPI>());
|
||||
cp->ssl_factory = cc->new_factory();
|
||||
cp->load(opt, *proto_context_options, config.default_key_direction, false);
|
||||
cp->set_xmit_creds(!autologin || pcc.hasEmbeddedPassword() || autologin_sessions);
|
||||
cp->gui_version = config.gui_version;
|
||||
cp->force_aes_cbc_ciphersuites = config.force_aes_cbc_ciphersuites; // also used to disable proto V2
|
||||
cp->extra_peer_info = build_peer_info(config, pcc, autologin_sessions);
|
||||
cp->frame = frame;
|
||||
cp->now = &now_;
|
||||
cp->rng = rng;
|
||||
cp->prng = prng;
|
||||
|
||||
#ifdef PRIVATE_TUNNEL_PROXY
|
||||
if (config.alt_proxy && !dco)
|
||||
alt_proxy = PTProxy::new_proxy(opt, rng);
|
||||
#endif
|
||||
|
||||
// If HTTP proxy parameters are not supplied by API, try to get them from config
|
||||
if (!http_proxy_options)
|
||||
http_proxy_options = HTTPProxyTransport::Options::parse(opt);
|
||||
|
||||
// load remote list
|
||||
if (config.remote_override)
|
||||
remote_list.reset(new RemoteList(config.remote_override));
|
||||
else
|
||||
remote_list.reset(new RemoteList(opt, "", RemoteList::WARN_UNSUPPORTED, nullptr));
|
||||
if (!remote_list->defined())
|
||||
throw option_error("no remote option specified");
|
||||
|
||||
// Set remote list prng
|
||||
remote_list->set_random(prng);
|
||||
|
||||
// If running in tun_persist mode, we need to do basic DNS caching so that
|
||||
// we can avoid emitting DNS requests while the tunnel is blocked during
|
||||
// reconnections.
|
||||
remote_list->set_enable_cache(config.tun_persist);
|
||||
|
||||
// process server override
|
||||
remote_list->set_server_override(config.server_override);
|
||||
|
||||
// process protocol override, should be called after set_enable_cache
|
||||
remote_list->handle_proto_override(config.proto_override,
|
||||
http_proxy_options || (alt_proxy && alt_proxy->requires_tcp()));
|
||||
|
||||
// process remote-random
|
||||
if (opt.exists("remote-random"))
|
||||
remote_list->randomize();
|
||||
|
||||
// get "float" option
|
||||
server_addr_float = opt.exists("float");
|
||||
|
||||
// special remote cache handling for proxies
|
||||
if (alt_proxy)
|
||||
{
|
||||
remote_list->set_enable_cache(false); // remote server addresses will be resolved by proxy
|
||||
alt_proxy->set_enable_cache(config.tun_persist);
|
||||
}
|
||||
else if (http_proxy_options)
|
||||
{
|
||||
remote_list->set_enable_cache(false); // remote server addresses will be resolved by proxy
|
||||
http_proxy_options->proxy_server_set_enable_cache(config.tun_persist);
|
||||
}
|
||||
|
||||
// secret option not supported
|
||||
if (opt.exists("secret"))
|
||||
throw option_error("sorry, static key encryption mode (non-SSL/TLS) is not supported");
|
||||
|
||||
// fragment option not supported
|
||||
if (opt.exists("fragment"))
|
||||
throw option_error("sorry, 'fragment' directive is not supported, nor is connecting to a server that uses 'fragment' directive");
|
||||
|
||||
// init transport config
|
||||
const std::string session_name = load_transport_config();
|
||||
|
||||
// initialize tun/tap
|
||||
if (dco)
|
||||
{
|
||||
DCO::TunConfig tunconf;
|
||||
tunconf.tun_prop.layer = cp->layer;
|
||||
tunconf.tun_prop.session_name = session_name;
|
||||
if (tun_mtu)
|
||||
tunconf.tun_prop.mtu = tun_mtu;
|
||||
tunconf.tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
tunconf.tun_prop.remote_list = remote_list;
|
||||
tunconf.stop = config.stop;
|
||||
tun_factory = dco->new_tun_factory(tunconf, opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(OPENVPN_CUSTOM_TUN_FACTORY)
|
||||
{
|
||||
OPENVPN_CUSTOM_TUN_FACTORY::Ptr tunconf = OPENVPN_CUSTOM_TUN_FACTORY::new_obj();
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->tun_prop.remote_list = remote_list;
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#elif defined(USE_TUN_BUILDER)
|
||||
{
|
||||
TunBuilderClient::ClientConfig::Ptr tunconf = TunBuilderClient::ClientConfig::new_obj();
|
||||
tunconf->builder = config.builder;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->tun_prop.remote_list = remote_list;
|
||||
tun_factory = tunconf;
|
||||
#if defined(OPENVPN_PLATFORM_IPHONE)
|
||||
tunconf->retain_sd = true;
|
||||
tunconf->tun_prefix = true;
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_prop.remote_bypass = true;
|
||||
#endif
|
||||
#if defined(OPENVPN_PLATFORM_ANDROID)
|
||||
// Android VPN API doesn't support excluded routes, so we must emulate them
|
||||
tunconf->eer_factory.reset(new EmulateExcludeRouteFactoryImpl(false));
|
||||
#endif
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunBuilderClient::TunPersist(true, tunconf->retain_sd, config.builder));
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunLinux::ClientConfig::Ptr tunconf = TunLinux::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
tunconf->tun_prop.remote_list = remote_list;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->load(opt);
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#elif defined(OPENVPN_PLATFORM_MAC) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunMac::ClientConfig::Ptr tunconf = TunMac::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->stop = config.stop;
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunMac::TunPersist(true, false, nullptr));
|
||||
client_lifecycle.reset(new MacLifeCycle);
|
||||
#ifdef OPENVPN_COMMAND_AGENT
|
||||
tunconf->tun_setup_factory = UnixCommandAgent::new_agent(opt);
|
||||
#endif
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#elif defined(OPENVPN_PLATFORM_WIN) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunWin::ClientConfig::Ptr tunconf = TunWin::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->stop = config.stop;
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr));
|
||||
#ifdef OPENVPN_COMMAND_AGENT
|
||||
tunconf->tun_setup_factory = WinCommandAgent::new_agent(opt);
|
||||
#endif
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#else
|
||||
{
|
||||
TunNull::ClientConfig::Ptr tunconf = TunNull::ClientConfig::new_obj();
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// verify that tun implementation can handle OSI layer declared by config
|
||||
if (cp->layer == Layer(Layer::OSI_LAYER_2) && !tun_factory->layer_2_supported())
|
||||
throw ErrorCode(Error::TAP_NOT_SUPPORTED, true, "OSI layer 2 tunnels are not currently supported");
|
||||
|
||||
// server-poll-timeout
|
||||
{
|
||||
const Option *o = opt.get_ptr("server-poll-timeout");
|
||||
if (o)
|
||||
server_poll_timeout_ = parse_number_throw<unsigned int>(o->get(1, 16), "server-poll-timeout");
|
||||
}
|
||||
|
||||
// create default creds object in case submit_creds is not called,
|
||||
// and populate it with embedded creds, if available
|
||||
{
|
||||
ClientCreds::Ptr cc = new ClientCreds();
|
||||
if (pcc.hasEmbeddedPassword())
|
||||
{
|
||||
cc->set_username(userlocked_username);
|
||||
cc->set_password(pcc.embeddedPassword());
|
||||
cc->enable_password_cache(true);
|
||||
cc->set_replace_password_with_session_id(true);
|
||||
submit_creds(cc);
|
||||
creds_locked = true;
|
||||
}
|
||||
else if (autologin_sessions)
|
||||
{
|
||||
// autologin sessions require replace_password_with_session_id
|
||||
cc->set_replace_password_with_session_id(true);
|
||||
submit_creds(cc);
|
||||
creds_locked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
submit_creds(cc);
|
||||
}
|
||||
}
|
||||
|
||||
// configure push_base, a set of base options that will be combined with
|
||||
// options pushed by server.
|
||||
{
|
||||
push_base.reset(new PushOptionsBase());
|
||||
|
||||
// base options where multiple options of the same type can aggregate
|
||||
push_base->multi.extend(opt, "route");
|
||||
push_base->multi.extend(opt, "route-ipv6");
|
||||
push_base->multi.extend(opt, "redirect-gateway");
|
||||
push_base->multi.extend(opt, "redirect-private");
|
||||
push_base->multi.extend(opt, "dhcp-option");
|
||||
|
||||
// base options where only a single instance of each option makes sense
|
||||
push_base->singleton.extend(opt, "redirect-dns");
|
||||
push_base->singleton.extend(opt, "inactive");
|
||||
push_base->singleton.extend(opt, "route-metric");
|
||||
|
||||
// IPv6
|
||||
{
|
||||
const unsigned int n = push_base->singleton.extend(opt, "block-ipv6");
|
||||
if (!n && config.ipv6() == IPv6Setting::NO)
|
||||
push_base->singleton.emplace_back("block-ipv6");
|
||||
}
|
||||
}
|
||||
|
||||
// show unused options
|
||||
if (opt.n_unused())
|
||||
OPENVPN_LOG(OPENVPN_UNUSED_OPTIONS << std::endl << opt.render(Option::RENDER_TRUNC_64|Option::RENDER_NUMBER|Option::RENDER_BRACKET|Option::RENDER_UNUSED));
|
||||
}
|
||||
|
||||
static PeerInfo::Set::Ptr build_peer_info(const Config& config, const ParseClientConfig& pcc, const bool autologin_sessions)
|
||||
{
|
||||
PeerInfo::Set::Ptr pi(new PeerInfo::Set);
|
||||
|
||||
// IPv6
|
||||
if (config.ipv6() == IPv6Setting::NO)
|
||||
pi->emplace_back("IV_IPv6", "0");
|
||||
else if (config.ipv6() == IPv6Setting::YES)
|
||||
pi->emplace_back("IV_IPv6", "1");
|
||||
|
||||
// autologin sessions
|
||||
if (autologin_sessions)
|
||||
pi->emplace_back("IV_AUTO_SESS", "1");
|
||||
|
||||
// Config::peerInfo
|
||||
pi->append_foreign_set_ptr(config.extra_peer_info.get());
|
||||
|
||||
// setenv UV_ options
|
||||
pi->append_foreign_set_ptr(pcc.peerInfoUV());
|
||||
|
||||
// MAC address
|
||||
if (pcc.pushPeerInfo())
|
||||
{
|
||||
std::string hwaddr = get_hwaddr();
|
||||
if (!hwaddr.empty())
|
||||
pi->emplace_back("IV_HWADDR", hwaddr);
|
||||
}
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
void next()
|
||||
{
|
||||
bool omit_next = false;
|
||||
|
||||
if (alt_proxy)
|
||||
omit_next = alt_proxy->next();
|
||||
if (!omit_next)
|
||||
remote_list->next();
|
||||
load_transport_config();
|
||||
}
|
||||
|
||||
void remote_reset_cache_item()
|
||||
{
|
||||
remote_list->reset_cache_item();
|
||||
}
|
||||
|
||||
bool pause_on_connection_timeout()
|
||||
{
|
||||
if (reconnect_notify)
|
||||
return reconnect_notify->pause_on_connection_timeout();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
Client::Config::Ptr client_config()
|
||||
{
|
||||
Client::Config::Ptr cli_config = new Client::Config;
|
||||
|
||||
// Copy ProtoConfig so that modifications due to server push will
|
||||
// not persist across client instantiations.
|
||||
cli_config->proto_context_config.reset(new Client::ProtoConfig(*cp));
|
||||
|
||||
cli_config->proto_context_options = proto_context_options;
|
||||
cli_config->push_base = push_base;
|
||||
cli_config->transport_factory = transport_factory;
|
||||
cli_config->tun_factory = tun_factory;
|
||||
cli_config->cli_stats = cli_stats;
|
||||
cli_config->cli_events = cli_events;
|
||||
cli_config->creds = creds;
|
||||
cli_config->pushed_options_filter = pushed_options_filter;
|
||||
cli_config->tcp_queue_limit = tcp_queue_limit;
|
||||
cli_config->echo = echo;
|
||||
cli_config->info = info;
|
||||
cli_config->autologin_sessions = autologin_sessions;
|
||||
return cli_config;
|
||||
}
|
||||
|
||||
bool need_creds() const
|
||||
{
|
||||
return !autologin;
|
||||
}
|
||||
|
||||
void submit_creds(const ClientCreds::Ptr& creds_arg)
|
||||
{
|
||||
if (creds_arg && !creds_locked)
|
||||
{
|
||||
// if no username is defined in creds and userlocked_username is defined
|
||||
// in profile, set the creds username to be the userlocked_username
|
||||
if (!creds_arg->username_defined() && !userlocked_username.empty())
|
||||
creds_arg->set_username(userlocked_username);
|
||||
creds = creds_arg;
|
||||
}
|
||||
}
|
||||
|
||||
bool server_poll_timeout_enabled() const
|
||||
{
|
||||
return !http_proxy_options;
|
||||
}
|
||||
|
||||
Time::Duration server_poll_timeout() const
|
||||
{
|
||||
return Time::Duration::seconds(server_poll_timeout_);
|
||||
}
|
||||
|
||||
SessionStats& stats() {
|
||||
return *cli_stats;
|
||||
}
|
||||
const SessionStats::Ptr& stats_ptr() const {
|
||||
return cli_stats;
|
||||
}
|
||||
ClientEvent::Queue& events() {
|
||||
return *cli_events;
|
||||
}
|
||||
ClientLifeCycle* lifecycle() {
|
||||
return client_lifecycle.get();
|
||||
}
|
||||
|
||||
int conn_timeout() const {
|
||||
return conn_timeout_;
|
||||
}
|
||||
|
||||
RemoteList::Ptr remote_list_precache() const
|
||||
{
|
||||
RemoteList::Ptr r;
|
||||
if (alt_proxy)
|
||||
{
|
||||
alt_proxy->precache(r);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
if (http_proxy_options)
|
||||
{
|
||||
http_proxy_options->proxy_server_precache(r);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
return remote_list;
|
||||
}
|
||||
|
||||
void update_now()
|
||||
{
|
||||
now_.update();
|
||||
}
|
||||
|
||||
void finalize(const bool disconnected)
|
||||
{
|
||||
if (tun_factory)
|
||||
tun_factory->finalize(disconnected);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string load_transport_config()
|
||||
{
|
||||
// get current transport protocol
|
||||
const Protocol& transport_protocol = remote_list->current_transport_protocol();
|
||||
|
||||
// set transport protocol in Client::ProtoConfig
|
||||
cp->set_protocol(transport_protocol);
|
||||
|
||||
// If we are connecting over a proxy, and TCP protocol is required, but current
|
||||
// transport protocol is NOT TCP, we will throw an internal error because this
|
||||
// should have been caught earlier in RemoteList::handle_proto_override.
|
||||
|
||||
// construct transport object
|
||||
if (dco)
|
||||
{
|
||||
DCO::TransportConfig transconf;
|
||||
transconf.protocol = transport_protocol;
|
||||
transconf.remote_list = remote_list;
|
||||
transconf.frame = frame;
|
||||
transconf.stats = cli_stats;
|
||||
transconf.server_addr_float = server_addr_float;
|
||||
transport_factory = dco->new_transport_factory(transconf);
|
||||
}
|
||||
else if (alt_proxy)
|
||||
{
|
||||
if (alt_proxy->requires_tcp() && !transport_protocol.is_tcp())
|
||||
throw option_error("internal error: no TCP server entries for " + alt_proxy->name() + " transport");
|
||||
AltProxy::Config conf;
|
||||
conf.remote_list = remote_list;
|
||||
conf.frame = frame;
|
||||
conf.stats = cli_stats;
|
||||
conf.digest_factory.reset(new CryptoDigestFactory<SSLLib::CryptoAPI>());
|
||||
conf.socket_protect = socket_protect;
|
||||
conf.rng = rng;
|
||||
transport_factory = alt_proxy->new_transport_client_factory(conf);
|
||||
}
|
||||
else if (http_proxy_options)
|
||||
{
|
||||
if (!transport_protocol.is_tcp())
|
||||
throw option_error("internal error: no TCP server entries for HTTP proxy transport");
|
||||
|
||||
// HTTP Proxy transport
|
||||
HTTPProxyTransport::ClientConfig::Ptr httpconf = HTTPProxyTransport::ClientConfig::new_obj();
|
||||
httpconf->remote_list = remote_list;
|
||||
httpconf->frame = frame;
|
||||
httpconf->stats = cli_stats;
|
||||
httpconf->digest_factory.reset(new CryptoDigestFactory<SSLLib::CryptoAPI>());
|
||||
httpconf->socket_protect = socket_protect;
|
||||
httpconf->http_proxy_options = http_proxy_options;
|
||||
httpconf->rng = rng;
|
||||
#ifdef PRIVATE_TUNNEL_PROXY
|
||||
httpconf->skip_html = true;
|
||||
#endif
|
||||
transport_factory = httpconf;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transport_protocol.is_udp())
|
||||
{
|
||||
// UDP transport
|
||||
UDPTransport::ClientConfig::Ptr udpconf = UDPTransport::ClientConfig::new_obj();
|
||||
udpconf->remote_list = remote_list;
|
||||
udpconf->frame = frame;
|
||||
udpconf->stats = cli_stats;
|
||||
udpconf->socket_protect = socket_protect;
|
||||
udpconf->server_addr_float = server_addr_float;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
udpconf->gremlin_config = gremlin_config;
|
||||
#endif
|
||||
transport_factory = udpconf;
|
||||
}
|
||||
else if (transport_protocol.is_tcp())
|
||||
{
|
||||
// TCP transport
|
||||
TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj();
|
||||
tcpconf->remote_list = remote_list;
|
||||
tcpconf->frame = frame;
|
||||
tcpconf->stats = cli_stats;
|
||||
tcpconf->socket_protect = socket_protect;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
tcpconf->gremlin_config = gremlin_config;
|
||||
#endif
|
||||
transport_factory = tcpconf;
|
||||
}
|
||||
else
|
||||
throw option_error("internal error: unknown transport protocol");
|
||||
}
|
||||
return remote_list->current_server_host();
|
||||
}
|
||||
|
||||
Time now_; // current time
|
||||
RandomAPI::Ptr rng;
|
||||
RandomAPI::Ptr prng;
|
||||
Frame::Ptr frame;
|
||||
SSLLib::SSLAPI::Config cc;
|
||||
Client::ProtoConfig::Ptr cp;
|
||||
RemoteList::Ptr remote_list;
|
||||
bool server_addr_float;
|
||||
TransportClientFactory::Ptr transport_factory;
|
||||
TunClientFactory::Ptr tun_factory;
|
||||
SocketProtect* socket_protect;
|
||||
ReconnectNotify* reconnect_notify;
|
||||
SessionStats::Ptr cli_stats;
|
||||
ClientEvent::Queue::Ptr cli_events;
|
||||
ClientCreds::Ptr creds;
|
||||
unsigned int server_poll_timeout_;
|
||||
std::string server_override;
|
||||
Protocol proto_override;
|
||||
int conn_timeout_;
|
||||
unsigned int tcp_queue_limit;
|
||||
ProtoContextOptions::Ptr proto_context_options;
|
||||
HTTPProxyTransport::Options::Ptr http_proxy_options;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
Gremlin::Config::Ptr gremlin_config;
|
||||
#endif
|
||||
std::string userlocked_username;
|
||||
bool echo;
|
||||
bool info;
|
||||
bool autologin;
|
||||
bool autologin_sessions;
|
||||
bool creds_locked;
|
||||
PushOptionsBase::Ptr push_base;
|
||||
OptionList::FilterBase::Ptr pushed_options_filter;
|
||||
ClientLifeCycle::Ptr client_lifecycle;
|
||||
AltProxy::Ptr alt_proxy;
|
||||
DCO::Ptr dco;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,504 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// A preliminary parser for OpenVPN client configuration files.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_CLIOPTHELPER_H
|
||||
#define OPENVPN_CLIENT_CLIOPTHELPER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/userpass.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/client/cliconstants.hpp>
|
||||
#include <openvpn/ssl/peerinfo.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class ParseClientConfig {
|
||||
public:
|
||||
struct ServerEntry {
|
||||
std::string server;
|
||||
std::string friendlyName;
|
||||
};
|
||||
|
||||
struct ServerList : public std::vector<ServerEntry>
|
||||
{
|
||||
};
|
||||
|
||||
struct RemoteItem {
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string proto;
|
||||
};
|
||||
|
||||
ParseClientConfig(const OptionList& options)
|
||||
{
|
||||
try {
|
||||
// reset POD types
|
||||
reset_pod();
|
||||
|
||||
// limits
|
||||
const size_t max_server_list_size = 64;
|
||||
|
||||
// setenv UV_x
|
||||
PeerInfo::Set::Ptr peer_info_uv(new PeerInfo::Set);
|
||||
|
||||
// process setenv directives
|
||||
{
|
||||
const OptionList::IndexList* se = options.get_index_ptr("setenv");
|
||||
if (se)
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = se->begin(); i != se->end(); ++i)
|
||||
{
|
||||
const Option& o = options[*i];
|
||||
o.touch();
|
||||
const std::string arg1 = o.get_optional(1, 256);
|
||||
|
||||
// server-locked profiles not supported
|
||||
if (arg1 == "GENERIC_CONFIG")
|
||||
{
|
||||
error_ = true;
|
||||
message_ = "SERVER_LOCKED_UNSUPPORTED: server locked profiles are currently unsupported";
|
||||
return;
|
||||
}
|
||||
else if (arg1 == "ALLOW_PASSWORD_SAVE")
|
||||
allowPasswordSave_ = parse_bool(o, "setenv ALLOW_PASSWORD_SAVE", 2);
|
||||
else if (arg1 == "CLIENT_CERT")
|
||||
clientCertEnabled_ = parse_bool(o, "setenv CLIENT_CERT", 2);
|
||||
else if (arg1 == "USERNAME")
|
||||
userlockedUsername_ = o.get(2, 256);
|
||||
else if (arg1 == "FRIENDLY_NAME")
|
||||
friendlyName_ = o.get(2, 256);
|
||||
else if (arg1 == "SERVER")
|
||||
{
|
||||
const std::string& serv = o.get(2, 256);
|
||||
std::vector<std::string> slist = Split::by_char<std::vector<std::string>, NullLex, Split::NullLimit>(serv, '/', 0, 1);
|
||||
ServerEntry se;
|
||||
if (slist.size() == 1)
|
||||
{
|
||||
se.server = slist[0];
|
||||
se.friendlyName = slist[0];
|
||||
}
|
||||
else if (slist.size() == 2)
|
||||
{
|
||||
se.server = slist[0];
|
||||
se.friendlyName = slist[1];
|
||||
}
|
||||
if (!se.server.empty() && !se.friendlyName.empty() && serverList_.size() < max_server_list_size)
|
||||
serverList_.push_back(se);
|
||||
}
|
||||
else if (arg1 == "PUSH_PEER_INFO")
|
||||
pushPeerInfo_ = true;
|
||||
else if (string::starts_with(arg1, "UV_") && arg1.length() >= 4 && string::is_word(arg1))
|
||||
{
|
||||
const std::string value = o.get_optional(2, 256);
|
||||
if (string::is_printable(value))
|
||||
peer_info_uv->emplace_back(arg1, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative to "setenv CLIENT_CERT 0". Note that as of OpenVPN 2.3, this option
|
||||
// is only supported server-side, so this extends its meaning into the client realm.
|
||||
if (options.exists("client-cert-not-required"))
|
||||
clientCertEnabled_ = false;
|
||||
|
||||
// userlocked username
|
||||
{
|
||||
const Option* o = options.get_ptr("USERNAME");
|
||||
if (o)
|
||||
userlockedUsername_ = o->get(1, 256);
|
||||
}
|
||||
|
||||
// userlocked username/password via <auth-user-pass>
|
||||
std::vector<std::string> user_pass;
|
||||
const bool auth_user_pass = parse_auth_user_pass(options, &user_pass);
|
||||
if (auth_user_pass && user_pass.size() >= 1)
|
||||
{
|
||||
userlockedUsername_ = user_pass[0];
|
||||
if (user_pass.size() >= 2)
|
||||
{
|
||||
hasEmbeddedPassword_ = true;
|
||||
embeddedPassword_ = user_pass[1];
|
||||
}
|
||||
}
|
||||
|
||||
// External PKI
|
||||
externalPki_ = (clientCertEnabled_ && is_external_pki(options));
|
||||
|
||||
// allow password save
|
||||
{
|
||||
const Option* o = options.get_ptr("allow-password-save");
|
||||
if (o)
|
||||
allowPasswordSave_ = parse_bool(*o, "allow-password-save", 1);
|
||||
}
|
||||
|
||||
// autologin
|
||||
{
|
||||
autologin_ = is_autologin(options, auth_user_pass, user_pass);
|
||||
if (autologin_)
|
||||
allowPasswordSave_ = false; // saving passwords is incompatible with autologin
|
||||
}
|
||||
|
||||
// static challenge
|
||||
{
|
||||
const Option* o = options.get_ptr("static-challenge");
|
||||
if (o)
|
||||
{
|
||||
staticChallenge_ = o->get(1, 256);
|
||||
if (o->get_optional(2, 16) == "1")
|
||||
staticChallengeEcho_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// validate remote list
|
||||
RemoteList rl(options, "", 0, nullptr);
|
||||
{
|
||||
const RemoteList::Item* ri = rl.first_item();
|
||||
if (ri)
|
||||
{
|
||||
firstRemoteListItem_.host = ri->server_host;
|
||||
firstRemoteListItem_.port = ri->server_port;
|
||||
if (ri->transport_protocol.is_udp())
|
||||
firstRemoteListItem_.proto = "udp";
|
||||
else if (ri->transport_protocol.is_tcp())
|
||||
firstRemoteListItem_.proto = "tcp-client";
|
||||
}
|
||||
}
|
||||
|
||||
// determine if private key is encrypted
|
||||
if (!externalPki_)
|
||||
{
|
||||
const Option* o = options.get_ptr("key");
|
||||
if (o)
|
||||
{
|
||||
const std::string& key_txt = o->get(1, Option::MULTILINE);
|
||||
privateKeyPasswordRequired_ = (
|
||||
key_txt.find("-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\n") != std::string::npos
|
||||
|| key_txt.find("-----BEGIN ENCRYPTED PRIVATE KEY-----") != std::string::npos
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// profile name
|
||||
{
|
||||
const Option* o = options.get_ptr("PROFILE");
|
||||
if (o)
|
||||
{
|
||||
// take PROFILE substring up to '/'
|
||||
const std::string& pn = o->get(1, 256);
|
||||
const size_t slashpos = pn.find('/');
|
||||
if (slashpos != std::string::npos)
|
||||
profileName_ = pn.substr(0, slashpos);
|
||||
else
|
||||
profileName_ = pn;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rl.defined())
|
||||
profileName_ = rl.first_server_host();
|
||||
}
|
||||
}
|
||||
|
||||
// friendly name
|
||||
{
|
||||
const Option* o = options.get_ptr("FRIENDLY_NAME");
|
||||
if (o)
|
||||
friendlyName_ = o->get(1, 256);
|
||||
}
|
||||
|
||||
// server list
|
||||
{
|
||||
const Option* o = options.get_ptr("HOST_LIST");
|
||||
if (o)
|
||||
{
|
||||
SplitLines in(o->get(1, 4096 | Option::MULTILINE), 0);
|
||||
while (in(true))
|
||||
{
|
||||
ServerEntry se;
|
||||
se.server = in.line_ref();
|
||||
se.friendlyName = se.server;
|
||||
Option::validate_string("HOST_LIST server", se.server, 256);
|
||||
Option::validate_string("HOST_LIST friendly name", se.friendlyName, 256);
|
||||
if (!se.server.empty() && !se.friendlyName.empty() && serverList_.size() < max_server_list_size)
|
||||
serverList_.push_back(se);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push-peer-info
|
||||
{
|
||||
if (options.exists("push-peer-info"))
|
||||
pushPeerInfo_ = true;
|
||||
if (pushPeerInfo_)
|
||||
peerInfoUV_ = peer_info_uv;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error_ = true;
|
||||
message_ = Unicode::utf8_printable<std::string>(e.what(), 256);
|
||||
}
|
||||
}
|
||||
|
||||
static ParseClientConfig parse(const std::string& content, OptionList::KeyValueList* content_list)
|
||||
{
|
||||
OptionList options;
|
||||
return parse(content, content_list, options);
|
||||
}
|
||||
|
||||
static ParseClientConfig parse(const std::string& content,
|
||||
OptionList::KeyValueList* content_list,
|
||||
OptionList& options)
|
||||
{
|
||||
try {
|
||||
OptionList::Limits limits("profile is too large",
|
||||
ProfileParseLimits::MAX_PROFILE_SIZE,
|
||||
ProfileParseLimits::OPT_OVERHEAD,
|
||||
ProfileParseLimits::TERM_OVERHEAD,
|
||||
ProfileParseLimits::MAX_LINE_SIZE,
|
||||
ProfileParseLimits::MAX_DIRECTIVE_SIZE);
|
||||
options.clear();
|
||||
options.parse_from_config(content, &limits);
|
||||
options.parse_meta_from_config(content, "OVPN_ACCESS_SERVER", &limits);
|
||||
if (content_list)
|
||||
{
|
||||
content_list->preprocess();
|
||||
options.parse_from_key_value_list(*content_list, &limits);
|
||||
}
|
||||
process_setenv_opt(options);
|
||||
options.update_map();
|
||||
|
||||
// add in missing options
|
||||
bool added = false;
|
||||
|
||||
// client
|
||||
if (!options.exists("client"))
|
||||
{
|
||||
Option opt;
|
||||
opt.push_back("client");
|
||||
options.push_back(opt);
|
||||
added = true;
|
||||
}
|
||||
|
||||
// dev
|
||||
if (!options.exists("dev"))
|
||||
{
|
||||
Option opt;
|
||||
opt.push_back("dev");
|
||||
opt.push_back("tun");
|
||||
options.push_back(opt);
|
||||
added = true;
|
||||
}
|
||||
if (added)
|
||||
options.update_map();
|
||||
|
||||
return ParseClientConfig(options);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ParseClientConfig ret;
|
||||
ret.error_ = true;
|
||||
ret.message_ = Unicode::utf8_printable<std::string>(e.what(), 256);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// true if error
|
||||
bool error() const { return error_; }
|
||||
|
||||
// if error, message given here
|
||||
const std::string& message() const { return message_; }
|
||||
|
||||
// this username must be used with profile
|
||||
const std::string& userlockedUsername() const { return userlockedUsername_; }
|
||||
|
||||
// profile name of config
|
||||
const std::string& profileName() const { return profileName_; }
|
||||
|
||||
// "friendly" name of config
|
||||
const std::string& friendlyName() const { return friendlyName_; }
|
||||
|
||||
// true: no creds required, false: username/password required
|
||||
bool autologin() const { return autologin_; }
|
||||
|
||||
// profile embedded password via <auth-user-pass>
|
||||
bool hasEmbeddedPassword() const { return hasEmbeddedPassword_; }
|
||||
const std::string& embeddedPassword() const { return embeddedPassword_; }
|
||||
|
||||
// true: no client cert/key required, false: client cert/key required
|
||||
bool clientCertEnabled() const { return clientCertEnabled_; }
|
||||
|
||||
// if true, this is an External PKI profile (no cert or key directives)
|
||||
bool externalPki() const { return externalPki_; }
|
||||
|
||||
// static challenge, may be empty, ignored if autologin
|
||||
const std::string& staticChallenge() const { return staticChallenge_; }
|
||||
|
||||
// true if static challenge response should be echoed to UI, ignored if autologin
|
||||
bool staticChallengeEcho() const { return staticChallengeEcho_; }
|
||||
|
||||
// true if this profile requires a private key password
|
||||
bool privateKeyPasswordRequired() const { return privateKeyPasswordRequired_; }
|
||||
|
||||
// true if user is allowed to save authentication password in UI
|
||||
bool allowPasswordSave() const { return allowPasswordSave_; }
|
||||
|
||||
// true if "setenv PUSH_PEER_INFO" or "push-peer-info" are defined
|
||||
bool pushPeerInfo() const { return pushPeerInfo_; }
|
||||
|
||||
// "setenv UV_x" directives if pushPeerInfo() is true
|
||||
const PeerInfo::Set* peerInfoUV() const { return peerInfoUV_.get(); }
|
||||
|
||||
// optional list of user-selectable VPN servers
|
||||
const ServerList& serverList() const { return serverList_; }
|
||||
|
||||
// return first remote directive in config
|
||||
const RemoteItem& firstRemoteListItem() const { return firstRemoteListItem_; }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "user=" << userlockedUsername_
|
||||
<< " pn=" << profileName_
|
||||
<< " fn=" << friendlyName_
|
||||
<< " auto=" << autologin_
|
||||
<< " embed_pw=" << hasEmbeddedPassword_
|
||||
<< " epki=" << externalPki_
|
||||
<< " schal=" << staticChallenge_
|
||||
<< " scecho=" << staticChallengeEcho_;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
static bool parse_auth_user_pass(const OptionList& options, std::vector<std::string>* user_pass)
|
||||
{
|
||||
return UserPass::parse(options, "auth-user-pass", 0, user_pass);
|
||||
}
|
||||
|
||||
static void process_setenv_opt(OptionList& options)
|
||||
{
|
||||
for (OptionList::iterator i = options.begin(); i != options.end(); ++i)
|
||||
{
|
||||
Option& o = *i;
|
||||
if (o.size() >= 3 && o.ref(0) == "setenv" && o.ref(1) == "opt")
|
||||
o.remove_first(2);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_autologin(const OptionList& options,
|
||||
const bool auth_user_pass,
|
||||
const std::vector<std::string>& user_pass)
|
||||
{
|
||||
if (auth_user_pass && user_pass.size() >= 2) // embedded password?
|
||||
return true;
|
||||
else
|
||||
{
|
||||
const Option* autologin = options.get_ptr("AUTOLOGIN");
|
||||
if (autologin)
|
||||
return string::is_true(autologin->get_optional(1, 16));
|
||||
else
|
||||
{
|
||||
bool ret = !auth_user_pass;
|
||||
if (ret)
|
||||
{
|
||||
// External PKI profiles from AS don't declare auth-user-pass,
|
||||
// and we have no way of knowing if they are autologin unless
|
||||
// we examine their cert, which requires accessing the system-level
|
||||
// cert store on the client. For now, we are going to assume
|
||||
// that External PKI profiles from the AS are always userlogin,
|
||||
// unless explicitly overriden by AUTOLOGIN above.
|
||||
if (options.exists("EXTERNAL_PKI"))
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_external_pki(const OptionList& options)
|
||||
{
|
||||
const Option* epki = options.get_ptr("EXTERNAL_PKI");
|
||||
if (epki)
|
||||
return string::is_true(epki->get_optional(1, 16));
|
||||
else
|
||||
{
|
||||
const Option* cert = options.get_ptr("cert");
|
||||
const Option* key = options.get_ptr("key");
|
||||
return !cert || !key;
|
||||
}
|
||||
}
|
||||
|
||||
ParseClientConfig()
|
||||
{
|
||||
reset_pod();
|
||||
}
|
||||
|
||||
void reset_pod()
|
||||
{
|
||||
error_ = autologin_ = externalPki_ = staticChallengeEcho_ = false;
|
||||
privateKeyPasswordRequired_ = hasEmbeddedPassword_ = false;
|
||||
pushPeerInfo_ = false;
|
||||
allowPasswordSave_ = clientCertEnabled_ = true;
|
||||
}
|
||||
|
||||
bool parse_bool(const Option& o, const std::string& title, const size_t index)
|
||||
{
|
||||
const std::string parm = o.get(index, 16);
|
||||
if (parm == "0")
|
||||
return false;
|
||||
else if (parm == "1")
|
||||
return true;
|
||||
else
|
||||
throw option_error(title + ": parameter must be 0 or 1");
|
||||
}
|
||||
|
||||
bool error_;
|
||||
std::string message_;
|
||||
std::string userlockedUsername_;
|
||||
std::string profileName_;
|
||||
std::string friendlyName_;
|
||||
bool autologin_;
|
||||
bool clientCertEnabled_;
|
||||
bool externalPki_;
|
||||
bool pushPeerInfo_;
|
||||
std::string staticChallenge_;
|
||||
bool staticChallengeEcho_;
|
||||
bool privateKeyPasswordRequired_;
|
||||
bool allowPasswordSave_;
|
||||
ServerList serverList_;
|
||||
bool hasEmbeddedPassword_;
|
||||
std::string embeddedPassword_;
|
||||
RemoteItem firstRemoteListItem_;
|
||||
PeerInfo::Set::Ptr peerInfoUV_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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-2016 OpenVPN Technologies, 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_CLIENT_IPVERFLAGS_H
|
||||
#define OPENVPN_CLIENT_IPVERFLAGS_H
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/client/rgopt.hpp>
|
||||
#include <openvpn/tun/builder/rgwflags.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class IPVerFlags
|
||||
{
|
||||
public:
|
||||
IPVerFlags(const OptionList& opt,
|
||||
const IP::Addr::VersionMask ip_ver_flags)
|
||||
: ip_ver_flags_(ip_ver_flags),
|
||||
rg_flags_(opt),
|
||||
api_flags_(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool rgv4() const
|
||||
{
|
||||
return v4() && rg_flags_.redirect_gateway_ipv4_enabled();
|
||||
}
|
||||
|
||||
bool rgv6() const
|
||||
{
|
||||
return v6() && rg_flags_.redirect_gateway_ipv6_enabled();
|
||||
}
|
||||
|
||||
bool v4() const
|
||||
{
|
||||
return (ip_ver_flags_ & IP::Addr::V4_MASK) ? true : false;
|
||||
}
|
||||
|
||||
bool v6() const
|
||||
{
|
||||
return (ip_ver_flags_ & IP::Addr::V6_MASK) ? true : false;
|
||||
}
|
||||
|
||||
IP::Addr::VersionMask rg_ver_flags() const
|
||||
{
|
||||
IP::Addr::VersionMask flags = 0;
|
||||
if (rgv4())
|
||||
flags |= IP::Addr::V4_MASK;
|
||||
if (rgv6())
|
||||
flags |= IP::Addr::V6_MASK;
|
||||
return flags;
|
||||
}
|
||||
|
||||
IP::Addr::VersionMask ip_ver_flags() const
|
||||
{
|
||||
IP::Addr::VersionMask flags = 0;
|
||||
if (v4())
|
||||
flags |= IP::Addr::V4_MASK;
|
||||
if (v6())
|
||||
flags |= IP::Addr::V6_MASK;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// these flags are passed to tun_builder_reroute_gw method
|
||||
unsigned int api_flags() const
|
||||
{
|
||||
return api_flags_ | rg_flags_();
|
||||
}
|
||||
|
||||
void set_emulate_exclude_routes()
|
||||
{
|
||||
api_flags_ |= RGWFlags::EmulateExcludeRoutes;
|
||||
}
|
||||
|
||||
private:
|
||||
const IP::Addr::VersionMask ip_ver_flags_;
|
||||
const RedirectGatewayFlags rg_flags_;
|
||||
unsigned int api_flags_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_CLIENT_OPTFILT_H
|
||||
#define OPENVPN_CLIENT_OPTFILT_H
|
||||
|
||||
#include <openvpn/common/options.hpp>
|
||||
|
||||
// Options filters
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class PushedOptionsFilter : public OptionList::FilterBase
|
||||
{
|
||||
public:
|
||||
PushedOptionsFilter(const bool route_nopull)
|
||||
: route_nopull_(route_nopull) {}
|
||||
|
||||
virtual bool filter(const Option& opt)
|
||||
{
|
||||
const bool ret = filt(opt);
|
||||
if (!ret)
|
||||
OPENVPN_LOG("Ignored due to route-nopull: " << opt.render(Option::RENDER_TRUNC_64|Option::RENDER_BRACKET));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
// return false if pushed option should be ignored due to route-nopull directive.
|
||||
bool filt(const Option& opt)
|
||||
{
|
||||
if (route_nopull_)
|
||||
{
|
||||
if (opt.size() >= 1)
|
||||
{
|
||||
const std::string& directive = opt.ref(0);
|
||||
if (directive.length() >= 1)
|
||||
{
|
||||
switch (directive[0])
|
||||
{
|
||||
case 'b':
|
||||
if (directive == "block-ipv6")
|
||||
return false;
|
||||
break;
|
||||
case 'c':
|
||||
if (directive == "client-nat")
|
||||
return false;
|
||||
break;
|
||||
case 'd':
|
||||
if (directive == "dhcp-option" ||
|
||||
directive == "dhcp-renew" ||
|
||||
directive == "dhcp-pre-release" ||
|
||||
directive == "dhcp-release")
|
||||
return false;
|
||||
break;
|
||||
case 'i':
|
||||
if (directive == "ip-win32")
|
||||
return false;
|
||||
break;
|
||||
case 'r':
|
||||
if (directive == "route" ||
|
||||
directive == "route-ipv6" ||
|
||||
directive == "route-metric" ||
|
||||
directive == "redirect-gateway" ||
|
||||
directive == "redirect-private" ||
|
||||
directive == "register-dns" ||
|
||||
directive == "route-delay" ||
|
||||
directive == "route-method")
|
||||
return false;
|
||||
break;
|
||||
case 't':
|
||||
if (directive == "tap-sleep")
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool route_nopull_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,565 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// API for OpenVPN Client, may be used standalone or wrapped by swig.
|
||||
// Use ovpncli.i to wrap the API for swig.
|
||||
// The crux of the API is defined in OpenVPNClient (below)
|
||||
// and TunBuilderBase.
|
||||
|
||||
#ifndef ovpncli_hpp
|
||||
#define ovpncli_hpp
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/tun/builder/base.hpp>
|
||||
#include <openvpn/pki/epkibase.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class OptionList;
|
||||
class ProfileMerge;
|
||||
class Stop;
|
||||
|
||||
namespace ClientAPI {
|
||||
// Represents an OpenVPN server and its friendly name
|
||||
// (client reads)
|
||||
struct ServerEntry
|
||||
{
|
||||
std::string server;
|
||||
std::string friendlyName;
|
||||
};
|
||||
|
||||
// return properties of config
|
||||
// (client reads)
|
||||
struct EvalConfig
|
||||
{
|
||||
// true if error
|
||||
bool error = false;
|
||||
|
||||
// if error, message given here
|
||||
std::string message;
|
||||
|
||||
// this username must be used with profile
|
||||
std::string userlockedUsername;
|
||||
|
||||
// profile name of config
|
||||
std::string profileName;
|
||||
|
||||
// "friendly" name of config
|
||||
std::string friendlyName;
|
||||
|
||||
// true: no creds required, false: username/password required
|
||||
bool autologin = false;
|
||||
|
||||
// if true, this is an External PKI profile (no cert or key directives)
|
||||
bool externalPki = false;
|
||||
|
||||
// static challenge, may be empty, ignored if autologin
|
||||
std::string staticChallenge;
|
||||
|
||||
// true if static challenge response should be echoed to UI, ignored if autologin
|
||||
bool staticChallengeEcho = false;
|
||||
|
||||
// true if this profile requires a private key password
|
||||
bool privateKeyPasswordRequired = false;
|
||||
|
||||
// true if user is allowed to save authentication password in UI
|
||||
bool allowPasswordSave = false;
|
||||
|
||||
// information about the first remote item in config
|
||||
std::string remoteHost; // will be overridden by Config::serverOverride if defined
|
||||
std::string remotePort;
|
||||
std::string remoteProto;
|
||||
|
||||
// optional list of user-selectable VPN servers
|
||||
std::vector<ServerEntry> serverList;
|
||||
};
|
||||
|
||||
// used to pass credentials to VPN core
|
||||
// (client writes)
|
||||
struct ProvideCreds
|
||||
{
|
||||
std::string username;
|
||||
std::string password;
|
||||
|
||||
// response to challenge
|
||||
std::string response;
|
||||
|
||||
// Dynamic challenge/response cookie
|
||||
std::string dynamicChallengeCookie;
|
||||
|
||||
// If true, on successful connect, we will replace the password
|
||||
// with the session ID we receive from the server (if provided).
|
||||
// If false, the password will be cached for future reconnects
|
||||
// and will not be replaced with a session ID, even if the
|
||||
// server provides one.
|
||||
bool replacePasswordWithSessionID = false;
|
||||
|
||||
// If true, and if replacePasswordWithSessionID is true, and if
|
||||
// we actually receive a session ID from the server, cache
|
||||
// the user-provided password for future use before replacing
|
||||
// the active password with the session ID.
|
||||
bool cachePassword = false;
|
||||
};
|
||||
|
||||
// used to get session token from VPN core
|
||||
// (client reads)
|
||||
struct SessionToken
|
||||
{
|
||||
std::string username;
|
||||
std::string session_id; // an OpenVPN Session ID, used as a proxy for password
|
||||
};
|
||||
|
||||
// used to query challenge/response from user
|
||||
// (client reads)
|
||||
struct DynamicChallenge
|
||||
{
|
||||
std::string challenge;
|
||||
bool echo = false;
|
||||
bool responseRequired = false;
|
||||
|
||||
std::string stateID;
|
||||
};
|
||||
|
||||
// a basic key/value pair, used in Config below when OpenVPN profile is
|
||||
// passed as a dictionary
|
||||
struct KeyValue
|
||||
{
|
||||
KeyValue() {
|
||||
}
|
||||
|
||||
KeyValue(const std::string& key_arg, const std::string& value_arg)
|
||||
: key(key_arg),
|
||||
value(value_arg) {
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// OpenVPN config-file/profile
|
||||
// (client writes)
|
||||
struct Config
|
||||
{
|
||||
// OpenVPN profile as a string
|
||||
std::string content;
|
||||
|
||||
// OpenVPN profile as series of key/value pairs (may be provided exclusively
|
||||
// or in addition to content string above).
|
||||
std::vector<KeyValue> contentList;
|
||||
|
||||
// Set to identity OpenVPN GUI version.
|
||||
// Format should be "<gui_identifier><space><version>"
|
||||
// Passed to server as IV_GUI_VER.
|
||||
std::string guiVersion;
|
||||
|
||||
// Use a different server than that specified in "remote"
|
||||
// option of profile
|
||||
std::string serverOverride;
|
||||
|
||||
// Force a given transport protocol
|
||||
// Should be tcp, udp, or adaptive.
|
||||
std::string protoOverride;
|
||||
|
||||
// IPv6 preference
|
||||
// no -- disable IPv6, so tunnel will be IPv4-only
|
||||
// yes -- request combined IPv4/IPv6 tunnel
|
||||
// default (or empty string) -- leave decision to server
|
||||
std::string ipv6;
|
||||
|
||||
// Connection timeout in seconds, or 0 to retry indefinitely
|
||||
int connTimeout = 0;
|
||||
|
||||
// Keep tun interface active during pauses or reconnections
|
||||
bool tunPersist = false;
|
||||
|
||||
// If true and a redirect-gateway profile doesn't also define
|
||||
// DNS servers, use the standard Google DNS servers.
|
||||
bool googleDnsFallback = false;
|
||||
|
||||
// Enable autologin sessions
|
||||
bool autologinSessions = true;
|
||||
|
||||
// An ID used for get-certificate and RSA signing callbacks
|
||||
// for External PKI profiles.
|
||||
std::string externalPkiAlias;
|
||||
|
||||
// If true, don't send client cert/key to peer.
|
||||
bool disableClientCert = false;
|
||||
|
||||
// SSL library debug level
|
||||
int sslDebugLevel = 0;
|
||||
|
||||
// Compression mode, one of:
|
||||
// yes -- allow compression on both uplink and downlink
|
||||
// asym -- allow compression on downlink only (i.e. server -> client)
|
||||
// no (default if empty) -- support compression stubs only
|
||||
std::string compressionMode;
|
||||
|
||||
// private key password (optional)
|
||||
std::string privateKeyPassword;
|
||||
|
||||
// Default key direction parameter for tls-auth (0, 1, or
|
||||
// -1 (bidirectional -- default)) if no key-direction parameter
|
||||
// defined in profile. Generally should be -1 (bidirectional)
|
||||
// for compatibility with 2.x branch
|
||||
int defaultKeyDirection = -1;
|
||||
|
||||
// If true, force ciphersuite to be one of:
|
||||
// 1. TLS_DHE_RSA_WITH_AES_256_CBC_SHA, or
|
||||
// 2. TLS_DHE_RSA_WITH_AES_128_CBC_SHA
|
||||
// and disable setting TLS minimum version.
|
||||
// This is intended for compatibility with legacy systems.
|
||||
bool forceAesCbcCiphersuites = false;
|
||||
|
||||
// Override the minimum TLS version:
|
||||
// disabled -- don't specify a minimum, and disable any minimum
|
||||
// specified in profile
|
||||
// default or "" -- use profile minimum
|
||||
// tls_1_0 -- use TLS 1.0 minimum (overrides profile)
|
||||
// tls_1_1 -- use TLS 1.1 minimum (overrides profile)
|
||||
// tls_1_2 -- use TLS 1.2 minimum (overrides profile)
|
||||
std::string tlsVersionMinOverride;
|
||||
|
||||
// Pass custom key/value pairs to OpenVPN server.
|
||||
std::vector<KeyValue> peerInfo;
|
||||
|
||||
// HTTP Proxy parameters (optional)
|
||||
std::string proxyHost; // hostname or IP address of proxy
|
||||
std::string proxyPort; // port number of proxy
|
||||
std::string proxyUsername; // proxy credentials (optional)
|
||||
std::string proxyPassword; // proxy credentials (optional)
|
||||
bool proxyAllowCleartextAuth = false; // enables HTTP Basic auth
|
||||
|
||||
// Custom proxy implementation
|
||||
bool altProxy = false;
|
||||
|
||||
// Custom Data Channel Offload implementation
|
||||
bool dco = false;
|
||||
|
||||
// pass through pushed "echo" directives via "ECHO" event
|
||||
bool echo = false;
|
||||
|
||||
// pass through control channel INFO notifications via "INFO" event
|
||||
bool info = false;
|
||||
|
||||
// Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN)
|
||||
std::string gremlinConfig;
|
||||
};
|
||||
|
||||
// used to communicate VPN events such as connect, disconnect, etc.
|
||||
// (client reads)
|
||||
struct Event
|
||||
{
|
||||
bool error = false; // true if error (fatal or nonfatal)
|
||||
bool fatal = false; // true if fatal error (will disconnect)
|
||||
std::string name; // event name
|
||||
std::string info; // additional event info
|
||||
};
|
||||
|
||||
// used to communicate extra details about successful connection
|
||||
// (client reads)
|
||||
struct ConnectionInfo
|
||||
{
|
||||
bool defined = false;
|
||||
std::string user;
|
||||
std::string serverHost;
|
||||
std::string serverPort;
|
||||
std::string serverProto;
|
||||
std::string serverIp;
|
||||
std::string vpnIp4;
|
||||
std::string vpnIp6;
|
||||
std::string gw4;
|
||||
std::string gw6;
|
||||
std::string clientIp;
|
||||
std::string tunName;
|
||||
};
|
||||
|
||||
// returned by some methods as a status/error indication
|
||||
// (client reads)
|
||||
struct Status
|
||||
{
|
||||
bool error = false; // true if error
|
||||
std::string status; // an optional short error label that identifies the error
|
||||
std::string message; // if error, message given here
|
||||
};
|
||||
|
||||
// used to pass log lines
|
||||
// (client reads)
|
||||
struct LogInfo
|
||||
{
|
||||
LogInfo() {
|
||||
}
|
||||
LogInfo(std::string str);
|
||||
std::string text; // log output (usually but not always one line)
|
||||
};
|
||||
|
||||
// receives log messages
|
||||
struct LogReceiver
|
||||
{
|
||||
virtual void log(const LogInfo&) = 0;
|
||||
virtual ~LogReceiver() {
|
||||
}
|
||||
};
|
||||
|
||||
// used to pass stats for an interface
|
||||
struct InterfaceStats
|
||||
{
|
||||
long long bytesIn;
|
||||
long long packetsIn;
|
||||
long long errorsIn;
|
||||
long long bytesOut;
|
||||
long long packetsOut;
|
||||
long long errorsOut;
|
||||
};
|
||||
|
||||
// used to pass basic transport stats
|
||||
struct TransportStats
|
||||
{
|
||||
long long bytesIn;
|
||||
long long bytesOut;
|
||||
long long packetsIn;
|
||||
long long packetsOut;
|
||||
|
||||
// number of binary milliseconds (1/1024th of a second) since
|
||||
// last packet was received, or -1 if undefined
|
||||
int lastPacketReceived;
|
||||
};
|
||||
|
||||
// return value of merge_config methods
|
||||
struct MergeConfig
|
||||
{
|
||||
std::string status; // ProfileMerge::Status codes rendered as string
|
||||
std::string errorText; // error string (augments status)
|
||||
std::string basename; // profile basename
|
||||
std::string profileContent; // unified profile
|
||||
std::vector<std::string> refPathList; // list of all reference paths successfully read
|
||||
};
|
||||
|
||||
// base class for External PKI queries
|
||||
struct ExternalPKIRequestBase
|
||||
{
|
||||
bool error = false; // true if error occurred (client writes)
|
||||
std::string errorText; // text describing error (client writes)
|
||||
bool invalidAlias = false; // true if the error is caused by an invalid alias (client writes)
|
||||
std::string alias; // the alias string, used to query cert/key (client reads)
|
||||
};
|
||||
|
||||
// used to query for External PKI certificate
|
||||
struct ExternalPKICertRequest : public ExternalPKIRequestBase
|
||||
{
|
||||
// leaf cert
|
||||
std::string cert; // (client writes)
|
||||
|
||||
// chain of intermediates and root (optional)
|
||||
std::string supportingChain; // (client writes)
|
||||
};
|
||||
|
||||
// used to request an RSA signature
|
||||
struct ExternalPKISignRequest : public ExternalPKIRequestBase
|
||||
{
|
||||
std::string sigType; // signature type (client reads)
|
||||
std::string data; // data rendered as base64 (client reads)
|
||||
std::string sig; // RSA signature, rendered as base64 (client writes)
|
||||
};
|
||||
|
||||
// used to override "remote" directives
|
||||
struct RemoteOverride
|
||||
{
|
||||
// components of "remote" directive (client writes),
|
||||
std::string host; // either one of host
|
||||
std::string ip; // or ip must be defined (or both)
|
||||
std::string port;
|
||||
std::string proto;
|
||||
};
|
||||
|
||||
namespace Private {
|
||||
class ClientState;
|
||||
};
|
||||
|
||||
// Top-level OpenVPN client class.
|
||||
class OpenVPNClient : public TunBuilderBase, public LogReceiver, private ExternalPKIBase {
|
||||
public:
|
||||
OpenVPNClient();
|
||||
virtual ~OpenVPNClient();
|
||||
|
||||
// Call me first, before calling any other method (static or instance methods)
|
||||
// in this class.
|
||||
static void init_process();
|
||||
|
||||
// Release any resources allocated by init_process.
|
||||
static void uninit_process();
|
||||
|
||||
// Read an OpenVPN profile that might contain external
|
||||
// file references, returning a unified profile.
|
||||
static MergeConfig merge_config_static(const std::string& path, bool follow_references);
|
||||
|
||||
// Read an OpenVPN profile that might contain external
|
||||
// file references, returning a unified profile.
|
||||
static MergeConfig merge_config_string_static(const std::string& config_content);
|
||||
|
||||
// Parse profile and determine needed credentials statically.
|
||||
static EvalConfig eval_config_static(const Config& config);
|
||||
|
||||
// Maximum size of profile that should be allowed
|
||||
static long max_profile_size();
|
||||
|
||||
// Parse a dynamic challenge cookie, placing the result in dc.
|
||||
// Return true on success or false if parse error.
|
||||
static bool parse_dynamic_challenge(const std::string& cookie, DynamicChallenge& dc);
|
||||
|
||||
// Parse OpenVPN configuration file.
|
||||
EvalConfig eval_config(const Config&);
|
||||
|
||||
// Provide credentials and other options. Call before connect().
|
||||
Status provide_creds(const ProvideCreds&);
|
||||
|
||||
// Callback to "protect" a socket from being routed through the tunnel.
|
||||
// Will be called from the thread executing connect().
|
||||
virtual bool socket_protect(int socket) = 0;
|
||||
|
||||
// Primary VPN client connect method, doesn't return until disconnect.
|
||||
// Should be called by a worker thread. This method will make callbacks
|
||||
// to event() and log() functions. Make sure to call eval_config()
|
||||
// and possibly provide_creds() as well before this function.
|
||||
Status connect();
|
||||
|
||||
// Return information about the most recent connection. Should be called
|
||||
// after an event of type "CONNECTED".
|
||||
ConnectionInfo connection_info();
|
||||
|
||||
// Writes current session token to tok and returns true.
|
||||
// If session token is unavailable, false is returned and
|
||||
// tok is unmodified.
|
||||
bool session_token(SessionToken& tok);
|
||||
|
||||
// Stop the client. Only meaningful when connect() is running.
|
||||
// May be called asynchronously from a different thread
|
||||
// when connect() is running.
|
||||
void stop();
|
||||
|
||||
// Pause the client -- useful to avoid continuous reconnection attempts
|
||||
// when network is down. May be called from a different thread
|
||||
// when connect() is running.
|
||||
void pause(const std::string& reason);
|
||||
|
||||
// Resume the client after it has been paused. May be called from a
|
||||
// different thread when connect() is running.
|
||||
void resume();
|
||||
|
||||
// Do a disconnect/reconnect cycle n seconds from now. May be called
|
||||
// from a different thread when connect() is running.
|
||||
void reconnect(int seconds);
|
||||
|
||||
// When a connection is close to timeout, the core will call this
|
||||
// method. If it returns false, the core will disconnect with a
|
||||
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
|
||||
// state.
|
||||
virtual bool pause_on_connection_timeout() = 0;
|
||||
|
||||
// Get stats/error info. May be called from a different thread
|
||||
// when connect() is running.
|
||||
|
||||
// number of stats
|
||||
static int stats_n();
|
||||
|
||||
// return a stats name, index should be >= 0 and < stats_n()
|
||||
static std::string stats_name(int index);
|
||||
|
||||
// return a stats value, index should be >= 0 and < stats_n()
|
||||
long long stats_value(int index) const;
|
||||
|
||||
// return all stats in a bundle
|
||||
std::vector<long long> stats_bundle() const;
|
||||
|
||||
// return tun stats only
|
||||
InterfaceStats tun_stats() const;
|
||||
|
||||
// return transport stats only
|
||||
TransportStats transport_stats() const;
|
||||
|
||||
// Callback for delivering events during connect() call.
|
||||
// Will be called from the thread executing connect().
|
||||
virtual void event(const Event&) = 0;
|
||||
|
||||
// Callback for logging.
|
||||
// Will be called from the thread executing connect().
|
||||
virtual void log(const LogInfo&) = 0;
|
||||
|
||||
// External PKI callbacks
|
||||
// Will be called from the thread executing connect().
|
||||
virtual void external_pki_cert_request(ExternalPKICertRequest&) = 0;
|
||||
virtual void external_pki_sign_request(ExternalPKISignRequest&) = 0;
|
||||
|
||||
// Remote override callback (disabled by default).
|
||||
virtual bool remote_override_enabled();
|
||||
virtual void remote_override(RemoteOverride&);
|
||||
|
||||
// Do a crypto library self test
|
||||
static std::string crypto_self_test();
|
||||
|
||||
// Returns date/time of app expiration as a unix time value
|
||||
static int app_expire();
|
||||
|
||||
// Returns platform description string
|
||||
static std::string platform();
|
||||
|
||||
// Returns core copyright
|
||||
static std::string copyright();
|
||||
|
||||
// Hide protected methods/data from SWIG
|
||||
#ifdef SWIGJAVA
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
|
||||
Status do_connect();
|
||||
|
||||
virtual void connect_attach();
|
||||
virtual void connect_pre_run();
|
||||
virtual void connect_run();
|
||||
virtual void connect_session_stop();
|
||||
|
||||
virtual Stop* get_async_stop();
|
||||
|
||||
Private::ClientState* state;
|
||||
|
||||
private:
|
||||
static void parse_config(const Config&, EvalConfig&, OptionList&);
|
||||
void parse_extras(const Config&, EvalConfig&);
|
||||
void external_pki_error(const ExternalPKIRequestBase&, const size_t err_type);
|
||||
void process_epki_cert_chain(const ExternalPKICertRequest& req);
|
||||
void check_app_expired();
|
||||
static MergeConfig build_merge_config(const ProfileMerge&);
|
||||
|
||||
// from ExternalPKIBase
|
||||
virtual bool sign(const std::string& sig_type, const std::string& data, std::string& sig);
|
||||
|
||||
// disable copy and assignment
|
||||
OpenVPNClient(const OpenVPNClient&) = delete;
|
||||
OpenVPNClient& operator=(const OpenVPNClient&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,918 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// These classes handle parsing and representation of OpenVPN "remote" directives,
|
||||
// and the list of IP addresses that they resolve to.
|
||||
// <connection> blocks are supported.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_REMOTELIST_H
|
||||
#define OPENVPN_CLIENT_REMOTELIST_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/addrlist.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
#include <openvpn/client/cliconstants.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
|
||||
#if OPENVPN_DEBUG_REMOTELIST >= 1
|
||||
#define OPENVPN_LOG_REMOTELIST(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_REMOTELIST(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class RemoteList : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
// A single IP address that is part of a list of IP addresses
|
||||
// associated with a "remote" item.
|
||||
struct ResolvedAddr : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<ResolvedAddr> Ptr;
|
||||
IP::Addr addr;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return addr.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
// The IP address list associated with a single "remote" item.
|
||||
struct ResolvedAddrList : public std::vector<ResolvedAddr::Ptr>, public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<ResolvedAddrList> Ptr;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
for (std::vector<ResolvedAddr::Ptr>::const_iterator i = begin(); i != end(); ++i)
|
||||
{
|
||||
if (!ret.empty())
|
||||
ret += ' ';
|
||||
ret += (*i)->to_string();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
struct Item;
|
||||
|
||||
struct ConnBlock : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<ConnBlock> Ptr;
|
||||
|
||||
virtual void new_item(const Item& item) = 0;
|
||||
};
|
||||
|
||||
struct ConnBlockFactory
|
||||
{
|
||||
typedef RCPtr<ConnBlockFactory> Ptr;
|
||||
|
||||
virtual ConnBlock::Ptr new_conn_block(const OptionList::Ptr& opt) = 0;
|
||||
};
|
||||
|
||||
// A single "remote" item
|
||||
struct Item : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Item> Ptr;
|
||||
|
||||
// "remote" item parameters from config file
|
||||
std::string server_host;
|
||||
std::string server_port;
|
||||
Protocol transport_protocol;
|
||||
|
||||
// IP address list defined after server_host is resolved
|
||||
ResolvedAddrList::Ptr res_addr_list;
|
||||
|
||||
// Other options if this is a <connection> block
|
||||
ConnBlock::Ptr conn_block;
|
||||
|
||||
bool res_addr_list_defined() const
|
||||
{
|
||||
return res_addr_list && res_addr_list->size() > 0;
|
||||
}
|
||||
|
||||
// cache a single IP address
|
||||
void set_ip_addr(const IP::Addr& addr)
|
||||
{
|
||||
res_addr_list.reset(new ResolvedAddrList());
|
||||
ResolvedAddr::Ptr ra(new ResolvedAddr());
|
||||
ra->addr = addr;
|
||||
res_addr_list->push_back(std::move(ra));
|
||||
OPENVPN_LOG_REMOTELIST("*** RemoteList::Item endpoint SET " << to_string());
|
||||
}
|
||||
|
||||
// cache a list of DNS-resolved IP addresses
|
||||
template <class EPRANGE>
|
||||
void set_endpoint_range(EPRANGE& endpoint_range, RandomAPI* rng)
|
||||
{
|
||||
EPRANGE end;
|
||||
res_addr_list.reset(new ResolvedAddrList());
|
||||
for (const auto &i : endpoint_range)
|
||||
{
|
||||
ResolvedAddr::Ptr addr(new ResolvedAddr());
|
||||
addr->addr = IP::Addr::from_asio(i.endpoint().address());
|
||||
res_addr_list->push_back(addr);
|
||||
}
|
||||
if (rng && res_addr_list->size() >= 2)
|
||||
std::shuffle(res_addr_list->begin(), res_addr_list->end(), *rng);
|
||||
OPENVPN_LOG_REMOTELIST("*** RemoteList::Item endpoint SET " << to_string());
|
||||
}
|
||||
|
||||
// get an endpoint for contacting server
|
||||
template <class EP>
|
||||
bool get_endpoint(EP& endpoint, const size_t index) const
|
||||
{
|
||||
if (res_addr_list && index < res_addr_list->size())
|
||||
{
|
||||
endpoint.address((*res_addr_list)[index]->addr.to_asio());
|
||||
endpoint.port(parse_number_throw<unsigned int>(server_port, "remote_port"));
|
||||
OPENVPN_LOG_REMOTELIST("*** RemoteList::Item endpoint GET[" << index << "] " << endpoint << ' ' << to_string());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "host=" << server_host;
|
||||
if (res_addr_list)
|
||||
out << '[' << res_addr_list->to_string() << ']';
|
||||
out << " port=" << server_port
|
||||
<< " proto=" << transport_protocol.str();
|
||||
return out.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoteOverride
|
||||
{
|
||||
virtual Item::Ptr get() = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
// Directive names that we search for in options
|
||||
struct Directives
|
||||
{
|
||||
void init(const std::string& connection_tag)
|
||||
{
|
||||
connection = connection_tag.length() ? connection_tag : "connection";
|
||||
remote = "remote";
|
||||
proto = "proto";
|
||||
port = "port";
|
||||
}
|
||||
|
||||
std::string connection;
|
||||
std::string remote;
|
||||
std::string proto;
|
||||
std::string port;
|
||||
};
|
||||
|
||||
// Used to index into remote list.
|
||||
// The primary index is the remote list index.
|
||||
// The secondary index is the index into the
|
||||
// Item's IP address list (res_addr_list).
|
||||
class Index
|
||||
{
|
||||
public:
|
||||
Index()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
primary_ = secondary_ = 0;
|
||||
}
|
||||
|
||||
void reset_secondary()
|
||||
{
|
||||
secondary_ = 0;
|
||||
}
|
||||
|
||||
// return true if primary index was incremented
|
||||
bool increment(const size_t pri_len, const size_t sec_len)
|
||||
{
|
||||
if (++secondary_ >= sec_len)
|
||||
{
|
||||
secondary_ = 0;
|
||||
if (++primary_ >= pri_len)
|
||||
primary_ = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool equals(const Index& other) const
|
||||
{
|
||||
return primary_ == other.primary_ && secondary_ == other.secondary_;
|
||||
}
|
||||
|
||||
size_t primary() const { return primary_; }
|
||||
size_t secondary() const { return secondary_; }
|
||||
|
||||
private:
|
||||
size_t primary_;
|
||||
size_t secondary_;
|
||||
};
|
||||
|
||||
public:
|
||||
// Used for errors occurring after initial options processing,
|
||||
// and generally indicate logic errors
|
||||
// (option_error used during initial options processing).
|
||||
OPENVPN_EXCEPTION(remote_list_error);
|
||||
|
||||
typedef RCPtr<RemoteList> Ptr;
|
||||
|
||||
// Helper class used to pre-resolve all items in remote list.
|
||||
// This is useful in tun_persist mode, where it may be necessary
|
||||
// to pre-resolve all potential remote server items prior
|
||||
// to initial tunnel establishment.
|
||||
class PreResolve : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<PreResolve> Ptr;
|
||||
|
||||
struct NotifyCallback
|
||||
{
|
||||
// client callback when resolve operation is complete
|
||||
virtual void pre_resolve_done() = 0;
|
||||
};
|
||||
|
||||
PreResolve(asio::io_context& io_context_arg,
|
||||
const RemoteList::Ptr& remote_list_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: io_context(io_context_arg),
|
||||
resolver(io_context_arg),
|
||||
notify_callback(nullptr),
|
||||
remote_list(remote_list_arg),
|
||||
stats(stats_arg),
|
||||
index(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool work_available() const
|
||||
{
|
||||
return remote_list->defined() && remote_list->enable_cache;
|
||||
}
|
||||
|
||||
void start(NotifyCallback* notify_callback_arg)
|
||||
{
|
||||
if (notify_callback_arg)
|
||||
{
|
||||
// This method is a no-op (i.e. pre_resolve_done is called immediately)
|
||||
// if caching not enabled in underlying remote_list or if start() was
|
||||
// previously called and is still in progress.
|
||||
if (!notify_callback && work_available())
|
||||
{
|
||||
notify_callback = notify_callback_arg;
|
||||
remote_list->index.reset();
|
||||
index = 0;
|
||||
next();
|
||||
}
|
||||
else
|
||||
notify_callback_arg->pre_resolve_done();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
notify_callback = nullptr;
|
||||
index = 0;
|
||||
resolver.cancel();
|
||||
}
|
||||
|
||||
private:
|
||||
void next()
|
||||
{
|
||||
while (index < remote_list->list.size())
|
||||
{
|
||||
Item& item = *remote_list->list[index];
|
||||
|
||||
// try to resolve item if no cached data present
|
||||
if (!item.res_addr_list_defined())
|
||||
{
|
||||
// next item to resolve
|
||||
const Item* sitem = remote_list->search_server_host(item.server_host);
|
||||
if (sitem)
|
||||
{
|
||||
// item's server_host matches one previously resolved -- use it
|
||||
OPENVPN_LOG_REMOTELIST("*** PreResolve USED CACHE for " << item.server_host);
|
||||
item.res_addr_list = sitem->res_addr_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
// call into Asio to do the resolve operation
|
||||
OPENVPN_LOG_REMOTELIST("*** PreResolve RESOLVE on " << item.server_host);
|
||||
resolver.async_resolve(item.server_host, "",
|
||||
[self=Ptr(this)](const asio::error_code& error, asio::ip::tcp::resolver::results_type results)
|
||||
{
|
||||
self->resolve_callback(error, results);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
// Done resolving list. Prune out all entries we were unable to
|
||||
// resolve unless doing so would result in an empty list.
|
||||
// Then call client's callback method.
|
||||
{
|
||||
NotifyCallback* ncb = notify_callback;
|
||||
if (remote_list->cached_item_exists())
|
||||
remote_list->prune_uncached();
|
||||
cancel();
|
||||
ncb->pre_resolve_done();
|
||||
}
|
||||
}
|
||||
|
||||
// callback on resolve completion
|
||||
void resolve_callback(const asio::error_code& error,
|
||||
asio::ip::tcp::resolver::results_type results)
|
||||
{
|
||||
if (notify_callback && index < remote_list->list.size())
|
||||
{
|
||||
Item& item = *remote_list->list[index++];
|
||||
if (!error)
|
||||
{
|
||||
// resolve succeeded
|
||||
item.set_endpoint_range(results, remote_list->rng.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
// resolve failed
|
||||
OPENVPN_LOG("DNS pre-resolve error on " << item.server_host << ": " << error.message());
|
||||
if (stats)
|
||||
stats->error(Error::RESOLVE_ERROR);
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::resolver resolver;
|
||||
NotifyCallback* notify_callback;
|
||||
RemoteList::Ptr remote_list;
|
||||
SessionStats::Ptr stats;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
// create an empty remote list
|
||||
RemoteList()
|
||||
{
|
||||
init("");
|
||||
}
|
||||
|
||||
// create a remote list with a RemoteOverride callback
|
||||
RemoteList(RemoteOverride* remote_override_arg)
|
||||
: remote_override(remote_override_arg)
|
||||
{
|
||||
init("");
|
||||
next();
|
||||
}
|
||||
|
||||
// create a remote list with exactly one item
|
||||
RemoteList(const std::string& server_host,
|
||||
const std::string& server_port,
|
||||
const Protocol& transport_protocol,
|
||||
const std::string& title)
|
||||
{
|
||||
init("");
|
||||
|
||||
HostPort::validate_port(server_port, title);
|
||||
|
||||
Item::Ptr item(new Item());
|
||||
item->server_host = server_host;
|
||||
item->server_port = server_port;
|
||||
item->transport_protocol = transport_protocol;
|
||||
list.push_back(item);
|
||||
}
|
||||
|
||||
// RemoteList flags
|
||||
enum {
|
||||
WARN_UNSUPPORTED=1<<0,
|
||||
CONN_BLOCK_ONLY=1<<1,
|
||||
CONN_BLOCK_OMIT_UNDEF=1<<2,
|
||||
ALLOW_EMPTY=1<<3,
|
||||
};
|
||||
|
||||
// create a remote list from config file option list
|
||||
RemoteList(const OptionList& opt,
|
||||
const std::string& connection_tag,
|
||||
const unsigned int flags,
|
||||
ConnBlockFactory* conn_block_factory)
|
||||
{
|
||||
init(connection_tag);
|
||||
|
||||
// defaults
|
||||
Protocol default_proto(Protocol::UDPv4);
|
||||
std::string default_port = "1194";
|
||||
|
||||
// handle remote, port, and proto at the top-level
|
||||
if (!(flags & CONN_BLOCK_ONLY))
|
||||
add(opt, default_proto, default_port, ConnBlock::Ptr());
|
||||
|
||||
// cycle through <connection> blocks
|
||||
{
|
||||
const size_t max_conn_block_size = 4096;
|
||||
const OptionList::IndexList* conn = opt.get_index_ptr(directives.connection);
|
||||
if (conn)
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = conn->begin(); i != conn->end(); ++i)
|
||||
{
|
||||
try {
|
||||
const Option& o = opt[*i];
|
||||
o.touch();
|
||||
const std::string& conn_block_text = o.get(1, Option::MULTILINE);
|
||||
OptionList::Limits limits("<connection> block is too large",
|
||||
max_conn_block_size,
|
||||
ProfileParseLimits::OPT_OVERHEAD,
|
||||
ProfileParseLimits::TERM_OVERHEAD,
|
||||
ProfileParseLimits::MAX_LINE_SIZE,
|
||||
ProfileParseLimits::MAX_DIRECTIVE_SIZE);
|
||||
OptionList::Ptr conn_block = OptionList::parse_from_config_static_ptr(conn_block_text, &limits);
|
||||
Protocol proto(default_proto);
|
||||
std::string port(default_port);
|
||||
|
||||
// unsupported options
|
||||
if (flags & WARN_UNSUPPORTED)
|
||||
{
|
||||
unsupported_in_connection_block(*conn_block, "http-proxy");
|
||||
unsupported_in_connection_block(*conn_block, "http-proxy-option");
|
||||
unsupported_in_connection_block(*conn_block, "http-proxy-user-pass");
|
||||
}
|
||||
|
||||
// connection block options encapsulation via user-defined factory
|
||||
{
|
||||
ConnBlock::Ptr cb;
|
||||
if (conn_block_factory)
|
||||
cb = conn_block_factory->new_conn_block(conn_block);
|
||||
if (!(flags & CONN_BLOCK_OMIT_UNDEF) || cb)
|
||||
add(*conn_block, proto, port, cb);
|
||||
}
|
||||
}
|
||||
catch (Exception& e)
|
||||
{
|
||||
e.remove_label("option_error");
|
||||
e.add_label("connection_block");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & ALLOW_EMPTY) && list.empty())
|
||||
throw option_error("remote option not specified");
|
||||
|
||||
//OPENVPN_LOG(to_string());
|
||||
}
|
||||
|
||||
// if cache is enabled, all DNS names will be preemptively queried
|
||||
void set_enable_cache(const bool enable_cache_arg)
|
||||
{
|
||||
enable_cache = enable_cache_arg;
|
||||
}
|
||||
|
||||
bool get_enable_cache() const
|
||||
{
|
||||
return enable_cache;
|
||||
}
|
||||
|
||||
// override all server hosts to server_override
|
||||
void set_server_override(const std::string& server_override)
|
||||
{
|
||||
if (!server_override.empty())
|
||||
{
|
||||
for (std::vector<Item::Ptr>::iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
Item& item = **i;
|
||||
item.server_host = server_override;
|
||||
item.res_addr_list.reset(nullptr);
|
||||
}
|
||||
reset_cache();
|
||||
}
|
||||
}
|
||||
|
||||
void set_random(const RandomAPI::Ptr& rng_arg)
|
||||
{
|
||||
rng = rng_arg;
|
||||
}
|
||||
|
||||
// randomize item list, used to implement remote-random directive
|
||||
void randomize()
|
||||
{
|
||||
if (rng)
|
||||
{
|
||||
std::shuffle(list.begin(), list.end(), *rng);
|
||||
index.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// return true if at least one remote entry is of type proto
|
||||
bool contains_protocol(const Protocol& proto)
|
||||
{
|
||||
for (std::vector<Item::Ptr>::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
if (proto.transport_match((*i)->transport_protocol))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Higher-level version of set_proto_override that also supports indication
|
||||
// on whether or not TCP-based proxies are enabled. Should be called after set_enable_cache
|
||||
// because it may modify enable_cache flag.
|
||||
void handle_proto_override(const Protocol& proto_override, const bool tcp_proxy_enabled)
|
||||
{
|
||||
if (tcp_proxy_enabled)
|
||||
{
|
||||
const Protocol tcp(Protocol::TCP);
|
||||
if (contains_protocol(tcp))
|
||||
set_proto_override(tcp);
|
||||
else
|
||||
throw option_error("cannot connect via TCP-based proxy because no TCP server entries exist in profile");
|
||||
}
|
||||
else if (proto_override.defined() && contains_protocol(proto_override))
|
||||
set_proto_override(proto_override);
|
||||
}
|
||||
|
||||
// increment to next IP address
|
||||
void next()
|
||||
{
|
||||
if (remote_override)
|
||||
{
|
||||
list.clear();
|
||||
index.reset();
|
||||
Item::Ptr item = remote_override->get();
|
||||
if (item)
|
||||
list.push_back(std::move(item));
|
||||
}
|
||||
else
|
||||
{
|
||||
index.increment(list.size(), secondary_length(index.primary()));
|
||||
if (!enable_cache)
|
||||
reset_item(index.primary());
|
||||
}
|
||||
}
|
||||
|
||||
// Return details about current connection entry.
|
||||
// Return value is true if get_endpoint may be called
|
||||
// without raising an exception.
|
||||
bool endpoint_available(std::string* server_host, std::string* server_port, Protocol* transport_protocol) const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
if (server_host)
|
||||
*server_host = item.server_host;
|
||||
if (server_port)
|
||||
*server_port = item.server_port;
|
||||
const bool cached = (item.res_addr_list && index.secondary() < item.res_addr_list->size());
|
||||
if (transport_protocol)
|
||||
{
|
||||
if (cached)
|
||||
{
|
||||
// Since we know whether resolved address is IPv4 or IPv6, add
|
||||
// that info to the returned Protocol object.
|
||||
Protocol proto(item.transport_protocol);
|
||||
proto.mod_addr_version((*item.res_addr_list)[index.secondary()]->addr);
|
||||
*transport_protocol = proto;
|
||||
}
|
||||
else
|
||||
*transport_protocol = item.transport_protocol;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
// cache a list of DNS-resolved IP addresses
|
||||
template <class EPRANGE>
|
||||
void set_endpoint_range(EPRANGE& endpoint_range)
|
||||
{
|
||||
Item& item = *list[primary_index()];
|
||||
item.set_endpoint_range(endpoint_range, rng.get());
|
||||
index.reset_secondary();
|
||||
}
|
||||
|
||||
// get an endpoint for contacting server
|
||||
template <class EP>
|
||||
void get_endpoint(EP& endpoint) const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
if (!item.get_endpoint(endpoint, index.secondary()))
|
||||
throw remote_list_error("current remote server endpoint is undefined");
|
||||
}
|
||||
|
||||
// return true if object has at least one connection entry
|
||||
bool defined() const { return list.size() > 0; }
|
||||
|
||||
// return remote list size
|
||||
size_t size() const { return list.size(); }
|
||||
|
||||
const Item& get_item(const size_t index) const
|
||||
{
|
||||
return *list.at(index);
|
||||
}
|
||||
|
||||
// return hostname (or IP address) of current connection entry
|
||||
const std::string& current_server_host() const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
return item.server_host;
|
||||
}
|
||||
|
||||
// return transport protocol of current connection entry
|
||||
const Protocol& current_transport_protocol() const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
return item.transport_protocol;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename T::Ptr current_conn_block() const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
return item.conn_block.template dynamic_pointer_cast<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* current_conn_block_rawptr() const
|
||||
{
|
||||
const Item& item = *list[primary_index()];
|
||||
return dynamic_cast<T*>(item.conn_block.get());
|
||||
}
|
||||
|
||||
// return hostname (or IP address) of first connection entry
|
||||
std::string first_server_host() const
|
||||
{
|
||||
const Item& item = *list.at(0);
|
||||
return item.server_host;
|
||||
}
|
||||
|
||||
const Item* first_item() const
|
||||
{
|
||||
if (defined())
|
||||
return list[0].get();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
for (size_t i = 0; i < list.size(); ++i)
|
||||
{
|
||||
const Item& e = *list[i];
|
||||
out << '[' << i << "] " << e.to_string() << std::endl;
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// return a list of unique, cached IP addresses
|
||||
void cached_ip_address_list(IP::AddrList& addrlist) const
|
||||
{
|
||||
for (std::vector<Item::Ptr>::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
const Item& item = **i;
|
||||
if (item.res_addr_list_defined())
|
||||
{
|
||||
const ResolvedAddrList& ral = *item.res_addr_list;
|
||||
for (ResolvedAddrList::const_iterator j = ral.begin(); j != ral.end(); ++j)
|
||||
{
|
||||
const ResolvedAddr& addr = **j;
|
||||
addrlist.add(addr.addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset the cache associated with all items
|
||||
void reset_cache()
|
||||
{
|
||||
for (auto &e : list)
|
||||
e->res_addr_list.reset(nullptr);
|
||||
index.reset();
|
||||
}
|
||||
|
||||
// if caching is disabled, reset the cache for current item
|
||||
void reset_cache_item()
|
||||
{
|
||||
if (!enable_cache)
|
||||
reset_item(index.primary());
|
||||
}
|
||||
|
||||
private:
|
||||
// initialization, called by constructors
|
||||
void init(const std::string& connection_tag)
|
||||
{
|
||||
enable_cache = false;
|
||||
directives.init(connection_tag);
|
||||
}
|
||||
|
||||
// reset the cache associated with a given item
|
||||
void reset_item(const size_t i)
|
||||
{
|
||||
if (i <= list.size())
|
||||
list[i]->res_addr_list.reset(nullptr);
|
||||
}
|
||||
|
||||
// return the current primary index (into list) and raise an exception
|
||||
// if it is undefined
|
||||
const size_t primary_index() const
|
||||
{
|
||||
const size_t pri = index.primary();
|
||||
if (pri < list.size())
|
||||
return pri;
|
||||
else
|
||||
throw remote_list_error("current remote server item is undefined");
|
||||
}
|
||||
|
||||
// return the number of cached IP addresses associated with a given item
|
||||
size_t secondary_length(const size_t i) const
|
||||
{
|
||||
if (i < list.size())
|
||||
{
|
||||
const Item& item = *list[i];
|
||||
if (item.res_addr_list)
|
||||
return item.res_addr_list->size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search for cached Item by server_host
|
||||
Item* search_server_host(const std::string& server_host)
|
||||
{
|
||||
for (std::vector<Item::Ptr>::iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
Item* item = i->get();
|
||||
if (server_host == item->server_host && item->res_addr_list_defined())
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// prune remote entries so that only those of Protocol proto_override remain
|
||||
void set_proto_override(const Protocol& proto_override)
|
||||
{
|
||||
if (proto_override.defined())
|
||||
{
|
||||
size_t di = 0;
|
||||
for (size_t si = 0; si < list.size(); ++si)
|
||||
{
|
||||
const Item& item = *list[si];
|
||||
if (proto_override.transport_match(item.transport_protocol))
|
||||
{
|
||||
if (si != di)
|
||||
list[di] = list[si];
|
||||
++di;
|
||||
}
|
||||
}
|
||||
if (di != list.size())
|
||||
list.resize(di);
|
||||
reset_cache();
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if at least one cached Item exists
|
||||
bool cached_item_exists() const
|
||||
{
|
||||
for (std::vector<Item::Ptr>::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
const Item& item = **i;
|
||||
if (item.res_addr_list_defined())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prune uncached Items so that only Items containing a res_addr_list with
|
||||
// size > 0 remain.
|
||||
void prune_uncached()
|
||||
{
|
||||
size_t di = 0;
|
||||
for (size_t si = 0; si < list.size(); ++si)
|
||||
{
|
||||
const Item& item = *list[si];
|
||||
if (item.res_addr_list_defined())
|
||||
{
|
||||
if (si != di)
|
||||
list[di] = list[si];
|
||||
++di;
|
||||
}
|
||||
}
|
||||
if (di != list.size())
|
||||
list.resize(di);
|
||||
index.reset();
|
||||
}
|
||||
|
||||
void add(const OptionList& opt, Protocol& default_proto, std::string& default_port, ConnBlock::Ptr conn_block)
|
||||
{
|
||||
// parse "proto" option if present
|
||||
{
|
||||
const Option* o = opt.get_ptr(directives.proto);
|
||||
if (o)
|
||||
default_proto = Protocol::parse(o->get(1, 16), Protocol::CLIENT_SUFFIX);
|
||||
}
|
||||
|
||||
// parse "port" option if present
|
||||
{
|
||||
const Option* o = opt.get_ptr(directives.port);
|
||||
if (o)
|
||||
{
|
||||
default_port = o->get(1, 16);
|
||||
HostPort::validate_port(default_port, directives.port);
|
||||
}
|
||||
}
|
||||
|
||||
// cycle through remote entries
|
||||
{
|
||||
const OptionList::IndexList* rem = opt.get_index_ptr(directives.remote);
|
||||
if (rem)
|
||||
{
|
||||
for (OptionList::IndexList::const_iterator i = rem->begin(); i != rem->end(); ++i)
|
||||
{
|
||||
Item::Ptr e(new Item());
|
||||
const Option& o = opt[*i];
|
||||
o.touch();
|
||||
e->server_host = o.get(1, 256);
|
||||
int adj = 0;
|
||||
if (o.size() >= 3)
|
||||
{
|
||||
e->server_port = o.get(2, 16);
|
||||
if (Protocol::is_local_type(e->server_port))
|
||||
{
|
||||
adj = -1;
|
||||
e->server_port = "";
|
||||
}
|
||||
else
|
||||
HostPort::validate_port(e->server_port, directives.port);
|
||||
}
|
||||
else
|
||||
e->server_port = default_port;
|
||||
if (o.size() >= 4+adj)
|
||||
e->transport_protocol = Protocol::parse(o.get(3+adj, 16), Protocol::CLIENT_SUFFIX);
|
||||
else
|
||||
e->transport_protocol = default_proto;
|
||||
e->conn_block = conn_block;
|
||||
if (conn_block)
|
||||
conn_block->new_item(*e);
|
||||
list.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unsupported_in_connection_block(const OptionList& options, const std::string& option)
|
||||
{
|
||||
if (options.exists(option))
|
||||
OPENVPN_LOG("NOTE: " << option << " directive is not currently supported in <connection> blocks");
|
||||
}
|
||||
|
||||
bool enable_cache;
|
||||
Index index;
|
||||
|
||||
std::vector<Item::Ptr> list;
|
||||
|
||||
Directives directives;
|
||||
|
||||
RemoteOverride* remote_override = nullptr;
|
||||
|
||||
RandomAPI::Ptr rng;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// This class handles parsing and representation of redirect-gateway
|
||||
// and redirect-private directives.
|
||||
|
||||
#ifndef OPENVPN_CLIENT_RGOPT_H
|
||||
#define OPENVPN_CLIENT_RGOPT_H
|
||||
|
||||
#include <openvpn/common/options.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class RedirectGatewayFlags {
|
||||
public:
|
||||
enum Flags {
|
||||
RG_ENABLE = (1<<0),
|
||||
RG_REROUTE_GW = (1<<1),
|
||||
RG_LOCAL = (1<<2),
|
||||
RG_AUTO_LOCAL = (1<<3),
|
||||
RG_DEF1 = (1<<4),
|
||||
RG_BYPASS_DHCP = (1<<5),
|
||||
RG_BYPASS_DNS = (1<<6),
|
||||
RG_BLOCK_LOCAL = (1<<7),
|
||||
RG_IPv4 = (1<<8),
|
||||
RG_IPv6 = (1<<9),
|
||||
|
||||
RG_DEFAULT = (RG_IPv4),
|
||||
};
|
||||
|
||||
RedirectGatewayFlags() : flags_(RG_DEFAULT) {}
|
||||
|
||||
RedirectGatewayFlags(unsigned int flags) : flags_(flags) {}
|
||||
|
||||
explicit RedirectGatewayFlags(const OptionList& opt)
|
||||
{
|
||||
init(opt);
|
||||
}
|
||||
|
||||
void init(const OptionList& opt)
|
||||
{
|
||||
flags_ = RG_DEFAULT;
|
||||
doinit(opt, "redirect-gateway", true); // DIRECTIVE
|
||||
doinit(opt, "redirect-private", false); // DIRECTIVE
|
||||
}
|
||||
|
||||
unsigned int operator()() const { return flags_; }
|
||||
|
||||
bool redirect_gateway_ipv4_enabled() const
|
||||
{
|
||||
return rg_enabled() && (flags_ & RG_IPv4);
|
||||
}
|
||||
|
||||
bool redirect_gateway_ipv6_enabled() const
|
||||
{
|
||||
return rg_enabled() && (flags_ & RG_IPv6);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
ret += "[ ";
|
||||
if (flags_ & RG_ENABLE)
|
||||
ret += "ENABLE ";
|
||||
if (flags_ & RG_REROUTE_GW)
|
||||
ret += "REROUTE_GW ";
|
||||
if (flags_ & RG_LOCAL)
|
||||
ret += "LOCAL ";
|
||||
if (flags_ & RG_AUTO_LOCAL)
|
||||
ret += "AUTO_LOCAL ";
|
||||
if (flags_ & RG_DEF1)
|
||||
ret += "DEF1 ";
|
||||
if (flags_ & RG_BYPASS_DHCP)
|
||||
ret += "BYPASS_DHCP ";
|
||||
if (flags_ & RG_BYPASS_DNS)
|
||||
ret += "BYPASS_DNS ";
|
||||
if (flags_ & RG_BLOCK_LOCAL)
|
||||
ret += "BLOCK_LOCAL ";
|
||||
if (flags_ & RG_IPv4)
|
||||
ret += "IPv4 ";
|
||||
if (flags_ & RG_IPv6)
|
||||
ret += "IPv6 ";
|
||||
ret += "]";
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
bool rg_enabled() const
|
||||
{
|
||||
return (flags_ & (RG_ENABLE|RG_REROUTE_GW)) == (RG_ENABLE|RG_REROUTE_GW);
|
||||
}
|
||||
|
||||
void doinit(const OptionList& opt, const std::string& directive, const bool redirect_gateway)
|
||||
{
|
||||
OptionList::IndexMap::const_iterator rg = opt.map().find(directive);
|
||||
if (rg != opt.map().end())
|
||||
add_flags(opt, rg->second, redirect_gateway);
|
||||
}
|
||||
|
||||
void add_flags(const OptionList& opt, const OptionList::IndexList& idx, const bool redirect_gateway)
|
||||
{
|
||||
flags_ |= RG_ENABLE;
|
||||
if (redirect_gateway)
|
||||
flags_ |= RG_REROUTE_GW;
|
||||
else
|
||||
flags_ &= ~RG_REROUTE_GW;
|
||||
for (OptionList::IndexList::const_iterator i = idx.begin(); i != idx.end(); ++i)
|
||||
{
|
||||
const Option& o = opt[*i];
|
||||
for (size_t j = 1; j < o.size(); ++j)
|
||||
{
|
||||
const std::string& f = o.get(j, 64);
|
||||
if (f == "local")
|
||||
flags_ |= RG_LOCAL;
|
||||
else if (f == "autolocal")
|
||||
flags_ |= RG_AUTO_LOCAL;
|
||||
else if (f == "def1")
|
||||
flags_ |= RG_DEF1;
|
||||
else if (f == "bypass-dhcp")
|
||||
flags_ |= RG_BYPASS_DHCP;
|
||||
else if (f == "bypass-dns")
|
||||
flags_ |= RG_BYPASS_DNS;
|
||||
else if (f == "block-local")
|
||||
flags_ |= RG_BLOCK_LOCAL;
|
||||
else if (f == "ipv4")
|
||||
flags_ |= RG_IPv4;
|
||||
else if (f == "!ipv4")
|
||||
flags_ &= ~RG_IPv4;
|
||||
else if (f == "ipv6")
|
||||
flags_ |= RG_IPv6;
|
||||
else if (f == "!ipv6")
|
||||
flags_ &= ~RG_IPv6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int flags_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// The file should include any platform-specific files necessary
|
||||
// to declare the std::abort() function.
|
||||
|
||||
#ifndef OPENVPN_COMMON_ABORT_H
|
||||
#define OPENVPN_COMMON_ABORT_H
|
||||
|
||||
#include <cstdlib> // defines std::abort()
|
||||
|
||||
#endif // OPENVPN_COMMON_ABORT_H
|
||||
@@ -0,0 +1,215 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ACTION_H
|
||||
#define OPENVPN_COMMON_ACTION_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef HAVE_JSONCPP
|
||||
#include "json/json.h"
|
||||
#endif
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/destruct.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct Action : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Action> Ptr;
|
||||
|
||||
virtual void execute(std::ostream& os) = 0;
|
||||
virtual std::string to_string() const = 0;
|
||||
#ifdef HAVE_JSONCPP
|
||||
virtual Json::Value to_json() const
|
||||
{
|
||||
throw Exception("Action::to_json() virtual method not implemented");
|
||||
}
|
||||
#endif
|
||||
virtual ~Action() {}
|
||||
};
|
||||
|
||||
class ActionList : public std::vector<Action::Ptr>, public DestructorBase
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ActionList> Ptr;
|
||||
|
||||
ActionList()
|
||||
{
|
||||
reserve(16);
|
||||
}
|
||||
|
||||
void add(Action* action)
|
||||
{
|
||||
if (action)
|
||||
emplace_back(action);
|
||||
}
|
||||
|
||||
void add(const Action::Ptr& action)
|
||||
{
|
||||
if (action)
|
||||
push_back(action);
|
||||
}
|
||||
|
||||
void add(const ActionList& other)
|
||||
{
|
||||
insert(end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
bool exists(const Action::Ptr& action) const
|
||||
{
|
||||
if (action)
|
||||
{
|
||||
const std::string cmp = action->to_string();
|
||||
for (auto &a : *this)
|
||||
{
|
||||
if (a->to_string() == cmp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os)
|
||||
{
|
||||
Iter i(size(), reverse_);
|
||||
while (i())
|
||||
{
|
||||
if (is_halt())
|
||||
return;
|
||||
try {
|
||||
(*this)[i.index()]->execute(os);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
os << "action exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void execute_log()
|
||||
{
|
||||
std::ostringstream os;
|
||||
execute(os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
Iter i(size(), reverse_);
|
||||
while (i())
|
||||
{
|
||||
ret += (*this)[i.index()]->to_string();
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void enable_destroy(const bool state)
|
||||
{
|
||||
enable_destroy_ = state;
|
||||
}
|
||||
|
||||
void halt()
|
||||
{
|
||||
halt_ = true;
|
||||
}
|
||||
|
||||
virtual void destroy(std::ostream& os) override // defined by DestructorBase
|
||||
{
|
||||
if (enable_destroy_)
|
||||
{
|
||||
execute(os);
|
||||
enable_destroy_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_halt() const
|
||||
{
|
||||
return halt_;
|
||||
}
|
||||
|
||||
protected:
|
||||
class Iter
|
||||
{
|
||||
public:
|
||||
Iter(size_t size, const bool reverse)
|
||||
{
|
||||
if (reverse)
|
||||
{
|
||||
idx = size;
|
||||
end = -1;
|
||||
delta = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = -1;
|
||||
end = size;
|
||||
delta = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
return (idx += delta) != end;
|
||||
}
|
||||
|
||||
size_t index() const
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t idx;
|
||||
size_t end;
|
||||
size_t delta;
|
||||
};
|
||||
|
||||
bool reverse_ = false;
|
||||
bool enable_destroy_ = false;
|
||||
volatile bool halt_ = false;
|
||||
};
|
||||
|
||||
struct ActionListReversed : public ActionList
|
||||
{
|
||||
ActionListReversed()
|
||||
{
|
||||
reverse_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
struct ActionListFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<ActionListFactory> Ptr;
|
||||
|
||||
virtual ActionList::Ptr new_action_list() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ACTIONTHREAD_H
|
||||
#define OPENVPN_COMMON_ACTIONTHREAD_H
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/log/logthread.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ActionThread : public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ActionThread> Ptr;
|
||||
|
||||
struct Notify
|
||||
{
|
||||
virtual void action_thread_finished(const ActionThread* self, bool status) = 0;
|
||||
};
|
||||
|
||||
ActionThread(asio::io_context& io_context_arg,
|
||||
const ActionList::Ptr& action_list,
|
||||
Notify* completion_handler_arg)
|
||||
: io_context(io_context_arg),
|
||||
thread(nullptr),
|
||||
actions(action_list),
|
||||
completion_handler(completion_handler_arg)
|
||||
{
|
||||
if (actions)
|
||||
thread = new std::thread(&ActionThread::thread_func, this);
|
||||
}
|
||||
|
||||
void stop(const bool halt)
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
if (halt)
|
||||
actions->halt();
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
// Necessary because no guarantee that completion_handler
|
||||
// obj will remain in scope during io_context.post delay.
|
||||
completion_handler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~ActionThread()
|
||||
{
|
||||
stop(true);
|
||||
}
|
||||
|
||||
private:
|
||||
void completion_post(bool status)
|
||||
{
|
||||
Notify* n = completion_handler;
|
||||
completion_handler = nullptr;
|
||||
if (n)
|
||||
n->action_thread_finished(this, status);
|
||||
}
|
||||
|
||||
void thread_func()
|
||||
{
|
||||
Log::Context logctx(logwrap);
|
||||
bool status = false;
|
||||
try {
|
||||
OPENVPN_LOG("START THREAD...");
|
||||
status = actions->execute();
|
||||
OPENVPN_LOG("END THREAD");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("ActionThread Exception: " << e.what());
|
||||
}
|
||||
asio::post(io_context, [self=Ptr(this), status]()
|
||||
{
|
||||
self->completion_post(status);
|
||||
});
|
||||
}
|
||||
|
||||
asio::io_context& io_context;
|
||||
std::thread* thread;
|
||||
ActionList::Ptr actions; // actions to execute in child thread
|
||||
Notify* completion_handler; // completion handler
|
||||
Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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-2016 OpenVPN Technologies, 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 an ARCH_x macro that describes our target architecture
|
||||
|
||||
#ifndef OPENVPN_COMMON_ARCH_H
|
||||
#define OPENVPN_COMMON_ARCH_H
|
||||
|
||||
#if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
|
||||
# define OPENVPN_ARCH_x86_64
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
# define OPENVPN_ARCH_i386
|
||||
#elif defined(__aarch64__) || defined(__arm64__)
|
||||
# define OPENVPN_ARCH_ARM64
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
# define OPENVPN_ARCH_ARM
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ARGV_H
|
||||
#define OPENVPN_COMMON_ARGV_H
|
||||
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Argv : public std::vector<std::string>
|
||||
{
|
||||
public:
|
||||
Argv(const size_t capacity=16)
|
||||
{
|
||||
reserve(capacity);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
bool first = true;
|
||||
for (const auto &s : *this)
|
||||
{
|
||||
if (!first)
|
||||
ret += ' ';
|
||||
ret += s;
|
||||
first = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class ArgvWrapper
|
||||
{
|
||||
public:
|
||||
explicit ArgvWrapper(const std::vector<std::string>& argv)
|
||||
{
|
||||
size_t i;
|
||||
argc = argv.size();
|
||||
cargv = new char *[argc+1];
|
||||
for (i = 0; i < argc; ++i)
|
||||
cargv[i] = string_alloc(argv[i]);
|
||||
cargv[i] = nullptr;
|
||||
}
|
||||
|
||||
ArgvWrapper(ArgvWrapper&& rhs) noexcept
|
||||
{
|
||||
argc = rhs.argc;
|
||||
cargv = rhs.cargv;
|
||||
rhs.argc = 0;
|
||||
rhs.cargv = nullptr;
|
||||
}
|
||||
|
||||
ArgvWrapper& operator=(ArgvWrapper&& rhs) noexcept
|
||||
{
|
||||
del();
|
||||
argc = rhs.argc;
|
||||
cargv = rhs.cargv;
|
||||
rhs.argc = 0;
|
||||
rhs.cargv = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ArgvWrapper()
|
||||
{
|
||||
del();
|
||||
}
|
||||
|
||||
char *const *c_argv() const noexcept
|
||||
{
|
||||
return cargv;
|
||||
}
|
||||
|
||||
char **c_argv() noexcept
|
||||
{
|
||||
return cargv;
|
||||
}
|
||||
|
||||
size_t c_argc() const noexcept
|
||||
{
|
||||
return argc;
|
||||
}
|
||||
|
||||
private:
|
||||
ArgvWrapper(const ArgvWrapper&) = delete;
|
||||
ArgvWrapper& operator=(const ArgvWrapper&) = delete;
|
||||
|
||||
static char *string_alloc(const std::string& s)
|
||||
{
|
||||
const char *sdata = s.c_str();
|
||||
const size_t slen = s.length();
|
||||
char *ret = new char[slen+1];
|
||||
std::memcpy(ret, sdata, slen);
|
||||
ret[slen] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
void del()
|
||||
{
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
delete [] cargv[i];
|
||||
delete [] cargv;
|
||||
}
|
||||
|
||||
size_t argc;
|
||||
char **cargv;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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-2016 OpenVPN Technologies, 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_COMMON_ARRAYSIZE_H
|
||||
#define OPENVPN_COMMON_ARRAYSIZE_H
|
||||
|
||||
#include <cstddef> // defines size_t
|
||||
|
||||
namespace openvpn {
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t array_size( T (&)[N] ) {
|
||||
return N;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Asio TCP socket that can be configured so that open() method
|
||||
// always prebinds the socket to a given local address. Useful
|
||||
// for TCP clients.
|
||||
|
||||
#ifndef OPENVPN_COMMON_ASIOBOUNDSOCK_H
|
||||
#define OPENVPN_COMMON_ASIOBOUNDSOCK_H
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/common/extern.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace AsioBoundSocket {
|
||||
|
||||
typedef asio::stream_socket_service<asio::ip::tcp> SocketServiceBase;
|
||||
|
||||
struct SocketService : public SocketServiceBase
|
||||
{
|
||||
struct implementation_type : public SocketServiceBase::implementation_type
|
||||
{
|
||||
IP::Addr bind_local_addr;
|
||||
};
|
||||
|
||||
explicit SocketService(asio::io_context& io_context)
|
||||
: SocketServiceBase(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
static asio::detail::service_id<SocketService> id; // register the service
|
||||
|
||||
// Override the open method so we can bind immediately after open.
|
||||
asio::error_code open(implementation_type& impl,
|
||||
const protocol_type& protocol,
|
||||
asio::error_code& ec)
|
||||
{
|
||||
ec = SocketServiceBase::open(impl, protocol, ec);
|
||||
if (ec)
|
||||
return ec;
|
||||
if (impl.bind_local_addr.defined())
|
||||
{
|
||||
ec = set_option(impl, asio::socket_base::reuse_address(true), ec);
|
||||
if (ec)
|
||||
return ec;
|
||||
ec = bind(impl,
|
||||
asio::ip::tcp::endpoint(impl.bind_local_addr.to_asio(), 0), // port 0 -- kernel will choose port
|
||||
ec);
|
||||
}
|
||||
return ec;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
OPENVPN_EXTERN asio::detail::service_id<SocketService> SocketService::id;
|
||||
|
||||
typedef asio::basic_stream_socket<asio::ip::tcp, SocketService> SocketBase;
|
||||
|
||||
struct Socket : public SocketBase
|
||||
{
|
||||
explicit Socket(asio::io_context& io_context)
|
||||
: SocketBase(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
void bind_local(const IP::Addr& addr)
|
||||
{
|
||||
this->get_implementation().bind_local_addr = addr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ASIOCONTEXT_H
|
||||
#define OPENVPN_COMMON_ASIOCONTEXT_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class AsioContextStore
|
||||
{
|
||||
public:
|
||||
asio::io_context& new_context(int concurrency_hint)
|
||||
{
|
||||
asio::io_context* ioc = new asio::io_context(concurrency_hint);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
contexts.emplace_back(ioc);
|
||||
}
|
||||
return *ioc;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::vector<std::unique_ptr<asio::io_context>> contexts;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ASIOERR_H
|
||||
#define OPENVPN_COMMON_ASIOERR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <asio/error_code.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// returns a string describing a asio error code
|
||||
template <typename ErrorCode>
|
||||
inline std::string errinfo(ErrorCode err)
|
||||
{
|
||||
asio::error_code e(err, asio::system_category());
|
||||
return e.message();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Asio polymorphic socket for handling TCP
|
||||
// and unix domain sockets.
|
||||
|
||||
#ifndef OPENVPN_COMMON_ASIOPOLYSOCK_H
|
||||
#define OPENVPN_COMMON_ASIOPOLYSOCK_H
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/function.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/sockopt.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
#include <openvpn/common/peercred.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace AsioPolySock {
|
||||
class Base : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Base> Ptr;
|
||||
|
||||
virtual void async_send(const asio::const_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) = 0;
|
||||
|
||||
virtual void async_receive(const asio::mutable_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) = 0;
|
||||
|
||||
virtual std::string remote_endpoint_str() const = 0;
|
||||
virtual bool remote_ip_port(IP::Addr& addr, unsigned int& port) const = 0;
|
||||
|
||||
virtual void non_blocking(const bool state) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void tcp_nodelay() {}
|
||||
virtual void set_cloexec() {}
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
virtual bool peercreds(SockOpt::Creds& cr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual bool is_open() const = 0;
|
||||
virtual bool is_local() const = 0;
|
||||
|
||||
size_t index() const { return index_; }
|
||||
|
||||
protected:
|
||||
Base(const size_t index)
|
||||
: index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
size_t index_;
|
||||
};
|
||||
|
||||
struct TCP : public Base
|
||||
{
|
||||
typedef RCPtr<TCP> Ptr;
|
||||
|
||||
TCP(asio::io_context& io_context,
|
||||
const size_t index)
|
||||
: Base(index),
|
||||
socket(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void async_send(const asio::const_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
socket.async_send(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual void async_receive(const asio::mutable_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
socket.async_receive(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual std::string remote_endpoint_str() const override
|
||||
{
|
||||
return to_string(socket.remote_endpoint());
|
||||
}
|
||||
|
||||
virtual bool remote_ip_port(IP::Addr& addr, unsigned int& port) const override
|
||||
{
|
||||
try {
|
||||
addr = IP::Addr::from_asio(socket.remote_endpoint().address());
|
||||
port = socket.remote_endpoint().port();
|
||||
return true;
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void non_blocking(const bool state) override
|
||||
{
|
||||
socket.non_blocking(state);
|
||||
}
|
||||
|
||||
virtual void tcp_nodelay() override
|
||||
{
|
||||
socket.set_option(asio::ip::tcp::no_delay(true));
|
||||
}
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
virtual void set_cloexec() override
|
||||
{
|
||||
SockOpt::set_cloexec(socket.native_handle());
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void close() override
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
|
||||
virtual bool is_open() const override
|
||||
{
|
||||
return socket.is_open();
|
||||
}
|
||||
|
||||
virtual bool is_local() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket socket;
|
||||
};
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
struct Unix : public Base
|
||||
{
|
||||
typedef RCPtr<Unix> Ptr;
|
||||
|
||||
Unix(asio::io_context& io_context,
|
||||
const size_t index)
|
||||
: Base(index),
|
||||
socket(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void async_send(const asio::const_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
socket.async_send(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual void async_receive(const asio::mutable_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
socket.async_receive(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual std::string remote_endpoint_str() const override
|
||||
{
|
||||
return "LOCAL";
|
||||
}
|
||||
|
||||
virtual bool remote_ip_port(IP::Addr&, unsigned int&) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void non_blocking(const bool state) override
|
||||
{
|
||||
socket.non_blocking(state);
|
||||
}
|
||||
|
||||
virtual bool peercreds(SockOpt::Creds& cr) override
|
||||
{
|
||||
return SockOpt::peercreds(socket.native_handle(), cr);
|
||||
}
|
||||
|
||||
virtual void set_cloexec() override
|
||||
{
|
||||
SockOpt::set_cloexec(socket.native_handle());
|
||||
}
|
||||
|
||||
virtual void close() override
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
|
||||
virtual bool is_open() const override
|
||||
{
|
||||
return socket.is_open();
|
||||
}
|
||||
|
||||
virtual bool is_local() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
asio::local::stream_protocol::socket socket;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
struct NamedPipe : public Base
|
||||
{
|
||||
typedef RCPtr<NamedPipe> Ptr;
|
||||
|
||||
NamedPipe(asio::windows::stream_handle&& handle_arg,
|
||||
const size_t index)
|
||||
: Base(index),
|
||||
handle(std::move(handle_arg))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void async_send(const asio::const_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
handle.async_write_some(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual void async_receive(const asio::mutable_buffers_1& buf,
|
||||
Function<void(const asio::error_code&, const size_t)>&& callback) override
|
||||
{
|
||||
handle.async_read_some(buf, std::move(callback));
|
||||
}
|
||||
|
||||
virtual std::string remote_endpoint_str() const override
|
||||
{
|
||||
return "NAMED_PIPE";
|
||||
}
|
||||
|
||||
virtual bool remote_ip_port(IP::Addr&, unsigned int&) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void non_blocking(const bool state) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void close() override
|
||||
{
|
||||
handle.close();
|
||||
}
|
||||
|
||||
virtual bool is_open() const override
|
||||
{
|
||||
return handle.is_open();
|
||||
}
|
||||
|
||||
virtual bool is_local() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
asio::windows::stream_handle handle;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// A simple class that allows an arbitrary set of posix signals to be
|
||||
// associated with an Asio handler.
|
||||
|
||||
#ifndef OPENVPN_COMMON_ASIOSIGNAL_H
|
||||
#define OPENVPN_COMMON_ASIOSIGNAL_H
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ASIOSignals : public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ASIOSignals> Ptr;
|
||||
|
||||
ASIOSignals(asio::io_context& io_context)
|
||||
: halt(false), signals_(io_context) {}
|
||||
|
||||
enum {
|
||||
S_SIGINT = (1<<0),
|
||||
S_SIGTERM = (1<<1),
|
||||
#ifndef OPENVPN_PLATFORM_WIN
|
||||
S_SIGQUIT = (1<<2),
|
||||
S_SIGHUP = (1<<3),
|
||||
S_SIGUSR1 = (1<<4),
|
||||
S_SIGUSR2 = (1<<5),
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename SignalHandler>
|
||||
void register_signals(SignalHandler stop_handler, unsigned int sigmask = (S_SIGINT|S_SIGTERM))
|
||||
{
|
||||
if (sigmask & S_SIGINT)
|
||||
signals_.add(SIGINT);
|
||||
if (sigmask & S_SIGTERM)
|
||||
signals_.add(SIGTERM);
|
||||
#ifndef OPENVPN_PLATFORM_WIN
|
||||
if (sigmask & S_SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
if (sigmask & S_SIGHUP)
|
||||
signals_.add(SIGHUP);
|
||||
if (sigmask & S_SIGUSR1)
|
||||
signals_.add(SIGUSR1);
|
||||
if (sigmask & S_SIGUSR2)
|
||||
signals_.add(SIGUSR2);
|
||||
#endif
|
||||
signals_.async_wait(stop_handler);
|
||||
}
|
||||
|
||||
template <typename SignalHandler>
|
||||
void register_signals_all(SignalHandler stop_handler)
|
||||
{
|
||||
register_signals(stop_handler,
|
||||
S_SIGINT
|
||||
| S_SIGTERM
|
||||
#ifndef OPENVPN_PLATFORM_WIN
|
||||
| S_SIGQUIT
|
||||
| S_SIGHUP
|
||||
| S_SIGUSR1
|
||||
| S_SIGUSR2
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
signals_.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool halt;
|
||||
asio::signal_set signals_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ASIOSTOP_H
|
||||
#define OPENVPN_COMMON_ASIOSTOP_H
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <openvpn/common/stop.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class AsioStopScope : public Stop::Scope
|
||||
{
|
||||
public:
|
||||
AsioStopScope(asio::io_context& io_context,
|
||||
Stop* stop,
|
||||
std::function<void()>&& method)
|
||||
: Stop::Scope(stop, post_method(io_context, std::move(method)))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
static std::function<void()> post_method(asio::io_context& io_context, std::function<void()>&& method)
|
||||
{
|
||||
return [&io_context, method=std::move(method)]()
|
||||
{
|
||||
asio::post(io_context, std::move(method));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Interruptible sleep
|
||||
|
||||
#ifndef OPENVPN_COMMON_ASYNCSLEEP_H
|
||||
#define OPENVPN_COMMON_ASYNCSLEEP_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/common/sleep.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// returns false if Stop signal prevented full wait
|
||||
inline bool async_sleep_milliseconds(int milliseconds, Stop* async_stop)
|
||||
{
|
||||
const int milliseconds_per_retry = 250;
|
||||
volatile bool stop = false;
|
||||
|
||||
// allow asynchronous stop
|
||||
Stop::Scope stop_scope(async_stop, [&stop]() {
|
||||
stop = true;
|
||||
});
|
||||
|
||||
while (milliseconds > 0 && !stop)
|
||||
{
|
||||
const int ms = std::min(milliseconds, milliseconds_per_retry);
|
||||
sleep_milliseconds(ms);
|
||||
milliseconds -= ms;
|
||||
}
|
||||
|
||||
return !stop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Automatically reset a target object when
|
||||
// AutoReset goes out of scope.
|
||||
|
||||
#ifndef OPENVPN_COMMON_AUTORESET_H
|
||||
#define OPENVPN_COMMON_AUTORESET_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
class AutoReset
|
||||
{
|
||||
public:
|
||||
AutoReset(T& obj)
|
||||
: obj_(&obj)
|
||||
{}
|
||||
|
||||
~AutoReset()
|
||||
{
|
||||
if (obj_)
|
||||
obj_->reset();
|
||||
}
|
||||
|
||||
void disarm()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* obj_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,231 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// General-purpose base64 encode and decode.
|
||||
|
||||
#ifndef OPENVPN_COMMON_BASE64_H
|
||||
#define OPENVPN_COMMON_BASE64_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring> // for std::memset, std::strlen
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/extern.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Base64 {
|
||||
|
||||
class UCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(const unsigned char *data, size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
unsigned char operator[](const size_t i) const { return data_[i]; }
|
||||
|
||||
private:
|
||||
const unsigned char *data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_bad_map);
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_error);
|
||||
|
||||
// altmap is "+/=" by default
|
||||
Base64(const char *altmap = nullptr)
|
||||
{
|
||||
// build encoding map
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int j = 65;
|
||||
for (i = 0; i < 62; ++i)
|
||||
{
|
||||
enc[i] = j++;
|
||||
if (j == 91)
|
||||
j = 97;
|
||||
else if (j == 123)
|
||||
j = 48;
|
||||
}
|
||||
if (!altmap)
|
||||
altmap = "+/=";
|
||||
enc[62] = altmap[0];
|
||||
enc[63] = altmap[1];
|
||||
equal = altmap[2];
|
||||
}
|
||||
|
||||
// build decoding map
|
||||
{
|
||||
std::memset(dec, 0xFF, 128);
|
||||
for (unsigned int i = 0; i < 64; ++i)
|
||||
{
|
||||
const unsigned char c = enc[i];
|
||||
if (c >= 128)
|
||||
throw base64_bad_map();
|
||||
dec[c] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t decode_size_max(const size_t encode_size)
|
||||
{
|
||||
return encode_size;
|
||||
}
|
||||
|
||||
static size_t encode_size_max(const size_t decode_size)
|
||||
{
|
||||
return decode_size * 4 / 3 + 4;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
std::string encode(const V& data) const
|
||||
{
|
||||
char *s, *p;
|
||||
size_t i;
|
||||
unsigned int c;
|
||||
const size_t size = data.size();
|
||||
|
||||
p = s = new char[encode_size_max(size)];
|
||||
for (i = 0; i < size; ) {
|
||||
c = static_cast<unsigned char>(data[i++]) << 8;
|
||||
if (i < size)
|
||||
c += static_cast<unsigned char>(data[i]);
|
||||
i++;
|
||||
c <<= 8;
|
||||
if (i < size)
|
||||
c += static_cast<unsigned char>(data[i]);
|
||||
i++;
|
||||
p[0] = enc[(c & 0x00fc0000) >> 18];
|
||||
p[1] = enc[(c & 0x0003f000) >> 12];
|
||||
p[2] = enc[(c & 0x00000fc0) >> 6];
|
||||
p[3] = enc[c & 0x0000003f];
|
||||
if (i > size)
|
||||
p[3] = equal;
|
||||
if (i > size + 1)
|
||||
p[2] = equal;
|
||||
p += 4;
|
||||
}
|
||||
*p = '\0';
|
||||
const std::string ret(s);
|
||||
delete [] s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string encode(const unsigned char *data, size_t size) const
|
||||
{
|
||||
return encode(UCharWrap(data, size));
|
||||
}
|
||||
|
||||
std::string decode(const std::string& str) const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length());
|
||||
decode(ret, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void decode(V& dest, const std::string& str) const
|
||||
{
|
||||
for (const char *p = str.c_str(); *p != '\0' && (*p == equal || is_base64_char(*p)); p += 4)
|
||||
{
|
||||
unsigned int marker;
|
||||
const unsigned int val = token_decode(p, marker);
|
||||
dest.push_back((val >> 16) & 0xff);
|
||||
if (marker < 2)
|
||||
dest.push_back((val >> 8) & 0xff);
|
||||
if (marker < 1)
|
||||
dest.push_back(val & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_base64_char(const char c) const
|
||||
{
|
||||
const size_t idx = c;
|
||||
return idx < 128 && dec[idx] != 0xFF;
|
||||
}
|
||||
|
||||
unsigned int decode_base64_char(const char c) const
|
||||
{
|
||||
const size_t idx = c;
|
||||
if (idx >= 128)
|
||||
throw base64_decode_error();
|
||||
const unsigned int v = dec[idx];
|
||||
if (v == 0xFF)
|
||||
throw base64_decode_error();
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned int token_decode(const char *token, unsigned int& marker) const
|
||||
{
|
||||
size_t i;
|
||||
unsigned int val = 0;
|
||||
marker = 0; // number of equal chars seen
|
||||
if (std::strlen(token) < 4)
|
||||
throw base64_decode_error();
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
val <<= 6;
|
||||
if (token[i] == equal)
|
||||
marker++;
|
||||
else if (marker > 0)
|
||||
throw base64_decode_error();
|
||||
else
|
||||
val += decode_base64_char(token[i]);
|
||||
}
|
||||
if (marker > 2)
|
||||
throw base64_decode_error();
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned char enc[64];
|
||||
unsigned char dec[128];
|
||||
unsigned char equal;
|
||||
};
|
||||
|
||||
// provide a static Base64 object
|
||||
|
||||
OPENVPN_EXTERN const Base64* base64; // GLOBAL
|
||||
|
||||
inline void base64_init_static()
|
||||
{
|
||||
if (!base64)
|
||||
base64 = new Base64();
|
||||
}
|
||||
|
||||
inline void base64_uninit_static()
|
||||
{
|
||||
if (base64)
|
||||
{
|
||||
delete base64;
|
||||
base64 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_BINPREFIX_H
|
||||
#define OPENVPN_COMMON_BINPREFIX_H
|
||||
|
||||
#include <algorithm> // for std::min, std::max
|
||||
#include <cstring> // for std::memset, std::memcpy
|
||||
#include <cstdint> // for std::uint32_t, uint64_t
|
||||
|
||||
#include <openvpn/common/socktypes.hpp> // for ntohl
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Return the binary prefix of a big-endian data buffer
|
||||
// as a 32 or 64 bit type.
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix(const unsigned char *data)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8, "size inconsistency");
|
||||
if (sizeof(T) == 8)
|
||||
return (T(ntohl(*(uint32_t *)&data[0])) << 32) | T(ntohl(*(uint32_t *)&data[4]));
|
||||
else // sizeof(T) == 4
|
||||
return T(ntohl(*(uint32_t *)&data[0]));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix(const unsigned char *data, const size_t len)
|
||||
{
|
||||
unsigned char d[sizeof(T)]
|
||||
#ifndef _MSC_VER
|
||||
__attribute__((aligned(sizeof(T))))
|
||||
#endif
|
||||
;
|
||||
const size_t l = std::min(len, sizeof(d));
|
||||
std::memset(d, 0, sizeof(d));
|
||||
std::memcpy(d + sizeof(d) - l, data, l);
|
||||
return bin_prefix<T>(d);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix_floor(const unsigned char *data, const size_t len, const T floor)
|
||||
{
|
||||
return std::max(bin_prefix<T>(data, len), floor);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// A general-purpose circular list collection class.
|
||||
// Used by the OpenVPN anti-replay logic.
|
||||
|
||||
#ifndef OPENVPN_COMMON_CIRC_LIST_H
|
||||
#define OPENVPN_COMMON_CIRC_LIST_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
class CircList
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_reset);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_index);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_const_index);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_push);
|
||||
|
||||
CircList() { init(0); }
|
||||
|
||||
explicit CircList(const size_t capacity) { init(capacity); }
|
||||
|
||||
void init(const size_t capacity)
|
||||
{
|
||||
if (capacity)
|
||||
{
|
||||
data_.reserve(capacity);
|
||||
capacity_ = capacity;
|
||||
reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
head_ = capacity_ = 0;
|
||||
data_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (capacity_)
|
||||
{
|
||||
head_ = capacity_ - 1;
|
||||
data_.clear();
|
||||
}
|
||||
else
|
||||
throw circ_list_reset();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return capacity_ > 0;
|
||||
}
|
||||
|
||||
void push(const T& item)
|
||||
{
|
||||
if (++head_ >= capacity_)
|
||||
head_ = 0;
|
||||
if (head_ < data_.size())
|
||||
data_[head_] = item;
|
||||
else if (head_ == data_.size() && data_.size() < capacity_)
|
||||
data_.push_back(item);
|
||||
else
|
||||
throw circ_list_push(); // could occur if object isn't properly initialized
|
||||
}
|
||||
|
||||
T& operator[](const size_t index)
|
||||
{
|
||||
if (index >= data_.size())
|
||||
throw circ_list_index();
|
||||
else if (index <= head_)
|
||||
return data_[head_-index];
|
||||
else
|
||||
return data_[head_+capacity_-index];
|
||||
}
|
||||
|
||||
const T& operator[](const size_t index) const
|
||||
{
|
||||
if (index >= data_.size())
|
||||
throw circ_list_const_index();
|
||||
else if (index <= head_)
|
||||
return data_[head_-index];
|
||||
else
|
||||
return data_[head_+capacity_-index];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t capacity_;
|
||||
size_t head_;
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_CIRC_LIST_H
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_CLEANUP_H
|
||||
#define OPENVPN_COMMON_CLEANUP_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename F>
|
||||
class CleanupType
|
||||
{
|
||||
public:
|
||||
CleanupType(F&& method) noexcept
|
||||
: clean(std::move(method))
|
||||
{
|
||||
}
|
||||
|
||||
CleanupType(CleanupType&&) = default;
|
||||
|
||||
~CleanupType()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
private:
|
||||
CleanupType(const CleanupType&) = delete;
|
||||
CleanupType& operator=(const CleanupType&) = delete;
|
||||
|
||||
F clean;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
inline CleanupType<F> Cleanup(F&& method) noexcept
|
||||
{
|
||||
return CleanupType<F>(std::move(method));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Linux methods for enumerating the number of cores on machine,
|
||||
// and binding a thread to a particular core.
|
||||
|
||||
#ifndef OPENVPN_COMMON_CORE_H
|
||||
#define OPENVPN_COMMON_CORE_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
#include <unistd.h>
|
||||
#elif defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
inline int n_cores()
|
||||
{
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
|
||||
int count;
|
||||
size_t count_len = sizeof(count);
|
||||
if (::sysctlbyname("hw.logicalcpu", &count, &count_len, NULL, 0) != 0)
|
||||
count = 1;
|
||||
return count;
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
long ret = ::sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (ret <= 0)
|
||||
ret = 1;
|
||||
return ret;
|
||||
#elif defined(OPENVPN_PLATFORM_WIN)
|
||||
SYSTEM_INFO si;
|
||||
::GetSystemInfo(&si);
|
||||
return si.dwNumberOfProcessors;
|
||||
#else
|
||||
#error no implementation for n_cores()
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_COUNT_H
|
||||
#define OPENVPN_COMMON_COUNT_H
|
||||
|
||||
namespace openvpn {
|
||||
typedef long long count_t;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_DAEMON_H
|
||||
#define OPENVPN_COMMON_DAEMON_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/logrotate.hpp>
|
||||
#include <openvpn/common/redir.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_EXCEPTION(daemon_err);
|
||||
|
||||
inline void log_setup(const std::string& log_fn,
|
||||
const bool log_append,
|
||||
const int log_versions,
|
||||
const bool stdin_to_dev_null,
|
||||
const bool combine_out_err)
|
||||
{
|
||||
if (!log_append && log_versions >= 1)
|
||||
log_rotate(log_fn, log_versions);
|
||||
RedirectStd redir(stdin_to_dev_null ? "/dev/null" : "",
|
||||
log_fn,
|
||||
log_append ? RedirectStd::FLAGS_APPEND : RedirectStd::FLAGS_OVERWRITE,
|
||||
RedirectStd::MODE_USER_GROUP,
|
||||
combine_out_err);
|
||||
redir.redirect();
|
||||
}
|
||||
|
||||
inline void daemonize()
|
||||
{
|
||||
if (daemon(1, 1) < 0)
|
||||
throw daemon_err("daemon() failed");
|
||||
}
|
||||
|
||||
inline void daemonize(const std::string& log_fn,
|
||||
const bool log_append,
|
||||
const int log_versions)
|
||||
{
|
||||
log_setup(log_fn, log_append, log_versions, true, true);
|
||||
daemonize();
|
||||
}
|
||||
|
||||
inline void write_pid(const std::string& fn)
|
||||
{
|
||||
write_string(fn, to_string(::getpid()) + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Demangle a C++ name (GCC only)
|
||||
|
||||
#ifndef OPENVPN_COMMON_DEMANGLE_H
|
||||
#define OPENVPN_COMMON_DEMANGLE_H
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
inline std::string cxx_demangle(const char *mangled_name)
|
||||
{
|
||||
int status;
|
||||
std::unique_ptr<char[]> realname;
|
||||
|
||||
realname.reset(abi::__cxa_demangle(mangled_name, 0, 0, &status));
|
||||
if (!status)
|
||||
return std::string(realname.get());
|
||||
else
|
||||
return "DEMANGLE_ERROR";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_DESTRUCT_H
|
||||
#define OPENVPN_COMMON_DESTRUCT_H
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
// used for general-purpose cleanup
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct DestructorBase : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<DestructorBase> Ptr;
|
||||
virtual void destroy(std::ostream& os) = 0;
|
||||
virtual ~DestructorBase() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ENDIAN_H
|
||||
#define OPENVPN_COMMON_ENDIAN_H
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
// test for machine endiannes
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__)
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define OPENVPN_BIG_ENDIAN
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define OPENVPN_LITTLE_ENDIAN
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
#define OPENVPN_LITTLE_ENDIAN // assume that Windows is always little-endian
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace Endian {
|
||||
# ifdef OPENVPN_LITTLE_ENDIAN
|
||||
inline size_t e16(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e16rev(const size_t v)
|
||||
{
|
||||
return 15-v;
|
||||
}
|
||||
inline size_t e4(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e4rev(const size_t v)
|
||||
{
|
||||
return 3-v;
|
||||
}
|
||||
inline size_t e2(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e2rev(const size_t v)
|
||||
{
|
||||
return 1-v;
|
||||
}
|
||||
# elif OPENVPN_BIG_ENDIAN
|
||||
inline size_t e16rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e16(const size_t v)
|
||||
{
|
||||
return 15-v;
|
||||
}
|
||||
inline size_t e4rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e4(const size_t v)
|
||||
{
|
||||
return 3-v;
|
||||
}
|
||||
inline size_t e2rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e2(const size_t v)
|
||||
{
|
||||
return 1-v;
|
||||
}
|
||||
# else
|
||||
# error One of OPENVPN_LITTLE_ENDIAN or OPENVPN_BIG_ENDIAN must be defined
|
||||
# endif
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_ENDIAN_H
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_ENUMDIR_H
|
||||
#define OPENVPN_COMMON_ENUMDIR_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(enum_dir_error);
|
||||
|
||||
inline std::vector<std::string> enum_dir(const std::string& dirname,
|
||||
const size_t size_hint=0,
|
||||
const bool sort=false)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
if (size_hint)
|
||||
ret.reserve(size_hint);
|
||||
unique_ptr_del<DIR> dir(opendir(dirname.c_str()), [](DIR* d) { closedir(d); });
|
||||
if (!dir)
|
||||
throw enum_dir_error(dirname + ": cannot open directory");
|
||||
|
||||
struct dirent *e;
|
||||
while ((e = readdir(dir.get())) != nullptr)
|
||||
{
|
||||
std::string fn(e->d_name);
|
||||
if (fn != "." && fn != "..")
|
||||
ret.push_back(std::move(fn));
|
||||
}
|
||||
|
||||
if (sort)
|
||||
std::sort(ret.begin(), ret.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Basic exception handling. Allow exception classes for specific errors
|
||||
// to be easily defined, and allow exceptions to be thrown with a consise
|
||||
// syntax that allows stringstream concatenation using <<
|
||||
|
||||
#ifndef OPENVPN_COMMON_EXCEPTION_H
|
||||
#define OPENVPN_COMMON_EXCEPTION_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/stringize.hpp> // for OPENVPN_STRINGIZE
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
#ifdef OPENVPN_DEBUG_EXCEPTION
|
||||
// well-known preprocessor hack to get __FILE__:__LINE__ rendered as a string
|
||||
# define OPENVPN_FILE_LINE "/" __FILE__ ":" OPENVPN_STRINGIZE(__LINE__)
|
||||
#else
|
||||
# define OPENVPN_FILE_LINE
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// string exception class, where the exception is described by a std::string
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string& err) noexcept : err_(err) {}
|
||||
Exception(std::string&& err) noexcept : err_(std::move(err)) {}
|
||||
virtual const char* what() const throw() { return err_.c_str(); }
|
||||
const std::string& err() const noexcept { return err_; }
|
||||
virtual ~Exception() throw() {}
|
||||
|
||||
void add_label(const std::string& label)
|
||||
{
|
||||
err_ = label + ": " + err_;
|
||||
}
|
||||
|
||||
void remove_label(const std::string& label)
|
||||
{
|
||||
const std::string head = label + ": ";
|
||||
if (string::starts_with(err_, head))
|
||||
err_ = err_.substr(head.length());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string err_;
|
||||
};
|
||||
|
||||
// define a simple custom exception class with no extra info
|
||||
# define OPENVPN_SIMPLE_EXCEPTION(C) \
|
||||
class C : public std::exception { \
|
||||
public: \
|
||||
virtual const char* what() const throw() { return #C OPENVPN_FILE_LINE; } \
|
||||
}
|
||||
|
||||
// define a simple custom exception class with no extra info that inherits from a custom base
|
||||
# define OPENVPN_SIMPLE_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C() : B(#C OPENVPN_FILE_LINE) {} \
|
||||
virtual const char* what() const throw() { return #C OPENVPN_FILE_LINE; } \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info
|
||||
# define OPENVPN_EXCEPTION(C) \
|
||||
class C : public openvpn::Exception { \
|
||||
public: \
|
||||
C() : openvpn::Exception(#C OPENVPN_FILE_LINE) {} \
|
||||
C(std::string err) : openvpn::Exception(#C OPENVPN_FILE_LINE ": " + err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, but does not emit a tag
|
||||
# define OPENVPN_UNTAGGED_EXCEPTION(C) \
|
||||
class C : public openvpn::Exception { \
|
||||
public: \
|
||||
C(std::string err) : openvpn::Exception(err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, and inherits from a custom base
|
||||
# define OPENVPN_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C() : B(#C OPENVPN_FILE_LINE) {} \
|
||||
C(std::string err) : B(#C OPENVPN_FILE_LINE ": " + err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, and inherits from a custom base,
|
||||
// but does not emit a tag
|
||||
# define OPENVPN_UNTAGGED_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C(std::string err) : B(err) {} \
|
||||
}
|
||||
|
||||
// throw an Exception with stringstream concatenation allowed
|
||||
# define OPENVPN_THROW_EXCEPTION(stuff) \
|
||||
do { \
|
||||
std::ostringstream _ovpn_exc; \
|
||||
_ovpn_exc << stuff; \
|
||||
throw openvpn::Exception(_ovpn_exc.str()); \
|
||||
} while (0)
|
||||
|
||||
// throw an OPENVPN_EXCEPTION class with stringstream concatenation allowed
|
||||
# define OPENVPN_THROW(exc, stuff) \
|
||||
do { \
|
||||
std::ostringstream _ovpn_exc; \
|
||||
_ovpn_exc << stuff; \
|
||||
throw exc(_ovpn_exc.str()); \
|
||||
} while (0)
|
||||
|
||||
// properly rethrow an exception that might be derived from Exception
|
||||
inline void throw_ref(const std::exception& e)
|
||||
{
|
||||
const Exception* ex = dynamic_cast<const Exception*>(&e);
|
||||
if (ex)
|
||||
throw *ex;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_EXCEPTION_H
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_EXTERN_H
|
||||
#define OPENVPN_COMMON_EXTERN_H
|
||||
|
||||
#ifndef OPENVPN_EXTERN
|
||||
#define OPENVPN_EXTERN
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_FFS_H
|
||||
#define OPENVPN_COMMON_FFS_H
|
||||
|
||||
// find_first_set: find the one-based position of the first 1 bit in
|
||||
// a word (scanning from least significant bit to most significant)
|
||||
|
||||
// find_last_set: find the one-based position of the last 1 bit in
|
||||
// a word (scanning from most significant bit to least significant)
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
inline int find_first_set(unsigned int v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return __builtin_ffs(v);
|
||||
}
|
||||
|
||||
inline int find_last_set(unsigned int v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return 32 - __builtin_clz(v);
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
inline int find_first_set(unsigned int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
unsigned int r = 0;
|
||||
_BitScanForward((unsigned long *)&r, x);
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
inline int find_last_set(unsigned int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
unsigned int r = 0;
|
||||
_BitScanReverse((unsigned long *)&r, x);
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#error no find_first_set / find_last_set implementation for this platform
|
||||
#endif
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_FFS_H
|
||||
@@ -0,0 +1,204 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Basic file-handling methods.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FILE_H
|
||||
#define OPENVPN_COMMON_FILE_H
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdint> // for std::uint64_t
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_UNTAGGED_EXCEPTION(file_exception);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, open_file_error);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_too_large);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_is_binary);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_not_utf8);
|
||||
|
||||
// Read text from file via stream approach that doesn't require that we
|
||||
// establish the length of the file in advance.
|
||||
inline std::string read_text_simple(const std::string& filename)
|
||||
{
|
||||
std::ifstream ifs(filename.c_str());
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
const std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Read a file (may be text or binary).
|
||||
inline BufferPtr read_binary(const std::string& filename,
|
||||
const std::uint64_t max_size = 0,
|
||||
const unsigned int buffer_flags = 0)
|
||||
{
|
||||
std::ifstream ifs(filename.c_str(), std::ios::binary);
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
|
||||
// get length of file
|
||||
ifs.seekg (0, std::ios::end);
|
||||
const std::streamsize length = ifs.tellg();
|
||||
if (max_size && std::uint64_t(length) > max_size)
|
||||
OPENVPN_THROW(file_too_large, "file too large [" << length << '/' << max_size << "]: " << filename);
|
||||
ifs.seekg (0, std::ios::beg);
|
||||
|
||||
// allocate buffer
|
||||
BufferPtr b = new BufferAllocated(size_t(length), buffer_flags | BufferAllocated::ARRAY);
|
||||
|
||||
// read data
|
||||
ifs.read((char *)b->data(), length);
|
||||
|
||||
// check for errors
|
||||
if (ifs.gcount() != length)
|
||||
OPENVPN_THROW(open_file_error, "read length inconsistency: " << filename);
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
// Read a file (may be text or binary) without seeking to determine
|
||||
// its length.
|
||||
inline BufferPtr read_binary_linear(const std::string& filename,
|
||||
const std::uint64_t max_size = 0,
|
||||
const size_t block_size = 1024)
|
||||
{
|
||||
std::ifstream ifs(filename.c_str(), std::ios::binary);
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
|
||||
BufferList buflist;
|
||||
std::streamsize total_size = 0;
|
||||
while (true)
|
||||
{
|
||||
BufferPtr b = new BufferAllocated(block_size, 0);
|
||||
ifs.read((char *)b->data(), b->remaining());
|
||||
const std::streamsize size = ifs.gcount();
|
||||
if (size)
|
||||
{
|
||||
b->set_size(size);
|
||||
total_size += size;
|
||||
if (max_size && std::uint64_t(total_size) > max_size)
|
||||
OPENVPN_THROW(file_too_large, "file too large [" << total_size << '/' << max_size << "]: " << filename);
|
||||
buflist.push_back(std::move(b));
|
||||
}
|
||||
if (ifs.eof())
|
||||
break;
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
}
|
||||
return buflist.join();
|
||||
}
|
||||
|
||||
// Read a text file as a std::string, throw error if file is binary
|
||||
inline std::string read_text(const std::string& filename, const std::uint64_t max_size = 0)
|
||||
{
|
||||
BufferPtr bp = read_binary(filename, max_size);
|
||||
if (bp->contains_null())
|
||||
OPENVPN_THROW(file_is_binary, "file is binary: " << filename);
|
||||
return std::string((const char *)bp->c_data(), bp->size());
|
||||
}
|
||||
|
||||
// Read a UTF-8 file as a std::string, throw errors if file is binary or malformed UTF-8
|
||||
inline std::string read_text_utf8(const std::string& filename, const std::uint64_t max_size = 0)
|
||||
{
|
||||
BufferPtr bp = read_binary(filename, max_size);
|
||||
|
||||
// check if binary
|
||||
if (bp->contains_null())
|
||||
OPENVPN_THROW(file_is_binary, "file is binary: " << filename);
|
||||
|
||||
// remove Windows UTF-8 BOM if present
|
||||
if (bp->size() >= 3)
|
||||
{
|
||||
const unsigned char *data = bp->c_data();
|
||||
if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF)
|
||||
bp->advance(3);
|
||||
}
|
||||
|
||||
// verify that file is valid UTF-8
|
||||
if (!Unicode::is_valid_utf8_uchar_buf(bp->c_data(), bp->size()))
|
||||
OPENVPN_THROW(file_not_utf8, "file is not UTF8: " << filename);
|
||||
|
||||
return std::string((const char *)bp->c_data(), bp->size());
|
||||
}
|
||||
|
||||
// Read multi-line string from stdin
|
||||
inline std::string read_stdin()
|
||||
{
|
||||
std::string ret;
|
||||
std::string line;
|
||||
while (std::getline(std::cin, line))
|
||||
{
|
||||
ret += line;
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write binary buffer to file
|
||||
inline void write_binary(const std::string& filename, const Buffer& buf)
|
||||
{
|
||||
std::ofstream ofs(filename.c_str(), std::ios::binary);
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for write: " << filename);
|
||||
ofs.write((const char *)buf.c_data(), buf.size());
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot write: " << filename);
|
||||
}
|
||||
|
||||
// Write binary buffer list to file
|
||||
template <typename BUFLIST>
|
||||
inline void write_binary_list(const std::string& filename, const BUFLIST& buflist)
|
||||
{
|
||||
std::ofstream ofs(filename.c_str(), std::ios::binary);
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for write: " << filename);
|
||||
for (auto &buf : buflist)
|
||||
{
|
||||
ofs.write((const char *)buf->c_data(), buf->size());
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot write: " << filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Write std::string to file
|
||||
inline void write_string(const std::string& filename, const std::string& str)
|
||||
{
|
||||
BufferPtr buf = buf_from_string(str);
|
||||
write_binary(filename, *buf);
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_FILE_H
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Atomic file-handling methods.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FILEATOMIC_H
|
||||
#define OPENVPN_COMMON_FILEATOMIC_H
|
||||
|
||||
#include <stdio.h> // for rename()
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#error atomic file methods not supported on Windows
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
// Atomically write binary buffer to file (relies on
|
||||
// the atomicity of rename())
|
||||
inline void write_binary_atomic(const std::string& filename,
|
||||
const Buffer& buf,
|
||||
RandomAPI& rng)
|
||||
{
|
||||
// generate temporary filename
|
||||
unsigned char data[16];
|
||||
rng.rand_fill(data);
|
||||
const std::string tfn = filename + '.' + render_hex(data, sizeof(data));
|
||||
|
||||
// write to temporary file
|
||||
write_binary(tfn, buf);
|
||||
|
||||
// then move into position
|
||||
if (::rename(tfn.c_str(), filename.c_str()) == -1)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(open_file_error, "error moving '" << tfn << "' -> '" << filename << "' : " << std::strerror(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,361 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_FORMAT_H
|
||||
#define OPENVPN_COMMON_FORMAT_H
|
||||
|
||||
#include <cstddef> // for std::nullptr_t
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Convert an arbitrary argument to a string.
|
||||
|
||||
#ifndef OPENVPN_PLATFORM_ANDROID // Android NDK apparently doesn't support std::to_string : http://stackoverflow.com/questions/22774009/android-ndk-stdto-string-support
|
||||
// numeric types
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
|
||||
inline std::string to_string(T value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
// non-numeric types
|
||||
template <typename T
|
||||
#ifndef OPENVPN_PLATFORM_ANDROID
|
||||
, typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0
|
||||
#endif
|
||||
>
|
||||
inline std::string to_string(const T& value)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << value;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// Concatenate arguments into a string:
|
||||
// print(args...) -- concatenate
|
||||
// prints(args...) -- concatenate but delimit args with space
|
||||
// printd(char delim, args...) -- concatenate but delimit args with delim
|
||||
|
||||
namespace print_detail {
|
||||
template<typename T>
|
||||
inline void print(std::ostream& os, char delim, const T& first)
|
||||
{
|
||||
os << first;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline void print(std::ostream& os, char delim, const T& first, Args... args)
|
||||
{
|
||||
os << first;
|
||||
if (delim)
|
||||
os << delim;
|
||||
print(os, delim, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string printd(char delim, Args... args)
|
||||
{
|
||||
std::ostringstream os;
|
||||
print_detail::print(os, delim, args...);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string print(Args... args)
|
||||
{
|
||||
return printd(0, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string prints(Args... args)
|
||||
{
|
||||
return printd(' ', args...);
|
||||
}
|
||||
|
||||
// String formatting similar to sprintf.
|
||||
// %s formats any argument regardless of type.
|
||||
// %r formats any argument regardless of type and single-quotes it.
|
||||
// %R formats any argument regardless of type and double-quotes it.
|
||||
// %% formats '%'
|
||||
// printfmt(<format_string>, args...)
|
||||
|
||||
namespace print_formatted_detail {
|
||||
template<typename T>
|
||||
class Output {};
|
||||
|
||||
template<>
|
||||
class Output<std::string>
|
||||
{
|
||||
public:
|
||||
Output(const size_t reserve)
|
||||
{
|
||||
if (reserve)
|
||||
str_.reserve(reserve);
|
||||
}
|
||||
|
||||
// numeric types
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
|
||||
void append(T value)
|
||||
{
|
||||
str_ += openvpn::to_string(value);
|
||||
}
|
||||
|
||||
// non-numeric types not specialized below
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
|
||||
void append(const T& value)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << value;
|
||||
str_ += os.str();
|
||||
}
|
||||
|
||||
// specialization for std::string
|
||||
void append(const std::string& value)
|
||||
{
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for const char *
|
||||
void append(const char *value)
|
||||
{
|
||||
if (value)
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for char *
|
||||
void append(char *value)
|
||||
{
|
||||
if (value)
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for char
|
||||
void append(const char c)
|
||||
{
|
||||
str_ += c;
|
||||
}
|
||||
|
||||
// specialization for bool
|
||||
void append(const bool value)
|
||||
{
|
||||
str_ += value ? "true" : "false";
|
||||
}
|
||||
|
||||
// specialization for nullptr
|
||||
void append(std::nullptr_t)
|
||||
{
|
||||
str_ += "nullptr";
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return std::move(str_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str_;
|
||||
};
|
||||
|
||||
template<>
|
||||
class Output<std::ostringstream>
|
||||
{
|
||||
public:
|
||||
Output(const size_t reserve)
|
||||
{
|
||||
// fixme -- figure out how to reserve space in std::ostringstream
|
||||
}
|
||||
|
||||
// general types
|
||||
template <typename T>
|
||||
void append(const T& value)
|
||||
{
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for const char *
|
||||
void append(const char *value)
|
||||
{
|
||||
if (value)
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for char *
|
||||
void append(char *value)
|
||||
{
|
||||
if (value)
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for bool
|
||||
void append(const bool value)
|
||||
{
|
||||
if (value)
|
||||
os_ << "true";
|
||||
else
|
||||
os_ << "false";
|
||||
}
|
||||
|
||||
// specialization for nullptr
|
||||
void append(std::nullptr_t)
|
||||
{
|
||||
os_ << "nullptr";
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return os_.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream os_;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename OUTPUT>
|
||||
class PrintFormatted
|
||||
{
|
||||
public:
|
||||
PrintFormatted(const std::string& fmt_arg, const size_t reserve)
|
||||
: fmt(fmt_arg),
|
||||
fi(fmt.begin()),
|
||||
out(reserve),
|
||||
pct(false)
|
||||
{
|
||||
}
|
||||
|
||||
void process()
|
||||
{
|
||||
process_finish();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void process(const T& last)
|
||||
{
|
||||
process_arg(last);
|
||||
process_finish();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void process(const T& first, Args... args)
|
||||
{
|
||||
process_arg(first);
|
||||
process(args...);
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return out.str();
|
||||
}
|
||||
|
||||
private:
|
||||
PrintFormatted(const PrintFormatted&) = delete;
|
||||
PrintFormatted& operator=(const PrintFormatted&) = delete;
|
||||
|
||||
template<typename T>
|
||||
bool process_arg(const T& arg)
|
||||
{
|
||||
while (fi != fmt.end())
|
||||
{
|
||||
const char c = *fi++;
|
||||
if (pct)
|
||||
{
|
||||
pct = false;
|
||||
const int quote = quote_delim(c);
|
||||
if (quote >= 0)
|
||||
{
|
||||
if (quote)
|
||||
out.append((char)quote);
|
||||
out.append(arg);
|
||||
if (quote)
|
||||
out.append((char)quote);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
out.append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '%')
|
||||
pct = true;
|
||||
else
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void process_finish()
|
||||
{
|
||||
// '?' printed for %s operators that don't match an argument
|
||||
while (process_arg("?"))
|
||||
;
|
||||
}
|
||||
|
||||
static int quote_delim(const char fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case 's':
|
||||
return 0;
|
||||
case 'r':
|
||||
return '\'';
|
||||
case 'R':
|
||||
return '\"';
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& fmt;
|
||||
std::string::const_iterator fi;
|
||||
print_formatted_detail::Output<OUTPUT> out;
|
||||
bool pct;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string printfmt(const std::string& fmt, Args... args)
|
||||
{
|
||||
#ifdef OPENVPN_PLATFORM_ANDROID
|
||||
PrintFormatted<std::ostringstream> pf(fmt, 256);
|
||||
#else
|
||||
PrintFormatted<std::string> pf(fmt, 256);
|
||||
#endif
|
||||
pf.process(args...);
|
||||
return pf.str();
|
||||
}
|
||||
|
||||
# define OPENVPN_FMT(...) OPENVPN_LOG_STRING(printfmt(__VA_ARGS__))
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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-2016 OpenVPN Technologies, 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/>.
|
||||
|
||||
// High-performance functor with move-only semantics.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FUNCTION_H
|
||||
#define OPENVPN_COMMON_FUNCTION_H
|
||||
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
template <typename F>
|
||||
class Function;
|
||||
|
||||
template <typename R, typename ... A>
|
||||
class Function<R(A...)>
|
||||
{
|
||||
public:
|
||||
static constexpr size_t N = 3; // max size of functor in machine words
|
||||
|
||||
template <typename T>
|
||||
Function(T&& functor) noexcept
|
||||
{
|
||||
static_assert(sizeof(Intern<T>) <= sizeof(data), "Functor too large");
|
||||
setup_methods<T>();
|
||||
new (data) Intern<T>(std::move(functor));
|
||||
}
|
||||
|
||||
Function(Function&& f) noexcept
|
||||
{
|
||||
methods = f.methods;
|
||||
methods->move(data, f.data);
|
||||
}
|
||||
|
||||
~Function()
|
||||
{
|
||||
methods->destruct(data);
|
||||
}
|
||||
|
||||
R operator()(A... args)
|
||||
{
|
||||
return methods->invoke(data, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Methods
|
||||
{
|
||||
R (*invoke)(void *, A...);
|
||||
void (*move)(void *, void *);
|
||||
void (*destruct)(void *);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void setup_methods()
|
||||
{
|
||||
static const struct Methods m = {
|
||||
&Intern<T>::invoke,
|
||||
&Intern<T>::move,
|
||||
&Intern<T>::destruct,
|
||||
};
|
||||
methods = &m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Intern
|
||||
{
|
||||
public:
|
||||
Intern(Intern&& obj) noexcept
|
||||
: functor_(std::move(obj.functor_))
|
||||
{
|
||||
}
|
||||
|
||||
Intern(T&& functor) noexcept
|
||||
: functor_(std::move(functor))
|
||||
{
|
||||
}
|
||||
|
||||
static R invoke(void *ptr, A... args)
|
||||
{
|
||||
Intern* self = reinterpret_cast<Intern<T>*>(ptr);
|
||||
return self->functor_(args...);
|
||||
}
|
||||
|
||||
static void move(void *dest, void *src)
|
||||
{
|
||||
Intern* s = reinterpret_cast<Intern<T>*>(src);
|
||||
new (dest) Intern(std::move(*s));
|
||||
}
|
||||
|
||||
static void destruct(void *ptr)
|
||||
{
|
||||
Intern* self = reinterpret_cast<Intern<T>*>(ptr);
|
||||
self->~Intern();
|
||||
}
|
||||
|
||||
private:
|
||||
T functor_;
|
||||
};
|
||||
|
||||
const Methods* methods;
|
||||
void* data[N];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OPENVPN_COMMON_GETOPT_H
|
||||
#define OPENVPN_COMMON_GETOPT_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <getopt.h>
|
||||
#else
|
||||
|
||||
#include <cstring> // for std::strlen, std::strchr, std::strncmp
|
||||
|
||||
#define GETOPT_BADCH (int)'?'
|
||||
#define GETOPT_BADARG (int)':'
|
||||
#define GETOPT_EMSG ""
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(getopt_assert);
|
||||
OPENVPN_EXCEPTION(getopt_exception);
|
||||
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = 0; /* character checked for validity */
|
||||
int optreset = 0; /* reset getopt */
|
||||
char *optarg = nullptr; /* argument associated with option */
|
||||
|
||||
struct option
|
||||
{
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
enum {
|
||||
no_argument=0,
|
||||
required_argument=1,
|
||||
optional_argument=2
|
||||
};
|
||||
|
||||
namespace getopt_private {
|
||||
inline void error(const char *prefix, int arg)
|
||||
{
|
||||
std::string err = prefix;
|
||||
err += " -- ";
|
||||
err += (char)arg;
|
||||
throw getopt_exception(err);
|
||||
}
|
||||
|
||||
inline void error(const char *prefix, const char *arg)
|
||||
{
|
||||
std::string err = prefix;
|
||||
err += " -- ";
|
||||
err += arg;
|
||||
throw getopt_exception(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
inline int getopt_internal(int nargc, char * const *nargv, const char *ostr)
|
||||
{
|
||||
static char *place = GETOPT_EMSG; /* option letter processing */
|
||||
const char *oli; /* option letter list index */
|
||||
|
||||
if (!nargv || !ostr)
|
||||
throw getopt_assert();
|
||||
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
||||
place = GETOPT_EMSG;
|
||||
return (-1);
|
||||
}
|
||||
if (place[1] && *++place == '-') { /* found "--" */
|
||||
/* ++optind; */
|
||||
place = GETOPT_EMSG;
|
||||
return (-2);
|
||||
}
|
||||
} /* option letter okay? */
|
||||
if ((optopt = (int)*place++) == (int)':' ||
|
||||
!(oli = std::strchr(ostr, optopt))) {
|
||||
/*
|
||||
* if the user didn't specify '-' as an option,
|
||||
* assume it means -1.
|
||||
*/
|
||||
if (optopt == (int)'-')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (opterr && *ostr != ':')
|
||||
getopt_private::error("illegal option", optopt);
|
||||
return (GETOPT_BADCH);
|
||||
}
|
||||
if (*++oli != ':') { /* don't need argument */
|
||||
optarg = nullptr;
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* need an argument */
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (nargc <= ++optind) { /* no arg */
|
||||
place = GETOPT_EMSG;
|
||||
if ((opterr) && (*ostr != ':'))
|
||||
getopt_private::error("option requires an argument", optopt);
|
||||
return (GETOPT_BADARG);
|
||||
} else /* white space */
|
||||
optarg = nargv[optind];
|
||||
place = GETOPT_EMSG;
|
||||
++optind;
|
||||
}
|
||||
return (optopt); /* dump back option letter */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
inline int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *index)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!nargv || !options || !long_options)
|
||||
throw getopt_assert();
|
||||
|
||||
if ((retval = getopt_private::getopt_internal(nargc, nargv, options)) == -2) {
|
||||
char *current_argv = nargv[optind++] + 2;
|
||||
char *has_equal;
|
||||
int i;
|
||||
int current_argv_len;
|
||||
int match = -1;
|
||||
|
||||
if (*current_argv == '\0')
|
||||
return(-1);
|
||||
if ((has_equal = std::strchr(current_argv, '=')) != nullptr) {
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = std::strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
if (std::strncmp(current_argv, long_options[i].name, current_argv_len))
|
||||
continue;
|
||||
|
||||
if (std::strlen(long_options[i].name) == (unsigned)current_argv_len) {
|
||||
match = i;
|
||||
break;
|
||||
}
|
||||
if (match == -1)
|
||||
match = i;
|
||||
}
|
||||
if (match != -1) {
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == nullptr)) {
|
||||
/*
|
||||
* Missing argument, leading :
|
||||
* indicates no error should be generated
|
||||
*/
|
||||
if ((opterr) && (*options != ':'))
|
||||
getopt_private::error("option requires an argument", current_argv);
|
||||
return (GETOPT_BADARG);
|
||||
}
|
||||
} else { /* No matching argument */
|
||||
if ((opterr) && (*options != ':'))
|
||||
getopt_private::error("illegal option", current_argv);
|
||||
return (GETOPT_BADCH);
|
||||
}
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
retval = 0;
|
||||
} else
|
||||
retval = long_options[match].val;
|
||||
if (index)
|
||||
*index = match;
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_GETPW_H
|
||||
#define OPENVPN_COMMON_GETPW_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline std::string get_password(const char *prompt)
|
||||
{
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
char *ret = getpass(prompt);
|
||||
return ret;
|
||||
#else
|
||||
throw Exception("get_password not implemented yet for Windows");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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-2016 OpenVPN Technologies, 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_COMMON_GLOB_H
|
||||
#define OPENVPN_COMMON_GLOB_H
|
||||
|
||||
#include <glob.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace openvpn {
|
||||
class Glob
|
||||
{
|
||||
public:
|
||||
Glob(const std::string& pattern, const int flags)
|
||||
{
|
||||
reset();
|
||||
status_ = ::glob(pattern.c_str(), flags, nullptr, &glob_);
|
||||
}
|
||||
|
||||
int status() const
|
||||
{
|
||||
return status_;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return glob_.gl_pathc;
|
||||
}
|
||||
|
||||
const char* operator[](const size_t i) const
|
||||
{
|
||||
return glob_.gl_pathv[i];
|
||||
}
|
||||
|
||||
~Glob()
|
||||
{
|
||||
::globfree(&glob_);
|
||||
}
|
||||
|
||||
private:
|
||||
void reset()
|
||||
{
|
||||
std::memset(&glob_, 0, sizeof(glob_));
|
||||
status_ = 0;
|
||||
}
|
||||
|
||||
Glob(const Glob&) = delete;
|
||||
Glob& operator=(const Glob&) = delete;
|
||||
|
||||
::glob_t glob_;
|
||||
int status_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user