Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
@@ -0,0 +1,65 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#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
+218
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,109 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Invert a route list. Used to support excluded routes on platforms that
// don't support them natively.
#pragma once
#include <openvpn/common/exception.hpp>
#include <openvpn/addr/route.hpp>
namespace openvpn {
namespace IP {
class AddressSpaceSplitter : public RouteList
{
public:
OPENVPN_EXCEPTION(address_space_splitter);
AddressSpaceSplitter() {}
// NOTE: when passing AddressSpaceSplitter to this constructor, make sure
// to static_cast it to RouteList& so as to avoid matching the
// default copy constructor.
explicit AddressSpaceSplitter(const RouteList& in)
: AddressSpaceSplitter(in, in.version_mask())
{
}
AddressSpaceSplitter(const RouteList& in, const Addr::VersionMask vermask)
{
in.verify_canonical();
if (vermask & Addr::V4_MASK)
descend(in, Route(Addr::from_zero(Addr::V4), 0));
if (vermask & Addr::V6_MASK)
descend(in, Route(Addr::from_zero(Addr::V6), 0));
}
private:
enum Type {
EQUAL,
SUBROUTE,
LEAF,
};
/**
* This method construct a non-overlapping list of routes spanning the address
* space in @param route. The routes are constructed in a way that each
* route in the returned list is smaller or equalto each route in
* parameter @param in
*
* @param route The route we currently are looking at and split if it does
* not meet the requirements
*/
void descend(const RouteList& in, const Route& route)
{
switch (find(in, route))
{
case SUBROUTE:
{
Route r1, r2;
if (route.split(r1, r2))
{
descend(in, r1);
descend(in, r2);
}
else
push_back(route);
break;
}
case EQUAL:
case LEAF:
push_back(route);
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)
type = EQUAL;
else if (route.contains(r))
return SUBROUTE;
}
return type;
}
};
}
}
+992
View File
@@ -0,0 +1,992 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ADDR_IP_H
#define OPENVPN_ADDR_IP_H
#include <string>
#include <cstring> // for std::memset
#include <openvpn/io/io.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))
{
}
#ifndef SWIGPYTHON
// When calling IP:Addr with None as the second parameter, Swig will
// always pick this function and complain about not being able to convert
// a null pointer to a const std::string reference. Hide this function, so
// swig is forced to take the const char* variant of this function instead
Addr(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC)
: Addr(from_string(ipstr, title.c_str(), required_version))
{
}
#endif
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"));
}
#ifndef SWIGPYTHON
void validate_version(const std::string& title, Version required_version) const
{
validate_version(title.c_str(), required_version);
}
#endif
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();
}
#ifndef SWIGPYTHON
static std::string validate(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC)
{
return validate(ipstr, title.c_str(), required_version);
}
#endif
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
{
openvpn_io::error_code ec;
openvpn_io::ip::make_address(ipstr, ec);
return !ec;
}
}
static Addr from_string(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC)
{
openvpn_io::error_code ec;
openvpn_io::ip::address a = openvpn_io::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(IPv4::Addr addr)
{
Addr a;
a.ver = V4;
a.u.v4 = std::move(addr);
return a;
}
static Addr from_ipv6(IPv6::Addr addr)
{
Addr a;
a.ver = V6;
a.u.v6 = std::move(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 = V6;
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);
}
// convert Addr to variable length byte string
void to_byte_string_variable(unsigned char *bytestr) const
{
if (ver == V4)
u.v4.to_byte_string(bytestr);
else if (ver == V6)
u.v6.to_byte_string(bytestr);
else
throw ip_exception("address unspecified");
}
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");
}
// validate the prefix length for the IP version
static bool validate_prefix_len(Version v, const unsigned int prefix_len)
{
if (v == V4)
{
if (prefix_len <= V4_SIZE)
return true;
}
else if (v == V6)
{
if (prefix_len <= V6_SIZE)
return true;
}
return false;
}
// 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 openvpn_io::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 openvpn_io::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");
}
openvpn_io::ip::address to_asio() const
{
switch (ver)
{
case V4:
return openvpn_io::ip::address_v4(u.v4.to_asio());
case V6:
return openvpn_io::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 is_loopback() const
{
switch (ver)
{
case V4:
return u.v4.is_loopback();
case V6:
return u.v6.is_loopback();
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);
}
int version_index() const
{
switch (ver)
{
case V4:
return 0;
case V6:
return 1;
default:
throw ip_exception("version index undefined");
}
}
int family() const
{
switch (ver)
{
case V4:
return AF_INET;
case V6:
return AF_INET6;
default:
return -1;
}
}
bool is_compatible(const Addr& other) const
{
return ver == other.ver;
}
bool is_ipv6() const
{
return ver == V6;
}
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 bytes
unsigned int size_bytes() const
{
return size() / 8;
}
// 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;
}
template <typename HASH>
void hash(HASH& h) const
{
switch (ver)
{
case Addr::V4:
u.v4.hash(h);
break;
case Addr::V6:
u.v6.hash(h);
break;
default:
break;
}
}
#ifdef HAVE_CITYHASH
std::size_t hashval() const
{
HashSizeT h;
hash(h);
return h.value();
}
#endif
#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)
}
}
#ifdef HAVE_CITYHASH
OPENVPN_HASH_METHOD(openvpn::IP::Addr, hashval);
#endif
#endif
+75
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ADDR_IPERR_H
#define OPENVPN_ADDR_IPERR_H
#include <string>
#include <openvpn/io/io.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 openvpn_io::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
+588
View File
@@ -0,0 +1,588 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ADDR_IPV4_H
#define OPENVPN_ADDR_IPV4_H
#include <cstring> // for std::memcpy, std::memset
#include <sstream>
#include <cstdint> // for std::uint32_t
#include <openvpn/io/io.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;
bool defined() const
{
return true;
}
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 unsigned short port=0) const
{
struct sockaddr_in ret;
std::memset(&ret, 0, sizeof(ret));
ret.sin_family = AF_INET;
ret.sin_port = htons(port);
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;
}
void to_byte_string(unsigned char *bytestr) const
{
*(base_type*)bytestr = ntohl(u.addr);
}
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)
{
openvpn_io::error_code ec;
openvpn_io::ip::address_v4 a = openvpn_io::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 openvpn_io::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 openvpn_io::ip::address_v4& asio_addr)
{
Addr ret;
ret.u.addr = (std::uint32_t)asio_addr.to_uint();
return ret;
}
openvpn_io::ip::address_v4 to_asio() const
{
return openvpn_io::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;
}
bool is_loopback() const
{
return (u.addr & 0x7F000000) == 0x7F000000;
}
// 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;
}
template <typename HASH>
void hash(HASH& h) const
{
h(u.addr);
}
#ifdef HAVE_CITYHASH
std::size_t hashval() const
{
HashSizeT h;
hash(h);
return h.value();
}
#endif
#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)
}
}
#ifdef HAVE_CITYHASH
OPENVPN_HASH_METHOD(openvpn::IPv4::Addr, hashval);
#endif
#endif // OPENVPN_ADDR_IPV4_H
+847
View File
@@ -0,0 +1,847 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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 <openvpn/io/io.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 };
bool defined() const
{
return true;
}
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 unsigned short port=0) const
{
struct sockaddr_in6 ret;
std::memset(&ret, 0, sizeof(ret));
ret.sin6_family = AF_INET6;
ret.sin6_port = htons(port);
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)
{
openvpn_io::error_code ec;
openvpn_io::ip::address_v6 a = openvpn_io::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 openvpn_io::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 openvpn_io::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];
}
openvpn_io::ip::address_v6 to_asio() const
{
union ipv6addr addr;
host_to_network_order(&addr, &u);
return openvpn_io::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 is_loopback() const // ::1
{
return u.u64[Endian::e2(1)] == 0 && u.u64[Endian::e2(0)] == 1;
}
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;
}
template <typename HASH>
void hash(HASH& h) const
{
h(u.bytes, sizeof(u.bytes));
}
#ifdef HAVE_CITYHASH
std::size_t hashval() const
{
HashSizeT h;
hash(h);
return h.value();
}
#endif
#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];
openvpn_io::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)
}
}
#ifdef HAVE_CITYHASH
OPENVPN_HASH_METHOD(openvpn::IPv6::Addr, hashval);
#endif
#endif // OPENVPN_ADDR_IPV6_H
+67
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+164
View File
@@ -0,0 +1,164 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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)
{
auto 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)
{
auto 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();
}
// Return number of pool addresses currently in use.
size_t n_free() const
{
return 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)
{
freelist_fill();
if (freelist.empty())
return false;
const ADDR& a = freelist.front();
auto 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)
{
auto 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)
{
auto 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(); }
// Override to refill freelist on demand
virtual void freelist_fill()
{
}
std::string to_string() const
{
std::string ret;
for (const auto& e : map)
{
if (e.second)
{
ret += e.first.to_string();
ret += '\n';
}
}
return ret;
}
private:
std::deque<ADDR> freelist;
std::unordered_map<ADDR, bool> map;
};
typedef PoolType<IP::Addr> Pool;
}
}
#endif
+36
View File
@@ -0,0 +1,36 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
namespace openvpn {
// return ip_addr in brackets if it is IPv6
std::string quote_ip(const std::string& ip_addr)
{
if (ip_addr.find(':') != std::string::npos)
return '[' + ip_addr + ']';
else
return ip_addr;
}
}
@@ -0,0 +1,68 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <openvpn/addr/route.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/random/randapi.hpp>
namespace openvpn {
namespace IP {
inline IPv4::Addr random_addr_v4(RandomAPI& prng)
{
return IPv4::Addr::from_uint32(prng.rand_get<std::uint32_t>());
}
inline IPv6::Addr random_addr_v6(RandomAPI& prng)
{
unsigned char bytes[16];
prng.rand_fill(bytes);
return IPv6::Addr::from_byte_string(bytes);
}
inline Addr random_addr(const Addr::Version v, RandomAPI& prng)
{
switch (v)
{
case Addr::V4:
return Addr::from_ipv4(random_addr_v4(prng));
case Addr::V6:
return Addr::from_ipv6(random_addr_v6(prng));
default:
throw ip_exception("address unspecified");
}
}
// bit positions between templ.prefix_len and prefix_len are randomized
inline Route random_subnet(const Route& templ,
const unsigned int prefix_len,
RandomAPI& prng)
{
if (!templ.is_canonical())
throw Exception("IP::random_subnet: template route not canonical: " + templ.to_string());
return Route(((random_addr(templ.addr.version(), prng) & ~templ.netmask()) | templ.addr)
& Addr::netmask_from_prefix_len(templ.addr.version(), prefix_len),
prefix_len);
}
}
}
+137
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+59
View File
@@ -0,0 +1,59 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+344
View File
@@ -0,0 +1,344 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ADDR_ROUTE_H
#define OPENVPN_ADDR_ROUTE_H
#include <string>
#include <sstream>
#include <vector>
#include <cstdint> // for std::uint32_t
#include <tuple>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/number.hpp>
#include <openvpn/common/to_string.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>
class RouteType
{
public:
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;
}
bool defined() const
{
return addr.defined();
}
IP::Addr::Version version() const
{
return addr.version();
}
IP::Addr::VersionMask version_mask() const
{
return addr.version_mask();
}
RouteType<IPv4::Addr> to_ipv4() const
{
return RouteType<IPv4::Addr>(addr.to_ipv4(), prefix_len);
}
RouteType<IPv6::Addr> to_ipv6() const
{
return RouteType<IPv6::Addr>(addr.to_ipv6(), prefix_len);
}
ADDR netmask() const
{
return netmask_(addr, 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();
}
void verify_canonical() const
{
if (!is_canonical())
throw route_error("route not canonical: " + to_string());
}
bool is_host() const
{
return addr.defined() && prefix_len == addr.size();
}
unsigned int host_bits() const
{
if (prefix_len < addr.size())
return addr.size() - prefix_len;
else
return 0;
}
bool contains(const ADDR& a) const // assumes canonical address/routes
{
if (addr.defined() && version_eq(addr, a))
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 + netmask_(addr, 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 std::tie(prefix_len, addr) == std::tie(other.prefix_len, other.addr);
}
bool operator!=(const RouteType& other) const
{
return std::tie(prefix_len, addr) != std::tie(other.prefix_len, other.addr);
}
bool operator<(const RouteType& other) const
{
return std::tie(prefix_len, addr) < std::tie(other.prefix_len, other.addr);
}
template <typename HASH>
void hash(HASH& h) const
{
addr.hash(h);
h(prefix_len);
}
#ifdef HAVE_CITYHASH
std::size_t hash_value() const
{
HashSizeT h;
hash(h);
return h.value();
}
#endif
private:
static IPv4::Addr netmask_(const IPv4::Addr&, unsigned int prefix_len)
{
return IPv4::Addr::netmask_from_prefix_len(prefix_len);
}
static IPv6::Addr netmask_(const IPv6::Addr&, unsigned int prefix_len)
{
return IPv6::Addr::netmask_from_prefix_len(prefix_len);
}
static IP::Addr netmask_(const IP::Addr& addr, unsigned int prefix_len)
{
return IP::Addr::netmask_from_prefix_len(addr.version(), prefix_len);
}
static bool version_eq(const IPv4::Addr&, const IPv4::Addr&)
{
return true;
}
static bool version_eq(const IPv6::Addr&, const IPv6::Addr&)
{
return true;
}
static bool version_eq(const IP::Addr& a1, const IP::Addr& a2)
{
return a1.version() == a2.version();
}
};
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 (auto &r : *this)
os << r.to_string() << std::endl;
return os.str();
}
IP::Addr::VersionMask version_mask() const
{
IP::Addr::VersionMask mask = 0;
for (auto &r : *this)
mask |= r.version_mask();
return mask;
}
void verify_canonical() const
{
for (auto &r : *this)
r.verify_canonical();
}
template <typename R>
bool contains(const R& c) const
{
for (auto &r : *this)
if (r.contains(c))
return true;
return false;
}
};
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;
}
}
}
#ifdef HAVE_CITYHASH
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
#endif
+459
View File
@@ -0,0 +1,459 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CF_H
#define OPENVPN_APPLECRYPTO_CF_CF_H
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <utility>
#include <exception>
#include <CoreFoundation/CoreFoundation.h>
// 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), GET); \
else \
return cls(); \
}
namespace openvpn {
namespace CF
{
enum Rule {
CREATE, // create rule
GET // get rule
};
template <typename T> struct Type {};
template <typename T>
class Wrap
{
public:
Wrap() : obj_(nullptr) {}
explicit Wrap(T obj, const Rule rule=CREATE)
{
if (rule == GET && 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 Rule rule=CREATE)
{
if (rule == GET && obj)
CFRetain(obj);
if (obj_)
CFRelease(obj_);
obj_ = obj;
}
bool defined() const { return obj_ != nullptr; }
explicit operator bool() const noexcept
{
return defined();
}
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 Rule rule=CREATE)
{
return Wrap(cast(obj), rule);
}
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 create-rule 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) = delete; // prevent use because no way to pass rule parameter
T obj_;
};
// 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, GET);
}
// constructors
inline String string(const char *str)
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8));
}
inline String string(CFStringRef str)
{
return String(str, GET);
}
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 Dict dict(const void **keys, const void **values, CFIndex numValues)
{
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
keys,
values,
numValues,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
}
inline Dict const_dict(MutableDict& mdict)
{
return Dict(mdict(), CF::GET);
}
inline Array const_array(MutableArray& marray)
{
return Array(marray(), CF::GET);
}
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
struct cppstring_error : public std::exception
{
virtual const char* what() const throw()
{
return "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,261 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFHELPER_H
#define OPENVPN_APPLECRYPTO_CF_CFHELPER_H
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/apple/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 {
// essentially a vector of void *, used as source for array and dictionary constructors
typedef BufferAllocatedType<CFTypeRef, thread_unsafe_refcount> SrcList;
inline Array array(const SrcList& values)
{
return array((const void **)values.c_data(), values.size());
}
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 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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFHOST_H
#define OPENVPN_APPLECRYPTO_CF_CFHOST_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Host, host_cast, CFHostRef, CFHostGetTypeID)
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFRUNLOOP_H
#define OPENVPN_APPLECRYPTO_CF_CFRUNLOOP_H
#include <openvpn/apple/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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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/apple/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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#define OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Socket, socket_cast, CFSocketRef, CFSocketGetTypeID)
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#define OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(ReadStream, read_stream_cast, CFReadStreamRef, CFReadStreamGetTypeID)
OPENVPN_CF_WRAP(WriteStream, write_stream_cast, CFWriteStreamRef, CFWriteStreamGetTypeID)
}
}
#endif
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFTIMER_H
#define OPENVPN_APPLECRYPTO_CF_CFTIMER_H
#include <openvpn/apple/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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#include <string>
#include <openvpn/apple/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
+325
View File
@@ -0,0 +1,325 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLE_MACLIFE_H
#define OPENVPN_APPLE_MACLIFE_H
#include <string>
#include <sstream>
#include <thread>
#include <openvpn/log/logthread.hpp>
#include <openvpn/apple/cf/cftimer.hpp>
#include <openvpn/apple/cf/cfhelper.hpp>
#include <openvpn/apple/cf/cfrunloop.hpp>
#include <openvpn/apple/reachable.hpp>
#include <openvpn/client/clilife.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, false),
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::GET);
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()
{
ReachabilityViaInternet r;
return ReachabilityViaInternet::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::Internet)
{
const ReachabilityBase::Status status = rb.vstatus(flags);
state.net_up = (status != ReachabilityInterface::NotReachable);
OPENVPN_LOG("MacLifeCycle NET_STATE " << state.net_up << " status=" << ReachabilityBase::render_status(status) << " flags=" << ReachabilityBase::render_flags(flags));
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 exception: " << 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
+129
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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, &notifyPortRef, callback_static, &notifierObject);
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(&notifierObject);
// 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
+75
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+43
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,468 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
//
// 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/apple/cf/cf.hpp>
#include <openvpn/apple/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;
virtual ~ReachabilityBase() {}
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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLE_SCDYNSTORE_H
#define OPENVPN_APPLE_SCDYNSTORE_H
#include <SystemConfiguration/SCDynamicStore.h>
#include <openvpn/apple/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
+81
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,201 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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/apple/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:
case CryptoAlgs::AES_256_CTR:
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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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/apple/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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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 MbedTLS. 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/apple/cf/cfsec.hpp>
#include <openvpn/apple/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,78 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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";
}
// Return true if algorithm is crypto-strength
virtual bool is_crypto() const
{
return true;
}
// 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,99 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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_ASIO_ASIOBOUNDSOCK_H
#define OPENVPN_ASIO_ASIOBOUNDSOCK_H
#include <openvpn/io/io.hpp>
#include <openvpn/addr/ip.hpp>
#include <openvpn/common/extern.hpp>
#include <openvpn/common/to_string.hpp>
namespace openvpn {
namespace AsioBoundSocket {
typedef openvpn_io::basic_stream_socket<openvpn_io::ip::tcp> SocketBase;
class Socket : public SocketBase
{
public:
explicit Socket(openvpn_io::io_context& io_context)
: SocketBase(io_context)
{
}
// if port 0, kernel will dynamically allocate free port
void bind_local(const IP::Addr& addr, const unsigned short port=0)
{
bind_local_addr = addr;
bind_local_port = port;
}
std::string to_string() const
{
std::string ret;
ret.reserve(64);
if (bind_local_addr.defined())
{
ret += "local=[";
ret += bind_local_addr.to_string();
ret += "]:";
ret += openvpn::to_string(bind_local_port);
}
try {
const std::string re = openvpn::to_string(remote_endpoint());
if (!ret.empty())
ret += ' ';
ret += "remote=";
ret += re;
}
catch (const std::exception& e)
{
}
return ret;
}
protected:
virtual void async_connect_post_open(const protocol_type& protocol, openvpn_io::error_code& ec) override
{
if (bind_local_addr.defined())
{
set_option(openvpn_io::socket_base::reuse_address(true), ec);
if (ec)
return;
bind(openvpn_io::ip::tcp::endpoint(bind_local_addr.to_asio(), bind_local_port), ec);
}
}
private:
IP::Addr bind_local_addr;
unsigned short bind_local_port = 0;
};
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ASIO_ASIOCONTEXT_H
#define OPENVPN_ASIO_ASIOCONTEXT_H
#include <vector>
#include <memory>
#include <mutex>
#include <openvpn/io/io.hpp>
namespace openvpn {
class AsioContextStore
{
public:
openvpn_io::io_context& new_context(int concurrency_hint)
{
openvpn_io::io_context* ioc = new openvpn_io::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<openvpn_io::io_context>> contexts;
};
}
#endif
+41
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ASIO_ASIOERR_H
#define OPENVPN_ASIO_ASIOERR_H
#include <string>
#include <openvpn/io/io.hpp> // was: #include <asio/error_code.hpp>
namespace openvpn {
// returns a string describing an i/o error code
template <typename ErrorCode>
inline std::string errinfo(ErrorCode err)
{
openvpn_io::error_code e(err, openvpn_io::system_category());
return e.message();
}
}
#endif
@@ -0,0 +1,377 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Asio polymorphic socket for handling TCP
// and unix domain sockets.
#ifndef OPENVPN_ASIO_ASIOPOLYSOCK_H
#define OPENVPN_ASIO_ASIOPOLYSOCK_H
#include <openvpn/io/io.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/to_string.hpp>
#include <openvpn/common/sockopt.hpp>
#include <openvpn/addr/ip.hpp>
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
#include <openvpn/asio/alt_routing.hpp>
#elif defined(OPENVPN_POLYSOCK_SUPPORTS_BIND)
#include <openvpn/asio/asioboundsock.hpp>
#endif
#ifdef ASIO_HAS_LOCAL_SOCKETS
#include <openvpn/common/peercred.hpp>
#endif
namespace openvpn {
namespace AsioPolySock {
// for shutdown()
enum ShutdownFlags {
SHUTDOWN_SEND = (1<<0),
SHUTDOWN_RECV = (1<<1),
};
class Base : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<Base> Ptr;
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) = 0;
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::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 shutdown(const unsigned int flags) {}
virtual void tcp_nodelay() {}
virtual void set_cloexec() {}
virtual int native_handle()
{
return -1;
}
#ifdef ASIO_HAS_LOCAL_SOCKETS
virtual bool peercreds(SockOpt::Creds& cr)
{
return false;
}
#endif
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual bool alt_routing_enabled()
{
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(openvpn_io::io_context& io_context,
const size_t index)
: Base(index),
socket(io_context)
{
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_send(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_receive(buf, std::move(callback));
}
#if !defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual std::string remote_endpoint_str() const override
{
try {
return "TCP " + openvpn::to_string(socket.remote_endpoint());
}
catch (const std::exception&)
{
return "TCP";
}
}
#endif
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 (const std::exception&)
{
return false;
}
}
virtual void non_blocking(const bool state) override
{
socket.non_blocking(state);
}
virtual void tcp_nodelay() override
{
socket.set_option(openvpn_io::ip::tcp::no_delay(true));
}
#if !defined(OPENVPN_PLATFORM_WIN)
virtual void set_cloexec() override
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
#endif
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
virtual void close() override
{
socket.close();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_local() const override
{
return false;
}
virtual int native_handle() override
{
return socket.native_handle();
}
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual std::string remote_endpoint_str() const override
{
const char *proto = (socket.alt_routing_enabled() ? "TCP ALT " : "TCP ");
return proto + socket.to_string();
}
virtual bool alt_routing_enabled() override
{
return socket.alt_routing_enabled();
}
AltRouting::Socket socket;
#elif defined(OPENVPN_POLYSOCK_SUPPORTS_BIND)
AsioBoundSocket::Socket socket;
#else
openvpn_io::ip::tcp::socket socket;
#endif
};
#ifdef ASIO_HAS_LOCAL_SOCKETS
struct Unix : public Base
{
typedef RCPtr<Unix> Ptr;
Unix(openvpn_io::io_context& io_context,
const size_t index)
: Base(index),
socket(io_context)
{
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_send(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::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
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
#if !defined(OPENVPN_PLATFORM_MAC)
// shutdown() throws "socket is not connected" exception
// on macos if another side has called close() - this behavior
// breaks communication with agent, and hence disabled
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
#endif
virtual void close() override
{
socket.close();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_local() const override
{
return true;
}
virtual int native_handle() override
{
return socket.native_handle();
}
openvpn_io::local::stream_protocol::socket socket;
};
#endif
#if defined(OPENVPN_PLATFORM_WIN)
struct NamedPipe : public Base
{
typedef RCPtr<NamedPipe> Ptr;
NamedPipe(openvpn_io::windows::stream_handle&& handle_arg,
const size_t index)
: Base(index),
handle(std::move(handle_arg))
{
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
handle.async_write_some(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::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;
}
openvpn_io::windows::stream_handle handle;
};
#endif
}
}
#endif
@@ -0,0 +1,49 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <openvpn/common/to_string.hpp>
namespace openvpn {
template <class EPRANGE>
inline std::string asio_resolver_results_to_string(const EPRANGE& endpoint_range)
{
std::string ret;
ret.reserve(64);
bool first = true;
for (const auto &i : endpoint_range)
{
if (!first)
ret += ' ';
ret += '[';
ret += openvpn::to_string(i.endpoint().address());
ret += "]:";
ret += openvpn::to_string(i.endpoint().port());
first = false;
}
return ret;
}
}
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// A simple class that allows an arbitrary set of posix signals to be
// associated with an Asio handler.
#ifndef OPENVPN_ASIO_ASIOSIGNAL_H
#define OPENVPN_ASIO_ASIOSIGNAL_H
#include <openvpn/io/io.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(openvpn_io::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_SIGHUP
| S_SIGUSR1
| S_SIGUSR2
#endif
);
}
void cancel()
{
if (!halt)
{
halt = true;
signals_.cancel();
}
}
private:
bool halt;
openvpn_io::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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_ASIO_ASIOSTOP_H
#define OPENVPN_ASIO_ASIOSTOP_H
#include <openvpn/io/io.hpp>
#include <openvpn/common/stop.hpp>
namespace openvpn {
class AsioStopScope : public Stop::Scope
{
public:
AsioStopScope(openvpn_io::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(openvpn_io::io_context& io_context, std::function<void()>&& method)
{
return [&io_context, method=std::move(method)]()
{
openvpn_io::post(io_context, std::move(method));
};
}
};
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// A null Asio unit of work, that prevents the Asio event loop from
// exiting.
#ifndef OPENVPN_ASIO_ASIOWORK_H
#define OPENVPN_ASIO_ASIOWORK_H
#include <openvpn/io/io.hpp>
namespace openvpn {
class AsioWork
{
public:
AsioWork(openvpn_io::io_context& io_context)
: work(openvpn_io::make_work_guard(io_context))
{
}
private:
openvpn_io::executor_work_guard<openvpn_io::io_context::executor_type> work;
};
}
#endif
@@ -0,0 +1,108 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// A scoped Asio stream that is automatically closed by its destructor.
#ifndef OPENVPN_ASIO_SCOPED_ASIO_STREAM_H
#define OPENVPN_ASIO_SCOPED_ASIO_STREAM_H
#include <openvpn/common/size.hpp>
namespace openvpn {
template <typename STREAM>
class ScopedAsioStream
{
ScopedAsioStream(const ScopedAsioStream&) = delete;
ScopedAsioStream& operator=(const ScopedAsioStream&) = delete;
public:
typedef STREAM* base_type;
ScopedAsioStream() : obj_(undefined()) {}
explicit ScopedAsioStream(STREAM *obj)
: obj_(obj) {}
static STREAM* undefined() { return nullptr; }
STREAM* release()
{
STREAM* ret = obj_;
obj_ = nullptr;
//OPENVPN_LOG("**** SAS RELEASE=" << ret);
return ret;
}
static bool defined_static(STREAM* obj)
{
return obj != nullptr;
}
bool defined() const
{
return defined_static(obj_);
}
STREAM* operator()() const
{
return obj_;
}
void reset(STREAM* obj)
{
close();
obj_ = obj;
//OPENVPN_LOG("**** SAS RESET=" << obj_);
}
// unusual semantics: replace obj without closing it first
void replace(STREAM* obj)
{
//OPENVPN_LOG("**** SAS REPLACE " << obj_ << " -> " << obj);
obj_ = obj;
}
// return false if close error
bool close()
{
if (defined())
{
//OPENVPN_LOG("**** SAS CLOSE obj=" << obj_);
delete obj_;
obj_ = nullptr;
}
return true;
}
~ScopedAsioStream()
{
//OPENVPN_LOG("**** SAS DESTRUCTOR");
close();
}
private:
STREAM* obj_;
};
} // namespace openvpn
#endif
+284
View File
@@ -0,0 +1,284 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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/to_string.hpp>
#include <openvpn/pki/x509track.hpp>
#include <openvpn/ssl/sni_metadata.hpp>
namespace openvpn {
class OpenSSLContext;
class MbedTLSContext;
class AuthCert : public RC<thread_unsafe_refcount>
{
public:
// AuthCert needs to friend SSL implementation classes
friend class OpenSSLContext;
friend class MbedTLSContext;
typedef RCPtr<AuthCert> Ptr;
class Fail
{
public:
// Ordered by severity. If many errors are present, the
// most severe error will be returned by get_code().
enum Type {
OK=0, // OK MUST be 0
EXPIRED, // less severe...
BAD_CERT_TYPE,
CERT_FAIL,
SNI_ERROR, // more severe...
N
};
void add_fail(const size_t depth, const Type new_code, std::string reason)
{
if (new_code > code)
code = new_code;
while (errors.size() <= depth)
errors.emplace_back();
std::string& err = errors[depth];
if (err.empty())
err = std::move(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 std::string render_code(const Type code)
{
switch (code)
{
case OK:
return "OK";
case CERT_FAIL:
default:
return "CERT_FAIL";
case BAD_CERT_TYPE:
return "BAD_CERT_TYPE";
case EXPIRED:
return "EXPIRED";
case SNI_ERROR:
return "SNI_ERROR";
}
}
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 sni_defined() const
{
return !sni.empty();
}
bool cn_defined() const
{
return !cn.empty();
}
bool is_uninitialized() const
{
return cn.empty() && sn < 0 && !fail;
}
template <typename T>
T issuer_fp_prefix() const
{
return bin_prefix<T>(issuer_fp);
}
bool operator==(const AuthCert& other) const
{
return sni == other.sni && 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;
if (!sni.empty())
os << "SNI=" << sni << ' ';
if (sni_metadata)
os << "SNI_CN=" << sni_metadata->sni_client_name(*this) << ' ';
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;
}
// Allow sni_metadata object, if it exists, to generate the client name.
// Otherwise fall back to normalize_cn().
std::string sni_client_name() const
{
if (sni_metadata)
return sni_metadata->sni_client_name(*this);
else
return normalize_cn();
}
const std::string& get_sni() const
{
return sni;
}
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, std::string reason)
{
if (!fail)
fail.reset(new Fail());
fail->add_fail(depth, new_code, std::move(reason));
}
bool is_fail() const
{
return fail && fail->is_fail();
}
const Fail* get_fail() const
{
return fail.get();
}
std::string fail_str() const
{
if (fail)
return fail->to_string(true);
else
return "OK";
}
private:
std::string sni; // SNI (server name indication)
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;
SNI::Metadata::UPtr sni_metadata;
};
}
#endif
@@ -0,0 +1,92 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#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 bool strict) const
{
return ValidateCreds::is_valid(ValidateCreds::USERNAME, username, strict)
&& ValidateCreds::is_valid(ValidateCreds::PASSWORD, password, strict);
}
bool is_valid(const bool strict) const
{
return defined() && is_valid_user_pass(strict);
}
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.length() << " chars)" << 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
+228
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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,71 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_AUTH_VALIDATE_CREDS_H
#define OPENVPN_AUTH_VALIDATE_CREDS_H
#include <openvpn/common/unicode.hpp>
namespace openvpn {
// Validate authentication credential.
// Must be UTF-8.
// Other checks on size and content below.
// We don't check that the credential is non-empty.
namespace ValidateCreds {
enum Type {
USERNAME,
PASSWORD,
RESPONSE
};
template <typename STRING>
static bool is_valid(const Type type, const STRING& cred, const bool strict)
{
size_t max_len_flags;
if (strict)
{
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
}
else
{
switch (type)
{
case USERNAME:
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
break;
case PASSWORD:
case RESPONSE:
// length <= 16384 unicode chars
max_len_flags = 16384;
break;
default:
return false;
}
}
return Unicode::is_valid_utf8(cred, max_len_flags);
}
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_BUFFER_ASIOBUF_H
#define OPENVPN_BUFFER_ASIOBUF_H
#include <openvpn/io/io.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 openvpn_io::const_buffer value_type;
typedef const openvpn_io::const_buffer* const_iterator;
const openvpn_io::const_buffer* begin() const { return buf; }
const openvpn_io::const_buffer* end() const { return buf + 2; }
const size_t size() const
{
return openvpn_io::buffer_size(buf[0])
+ openvpn_io::buffer_size(buf[1]);
}
private:
const openvpn_io::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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+911
View File
@@ -0,0 +1,911 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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, std::remove_const
#ifndef OPENVPN_NO_IO
#include <openvpn/io/io.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_offset,
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_offset:
return "buffer_offset";
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, typename R>
class BufferAllocatedType;
template <typename T>
class BufferType {
template <typename, typename> friend class BufferAllocatedType;
public:
typedef T value_type;
typedef T* type;
typedef const T* const_type;
typedef typename std::remove_const<T>::type NCT; // non-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_offset(const size_t offset)
{
const size_t size = size_ + offset_ - offset;
if (offset > capacity_ || size > capacity_ || offset + size > capacity_)
OPENVPN_BUFFER_THROW(buffer_offset);
offset_ = offset;
size_ = size;
}
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_buffer_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;
}
bool is_zeroed() const
{
const T* end = c_data_end();
for (const T* p = c_data(); p < end; ++p)
{
if (*p)
return false;
}
return true;
}
// 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 OPENVPN_NO_IO
// return a openvpn_io::mutable_buffer object used by
// asio read methods, starting from data()
openvpn_io::mutable_buffer mutable_buffer(const size_t tailroom = 0)
{
return openvpn_io::mutable_buffer(data(), max_size_tailroom(tailroom));
}
// return a openvpn_io::mutable_buffer object used by
// asio read methods, starting from data_end()
openvpn_io::mutable_buffer mutable_buffer_append(const size_t tailroom = 0)
{
return openvpn_io::mutable_buffer(data_end(), remaining(tailroom));
}
// return a openvpn_io::const_buffer object used by
// asio write methods.
openvpn_io::const_buffer const_buffer() const
{
return openvpn_io::const_buffer(c_data(), size());
}
// clamped versions of mutable_buffer(), mutable_buffer_append(),
// and const_buffer()
openvpn_io::mutable_buffer mutable_buffer_clamp(const size_t tailroom = 0)
{
return openvpn_io::mutable_buffer(data(), buf_clamp_read(max_size_tailroom(tailroom)));
}
openvpn_io::mutable_buffer mutable_buffer_append_clamp(const size_t tailroom = 0)
{
return openvpn_io::mutable_buffer(data_end(), buf_clamp_read(remaining(tailroom)));
}
openvpn_io::const_buffer const_buffer_clamp() const
{
return openvpn_io::const_buffer(c_data(), buf_clamp_write(size()));
}
openvpn_io::const_buffer const_buffer_limit(const size_t limit) const
{
return openvpn_io::const_buffer(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 write(const void* data, const size_t size)
{
write((const T*)data, size);
}
void prepend(const T* data, const size_t size)
{
std::memcpy(prepend_alloc(size), data, size * sizeof(T));
}
void prepend(const void* data, const size_t size)
{
prepend((const T*)data, size);
}
void read(NCT* data, const size_t size)
{
std::memcpy(data, read_alloc(size), size * sizeof(T));
}
void read(void* data, const size_t size)
{
read((NCT*)data, size);
}
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);
}
BufferType read_alloc_buf(const size_t size)
{
if (size <= size_)
{
BufferType ret(data_, offset_, size, capacity_);
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);
}
template <typename B>
void append(const B& 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>
class BufferAllocatedType : public BufferType<T>, public RC<R>
{
using BufferType<T>::data_;
using BufferType<T>::offset_;
using BufferType<T>::size_;
using BufferType<T>::capacity_;
template <typename, typename> friend class BufferAllocatedType;
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 T_>
BufferAllocatedType(const BufferType<T_>& other, const unsigned int flags)
{
static_assert(sizeof(T) == sizeof(T_), "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.data_ + offset_, 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);
}
template <typename T_, typename R_>
void move(BufferAllocatedType<T_, R_>& other)
{
if (data_)
delete_(data_, capacity_, flags_);
move_(other);
}
RCPtr<BufferAllocatedType<T, R>> move_to_ptr()
{
RCPtr<BufferAllocatedType<T, R>> bp = new BufferAllocatedType<T, R>();
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_);
}
template <typename T_, typename R_>
BufferAllocatedType(BufferAllocatedType<T_, R_>&& 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;
}
template <typename T_, typename R_>
void move_(BufferAllocatedType<T_, R_>& 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_;
};
// specializations of BufferType for unsigned char
typedef BufferType<unsigned char> Buffer;
typedef BufferType<const unsigned char> ConstBuffer;
typedef BufferAllocatedType<unsigned char, thread_unsafe_refcount> BufferAllocated;
typedef RCPtr<BufferAllocated> BufferPtr;
// BufferAllocated with thread-safe refcount
typedef BufferAllocatedType<unsigned char, thread_safe_refcount> BufferAllocatedTS;
typedef RCPtr<BufferAllocatedTS> BufferPtrTS;
// cast BufferType<T> to BufferType<const T>
template <typename T>
inline BufferType<const T>& const_buffer_ref(BufferType<T>& src)
{
return (BufferType<const T>&)src;
}
template <typename T>
inline const BufferType<const T>& const_buffer_ref(const BufferType<T>& src)
{
return (const 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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,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-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
// Iterate over the lines in a buffer by returning
// a sub-buffer for each line. Zero-copy.
class BufferLineIterator
{
public:
BufferLineIterator(const ConstBuffer& buf)
: src(buf)
{
}
// Returns a zero-length buffer at end of iteration
ConstBuffer next()
{
return src.read_alloc_buf(line_len());
}
private:
size_t line_len() const
{
const unsigned char *const data = src.c_data();
size_t i = 0;
while (i < src.size())
if (data[i++] == '\n')
break;
return i;
}
ConstBuffer src;
};
}
+122
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_BUFFER_BUFREAD_H
#define OPENVPN_BUFFER_BUFREAD_H
#include <unistd.h>
#include <errno.h>
#include <string>
#include <cstring>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/strerror.hpp>
#include <openvpn/buffer/buflist.hpp>
namespace openvpn {
OPENVPN_EXCEPTION(buf_read_error);
inline bool buf_read(const int fd, Buffer& buf, const std::string& title)
{
const ssize_t status = ::read(fd, buf.data_end(), buf.remaining(0));
if (status < 0)
{
const int eno = errno;
OPENVPN_THROW(buf_read_error, "on " << title << " : " << strerror_str(eno));
}
else if (!status)
return false;
buf.inc_size(status);
return true;
}
inline BufferList buf_read(const int fd, const std::string& title)
{
BufferList buflist;
while (true)
{
BufferAllocated buf(1024, 0);
if (!buf_read(fd, buf, title))
break;
buflist.put_consume(buf);
}
return buflist;
}
}
#endif
@@ -0,0 +1,45 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
// constant-length Buffer for writing that cannot be extended
template <std::size_t N>
class StaticBuffer : public Buffer
{
public:
StaticBuffer()
: Buffer(data, N, false)
{
}
StaticBuffer(const StaticBuffer&) = delete;
StaticBuffer& operator=(const StaticBuffer&) = delete;
private:
unsigned char data[N];
};
}
+110
View File
@@ -0,0 +1,110 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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));
}
// Note: ConstBuffer deep links to str, so returned ConstBuffer
// is only defined while str is in scope.
inline ConstBuffer const_buf_from_string(const std::string& str)
{
return ConstBuffer((const unsigned char *)str.c_str(), str.size(), true);
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+96
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint> // for std::uint32_t, uint64_t, etc.
#include <lz4.h>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/socktypes.hpp> // for ntohl/htonl
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
namespace LZ4 {
OPENVPN_EXCEPTION(lz4_error);
inline BufferPtr compress(const ConstBuffer& src,
const size_t headroom,
const size_t tailroom)
{
// sanity check
if (src.size() > LZ4_MAX_INPUT_SIZE)
OPENVPN_THROW(lz4_error, "compress buffer size=" << src.size() << " exceeds LZ4_MAX_INPUT_SIZE=" << LZ4_MAX_INPUT_SIZE);
// allocate dest buffer
BufferPtr dest = new BufferAllocated(sizeof(std::uint32_t) + headroom + tailroom + LZ4_COMPRESSBOUND(src.size()), 0);
dest->init_headroom(headroom);
// as a hint to receiver, write the decompressed size
{
const std::uint32_t size = htonl(src.size());
dest->write(&size, sizeof(size));
}
// compress
const int comp_size = ::LZ4_compress_default((const char *)src.c_data(), (char *)dest->data_end(),
(int)src.size(), (int)dest->remaining(tailroom));
if (comp_size <= 0)
OPENVPN_THROW(lz4_error, "LZ4_compress_default returned error status=" << comp_size);
dest->inc_size(comp_size);
return dest;
}
inline BufferPtr decompress(const ConstBuffer& source,
const size_t headroom,
const size_t tailroom,
size_t max_decompressed_size=LZ4_MAX_INPUT_SIZE)
{
// get the decompressed size
ConstBuffer src(source);
if (src.size() < sizeof(std::uint32_t))
OPENVPN_THROW(lz4_error, "decompress buffer size=" << src.size() << " is too small");
std::uint32_t size;
src.read(&size, sizeof(size));
size = ntohl(size);
if (max_decompressed_size > LZ4_MAX_INPUT_SIZE)
max_decompressed_size = LZ4_MAX_INPUT_SIZE;
if (max_decompressed_size && size > max_decompressed_size)
OPENVPN_THROW(lz4_error, "decompress expansion size=" << size << " is too large (must be <= " << max_decompressed_size << ')');
// allocate dest buffer
BufferPtr dest = new BufferAllocated(headroom + tailroom + size, 0);
dest->init_headroom(headroom);
// decompress
const int decomp_size = LZ4_decompress_safe((const char *)src.c_data(), (char *)dest->data(),
(int)src.size(), size);
if (decomp_size <= 0)
OPENVPN_THROW(lz4_error, "LZ4_decompress_safe returned error status=" << decomp_size);
if (decomp_size != size)
OPENVPN_THROW(lz4_error, "decompress size inconsistency expected_size=" << size << " actual_size=" << decomp_size);
dest->inc_size(decomp_size);
return dest;
}
}
}
+96
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+186
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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);
}
size_t length() const
{
return data.size();
}
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
+145
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_BUFFER_ZLIB_H
#define OPENVPN_BUFFER_ZLIB_H
#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 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);
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 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);
return blist.join(headroom, tailroom, true);
}
else
return BufferPtr();
}
}
}
#endif
#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-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_CLIENT_ASYNC_RESOLVE_H
#define OPENVPN_CLIENT_ASYNC_RESOLVE_H
#ifdef USE_ASIO
#include <openvpn/client/async_resolve/asio.hpp>
#else
#include <openvpn/client/async_resolve/generic.hpp>
#endif
// create shortcuts for common templated classes
namespace openvpn {
typedef AsyncResolvable<openvpn_io::ip::udp::resolver> AsyncResolvableUDP;
typedef AsyncResolvable<openvpn_io::ip::tcp::resolver> AsyncResolvableTCP;
}
#endif /* OPENVPN_CLIENT_ASYNC_RESOLVE_H */
@@ -0,0 +1,163 @@
// 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-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H
#define OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H
#include <openvpn/io/io.hpp>
#include <openvpn/asio/asiowork.hpp>
#include <openvpn/common/bigmutex.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/hostport.hpp>
namespace openvpn {
template<typename RESOLVER_TYPE>
class AsyncResolvable
{
private:
typedef RCPtr<AsyncResolvable> Ptr;
class ResolveThread : public RC<thread_safe_refcount>
{
friend class AsyncResolvable<RESOLVER_TYPE>;
private:
typedef RCPtr<ResolveThread> Ptr;
openvpn_io::io_context& io_context;
AsyncResolvable<RESOLVER_TYPE> *parent;
std::atomic<bool> detached{false};
ResolveThread(openvpn_io::io_context &io_context_arg,
AsyncResolvable<RESOLVER_TYPE> *parent_arg,
const std::string& host, const std::string& port)
: io_context(io_context_arg),
parent(parent_arg)
{
std::thread t([self=Ptr(this), host, port]()
{
openvpn_io::io_context io_context(1);
openvpn_io::error_code error;
RESOLVER_TYPE resolver(io_context);
typename RESOLVER_TYPE::results_type results;
results = resolver.resolve(host, port, error);
if (!self->is_detached())
{
self->post_callback(results, error);
}
});
// detach the thread so that the client won't need to wait for
// it to join.
t.detach();
}
void detach()
{
detached.store(true, std::memory_order_relaxed);
parent = nullptr;
}
bool is_detached() const
{
return detached.load(std::memory_order_relaxed);
}
void post_callback(typename RESOLVER_TYPE::results_type results,
openvpn_io::error_code error)
{
openvpn_io::post(io_context, [self=Ptr(this), results, error]()
{
auto parent = self->parent;
if (!self->is_detached() && parent)
{
self->detach();
OPENVPN_ASYNC_HANDLER;
parent->resolve_callback(error, results);
}
});
}
};
openvpn_io::io_context& io_context;
std::unique_ptr<AsioWork> asio_work;
typename ResolveThread::Ptr resolve_thread;
public:
AsyncResolvable(openvpn_io::io_context& io_context_arg)
: io_context(io_context_arg)
{
}
virtual ~AsyncResolvable()
{
async_resolve_cancel();
}
virtual void resolve_callback(const openvpn_io::error_code& error,
typename RESOLVER_TYPE::results_type results) = 0;
// mimic the asynchronous DNS resolution by performing a
// synchronous one in a detached thread.
//
// This strategy has the advantage of allowing the core to
// stop/exit without waiting for the getaddrinfo() (used
// internally) to terminate.
// Note: getaddrinfo() is non-interruptible by design.
//
// In other words, we are re-creating exactly what ASIO would
// normally do in case of async_resolve(), with the difference
// that here we have control over the resolving thread and we
// can easily detach it. Deatching the internal thread created
// by ASIO would not be feasible as it is not exposed.
void async_resolve_name(const std::string& host, const std::string& port)
{
resolve_thread.reset(new ResolveThread(io_context, this, host, port));
}
// there might be nothing else in the main io_context queue
// right now, therefore we use AsioWork to prevent the loop
// from exiting while we perform the DNS resolution in the
// detached thread.
void async_resolve_lock()
{
asio_work.reset(new AsioWork(io_context));
}
// to be called by the child class when the core wants to stop
// and we don't need to wait for the detached thread any longer.
// It simulates a resolve abort
void async_resolve_cancel()
{
if (resolve_thread)
{
resolve_thread->detach();
resolve_thread.reset();
}
asio_work.reset();
}
};
}
#endif /* OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H */
@@ -0,0 +1,79 @@
// 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-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_CLIENT_ASYNC_RESOLVE_GENERIC_H
#define OPENVPN_CLIENT_ASYNC_RESOLVE_GENERIC_H
#include <openvpn/common/bigmutex.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/hostport.hpp>
namespace openvpn {
template<typename RESOLVER_TYPE>
class AsyncResolvable: public virtual RC<thread_unsafe_refcount>
{
private:
typedef RCPtr<AsyncResolvable> Ptr;
openvpn_io::io_context& io_context;
RESOLVER_TYPE resolver;
public:
AsyncResolvable(openvpn_io::io_context& io_context_arg)
: io_context(io_context_arg),
resolver(io_context_arg)
{
}
virtual void resolve_callback(const openvpn_io::error_code& error,
typename RESOLVER_TYPE::results_type results) = 0;
// This implementation assumes that the i/o reactor provides an asynchronous
// DNS resolution routine using its own primitives and that doesn't require
// us to take care of any non-interruptible opration (i.e. getaddrinfo() in
// case of ASIO).
//
// For example, iOS implements aync_resolve using GCD and CFHost. This
// implementation satisfies the constraints mentioned above
void async_resolve_name(const std::string& host, const std::string& port)
{
resolver.async_resolve(host, port, [self=Ptr(this)](const openvpn_io::error_code& error,
typename RESOLVER_TYPE::results_type results)
{
OPENVPN_ASYNC_HANDLER;
self->resolve_callback(error, results);
});
}
// no-op: needed to provide the same class signature of the ASIO version
void async_resolve_lock()
{
}
void async_resolve_cancel()
{
resolver.cancel();
}
};
}
#endif /* OPENVPN_CLIENT_ASYNC_RESOLVE_GENERIC_H */
@@ -0,0 +1,684 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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 <utility>
#include <openvpn/common/bigmutex.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/asio/asiowork.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(openvpn_io::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));
#ifdef OPENVPN_IO_REQUIRES_STOP
io_context.stop();
#endif
}
}
void stop_on_signal(const openvpn_io::error_code& error, int signal_number)
{
stop();
}
// like stop() but may be safely called by another thread
void thread_safe_stop()
{
if (!halt)
openvpn_io::post(io_context, [self=Ptr(this)]()
{
OPENVPN_ASYNC_HANDLER;
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 AsioWork(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_after(Time::Duration::seconds(seconds));
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
self->restart_wait_callback(gen, error);
});
}
}
void thread_safe_pause(const std::string& reason)
{
if (!halt)
openvpn_io::post(io_context, [self=Ptr(this), reason]()
{
OPENVPN_ASYNC_HANDLER;
self->pause(reason);
});
}
void thread_safe_resume()
{
if (!halt)
openvpn_io::post(io_context, [self=Ptr(this)]()
{
OPENVPN_ASYNC_HANDLER;
self->resume();
});
}
void thread_safe_reconnect(int seconds)
{
if (!halt)
openvpn_io::post(io_context, [self=Ptr(this), seconds]()
{
OPENVPN_ASYNC_HANDLER;
self->reconnect(seconds);
});
}
void dont_restart()
{
dont_restart_ = true;
}
void post_cc_msg(const std::string& msg)
{
if (!halt && client)
client->post_cc_msg(msg);
}
void thread_safe_post_cc_msg(std::string msg)
{
if (!halt)
openvpn_io::post(io_context, [self=Ptr(this), msg=std::move(msg)]()
{
OPENVPN_ASYNC_HANDLER;
self->post_cc_msg(msg);
});
}
~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 openvpn_io::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 openvpn_io::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 openvpn_io::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_after(Time::Duration::seconds(conn_timeout));
conn_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
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_ms = 2000)
{
OPENVPN_LOG("Client terminated, restarting in " << delay_ms << " ms...");
server_poll_timer.cancel();
interim_finalize();
client_options->remote_reset_cache_item();
restart_wait_timer.expires_after(Time::Duration::milliseconds(delay_ms));
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
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));
stop();
}
else
{
ClientEvent::Base::Ptr ev = new ClientEvent::AuthFailed(reason);
client_options->events().add_event(std::move(ev));
client_options->stats().error(Error::AUTH_FAILED);
if (client_options->retry_on_auth_failed())
queue_restart(5000);
else
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_REGISTER_RINGS_ERROR:
{
ClientEvent::Base::Ptr ev = new ClientEvent::TunSetupFailed(client->fatal_reason());
client_options->events().add_event(std::move(ev));
client_options->stats().error(Error::TUN_REGISTER_RINGS_ERROR);
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);
queue_restart(5000);
}
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);
// explicit exit notify is sent earlier by
// ClientProto::Session::inactive_callback()
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(5000); // 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(5000);
}
break;
case Error::TUN_HALT:
{
ClientEvent::Base::Ptr ev = new ClientEvent::TunHalt(client->fatal_reason());
client_options->events().add_event(std::move(ev));
client_options->stats().error(Error::TUN_HALT);
stop();
}
break;
case Error::RELAY:
{
ClientEvent::Base::Ptr ev = new ClientEvent::Relay();
client_options->events().add_event(std::move(ev));
client_options->stats().error(Error::RELAY);
transport_factory_relay = client->transport_factory_relay();
queue_restart(0);
}
break;
case Error::RELAY_ERROR:
{
ClientEvent::Base::Ptr ev = new ClientEvent::RelayError(client->fatal_reason());
client_options->events().add_event(std::move(ev));
client_options->stats().error(Error::RELAY_ERROR);
stop();
}
break;
default:
throw client_connect_unhandled_exception();
}
}
}
}
void new_client()
{
++generation;
if (client_options->asio_work_always_on())
asio_work.reset(new AsioWork(io_context));
else
asio_work.reset();
if (client)
{
client->stop(false);
interim_finalize();
}
if (generation > 1 && !transport_factory_relay)
{
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 in cliopt.hpp
Client::Config::Ptr cli_config = client_options->client_config(!transport_factory_relay);
client.reset(new Client(io_context, *cli_config, this)); // build ClientProto::Session from cliproto.hpp
client_finalized = false;
// relay?
if (transport_factory_relay)
{
client->transport_factory_override(std::move(transport_factory_relay));
transport_factory_relay.reset();
}
restart_wait_timer.cancel();
if (client_options->server_poll_timeout_enabled())
{
server_poll_timer.expires_after(client_options->server_poll_timeout());
server_poll_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
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;
openvpn_io::io_context& io_context;
ClientOptions::Ptr client_options;
Client::Ptr client;
TransportClientFactory::Ptr transport_factory_relay;
AsioTimer server_poll_timer;
AsioTimer restart_wait_timer;
AsioTimer conn_timer;
bool conn_timer_pending;
std::unique_ptr<AsioWork> asio_work;
RemoteList::PreResolve::Ptr pre_resolve;
};
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
MAX_SERVER_LIST_SIZE=4096, // maximum server list size, i.e. "setenv SERVER ..."
};
}
}
#endif
@@ -0,0 +1,227 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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;
}
// If we have a saved password that is not a session ID,
// restore it and wipe any existing session ID.
bool can_retry_auth_with_cached_password()
{
if (password_save_defined)
{
password = password_save;
password_save.clear();
password_save_defined = false;
did_replace_password_with_session_id = false;
return true;
}
else
return false;
}
void purge_session_id()
{
if (!can_retry_auth_with_cached_password())
{
password.clear();
did_replace_password_with_session_id = 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,177 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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/addrspacesplit.hpp>
namespace openvpn {
class EmulateExcludeRouteImpl : public EmulateExcludeRoute
{
public:
OPENVPN_EXCEPTION(emulate_exclude_route_error);
typedef RCPtr<EmulateExcludeRouteImpl> Ptr;
explicit EmulateExcludeRouteImpl(const bool exclude_server_address)
: exclude_server_address_(exclude_server_address)
{
}
private:
void add_route(const bool add, const IP::Addr& addr, const int prefix_len) override
{
(add ? include : exclude).emplace_back(addr, prefix_len);
}
void add_default_routes(bool ipv4, bool ipv6) override
{
if (ipv4)
add_route(true, IP::Addr::from_zero(IP::Addr::V4), 0);
if (ipv6)
add_route(true, IP::Addr::from_zero(IP::Addr::V6), 0);
}
bool enabled(const IPVerFlags& ipv) const override
{
return exclude.size() && (ipv.rgv4() || ipv.rgv6());
}
void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const override
{
const unsigned int ip_ver_flags = ipv.ip_ver_flags();
IP::RouteList rl, tempExcludeList;
rl.reserve(include.size() + exclude.size());
rl.insert(rl.end(), include.begin(), include.end());
rl.insert(rl.end(), exclude.begin(), exclude.end());
// Check if we have to exclude the server, if yes we temporarily add it to the list
// of excluded networks as small individual /32 or /128 network
const IP::RouteList* excludedRoutes = &exclude;
if (exclude_server_address_ && (server_addr.version_mask() & ip_ver_flags) &&
!exclude.contains(IP::Route(server_addr, server_addr.size())))
{
rl.emplace_back(server_addr, server_addr.size());
// Create a temporary list that includes all the routes + the server
tempExcludeList = exclude;
tempExcludeList.emplace_back(server_addr, server_addr.size());
excludedRoutes = &tempExcludeList;
}
if (excludedRoutes->empty())
{
// Samsung's Android VPN API does different things if you have
// 0.0.0.0/0 in the list of installed routes
// (even if 0.0.0.0/1 and 128.0.0.0/1 and are present it behaves different)
// We normally always split the address space, breaking a 0.0.0.0/0 into
// smaller routes. If no routes are excluded, we install the original
// routes without modifying them
for (const auto& rt: include)
{
if (rt.version() & ip_ver_flags)
{
if (!tb->tun_builder_add_route(rt.addr.to_string(), rt.prefix_len, -1, rt.addr.version() == IP::Addr::V6))
throw emulate_exclude_route_error("tun_builder_add_route failed");
}
}
return;
}
// Complete address space (0.0.0.0/0 or ::/0) split into smaller networks
// Figure out which parts of this non overlapping address we want to install
for (const auto& r: IP::AddressSpaceSplitter(rl, ip_ver_flags))
{
if (check_route_should_be_installed(r, *excludedRoutes))
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();
}
bool check_route_should_be_installed(const IP::Route& r, const IP::RouteList & excludedRoutes) const
{
// The whole address space was partioned into NON-overlapping routes that
// we get one by one with the parameter r.
// Therefore we already know that the whole route r either is included or
// excluded IPs.
// Figure out if this particular route should be installed or not
IP::Route const* bestroute = nullptr;
// Get the best (longest-prefix/smallest) route from included routes that completely
// matches this route
for (const auto& incRoute: include)
{
if (incRoute.contains(r))
{
if (!bestroute || bestroute->prefix_len < incRoute.prefix_len)
bestroute = &incRoute;
}
}
// No positive route matches the route at all, do not install it
if (!bestroute)
return false;
// Check if there is a more specific exclude route
for (const auto& exclRoute: excludedRoutes)
{
if (exclRoute.contains(r) && exclRoute.prefix_len > bestroute->prefix_len)
return false;
}
return true;
}
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,486 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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,
AUTH_PENDING,
RESOLVE,
WAIT,
WAIT_PROXY,
CONNECTING,
GET_CONFIG,
ASSIGN_IP,
ADD_ROUTES,
ECHO_OPT,
INFO,
WARN,
PAUSE,
RESUME,
RELAY,
COMPRESSION_ENABLED,
UNSUPPORTED_FEATURE,
// 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,
CLIENT_SETUP,
TUN_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.
RELAY_ERROR,
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",
"AUTH_PENDING",
"RESOLVE",
"WAIT",
"WAIT_PROXY",
"CONNECTING",
"GET_CONFIG",
"ASSIGN_IP",
"ADD_ROUTES",
"ECHO",
"INFO",
"WARN",
"PAUSE",
"RESUME",
"RELAY",
"COMPRESSION_ENABLED",
"UNSUPPORTED_FEATURE",
// nonfatal errors
"TRANSPORT_ERROR",
"TUN_ERROR",
"CLIENT_RESTART",
// fatal errors
"AUTH_FAILED",
"CERT_VERIFY_FAIL",
"TLS_VERSION_MIN",
"CLIENT_HALT",
"CLIENT_SETUP",
"TUN_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",
"RELAY_ERROR",
};
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 AuthPending : public Base
{
AuthPending() : Base(AUTH_PENDING) {}
};
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 Relay : public Base
{
Relay() : Base(RELAY) {}
};
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 UnsupportedFeature : public Base
{
typedef RCPtr<UnsupportedFeature> Ptr;
UnsupportedFeature(const std::string& name_arg, const std::string& reason_arg, bool critical_arg)
: Base(UNSUPPORTED_FEATURE),
name(name_arg),
reason(reason_arg),
critical(critical_arg) {}
std::string name;
std::string reason;
bool critical;
virtual std::string render() const
{
std::ostringstream out;
out << "name: " << name << ", reason: " << reason << ", critical: " << critical;
return out.str();
}
};
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"
if (!user.empty())
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 TunHalt : public ReasonBase
{
TunHalt(std::string reason) : ReasonBase(TUN_HALT, std::move(reason)) {}
};
struct RelayError : public ReasonBase
{
RelayError(std::string reason) : ReasonBase(RELAY_ERROR, 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)) {}
};
struct Warn : public ReasonBase
{
Warn(std::string value) : ReasonBase(WARN, std::move(value)) {}
};
class ClientSetup : public ReasonBase
{
public:
ClientSetup(const std::string& status, const std::string& message)
: ReasonBase(CLIENT_SETUP, make(status, message))
{
}
private:
static std::string make(const std::string& status, const std::string& message)
{
std::string ret;
ret += status;
if (!status.empty() && !message.empty())
ret += ": ";
ret += message;
return ret;
}
};
struct CompressionEnabled : public ReasonBase
{
CompressionEnabled(std::string msg)
: ReasonBase(COMPRESSION_ENABLED, std::move(msg))
{
}
};
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
+119
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+918
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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_EXTERNAL_TRANSPORT_FACTORY)
#include <openvpn/transport/client/extern/config.hpp>
#include <openvpn/transport/client/extern/fw.hpp>
#endif
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
// requires that client implements ExternalTun::Factory::new_tun_factory
#include <openvpn/tun/extern/config.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 sso_methods;
std::string server_override;
std::string port_override;
std::string hw_addr_override;
std::string platform_version;
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 wintun = false;
bool google_dns_fallback = false;
bool synchronous_dns_lookup = 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;
bool retry_on_auth_failed = false;
bool allow_local_lan_access = false;
std::string tls_version_min_override;
std::string tls_cert_profile_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
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
ExternalTun::Factory* extern_tun_factory = nullptr;
#endif
#if defined(OPENVPN_EXTERNAL_TRANSPORT_FACTORY)
ExternalTransport::Factory* extern_transport_factory = 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),
port_override(config.port_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),
asio_work_always_on_(false),
synchronous_dns_lookup(false),
retry_on_auth_failed_(config.retry_on_auth_failed)
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
,extern_transport_factory(config.extern_transport_factory)
#endif
{
// 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_EXTERNAL_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")));
// OpenVPN Protocol context (including SSL)
cp_main = proto_config(opt, config, pcc, false);
cp_relay = proto_config(opt, config, pcc, true); // may be null
layer = cp_main->layer;
#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/port overrides
remote_list->set_server_override(config.server_override);
remote_list->set_port_override(config.port_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");
#ifdef OPENVPN_PLATFORM_UWP
// workaround for OVPN3-62 Busy loop in win_event.hpp
asio_work_always_on_ = true;
#endif
synchronous_dns_lookup = config.synchronous_dns_lookup;
#ifdef OPENVPN_TLS_LINK
if (opt.exists("tls-ca"))
{
tls_ca = opt.cat("tls-ca");
}
#endif
// init transport config
const std::string session_name = load_transport_config();
// initialize tun/tap
if (dco)
{
DCO::TunConfig tunconf;
tunconf.tun_prop.layer = 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_EXTERNAL_TUN_FACTORY)
{
ExternalTun::Config tunconf;
tunconf.tun_prop.layer = 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.tun_prop.remote_list = remote_list;
tunconf.tun_persist = config.tun_persist;
tunconf.stop = config.stop;
tun_factory.reset(config.extern_tun_factory->new_tun_factory(tunconf, opt));
if (!tun_factory)
throw option_error("OPENVPN_EXTERNAL_TUN_FACTORY: no tun factory");
}
#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;
tunconf->tun_prop.allow_local_lan_access = config.allow_local_lan_access;
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 defined(OPENVPN_PLATFORM_MAC)
tunconf->tun_prefix = true;
#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 = 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;
if (config.tun_persist)
tunconf->tun_persist.reset(new TunLinux::TunPersist(true, false, nullptr));
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 = 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));
#ifndef OPENVPN_COMMAND_AGENT
/* remote_list is required by remote_bypass to work */
tunconf->tun_prop.remote_bypass = true;
tunconf->tun_prop.remote_list = remote_list;
#endif
}
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 = 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;
tunconf->wintun = config.wintun;
if (config.tun_persist)
{
tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr));
#ifndef OPENVPN_COMMAND_AGENT
/* remote_list is required by remote_bypass to work */
tunconf->tun_prop.remote_bypass = true;
tunconf->tun_prop.remote_list = remote_list;
#endif
}
#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
}
// The Core Library itself does not handle TAP/OSI_LAYER_2 currently,
// so we bail out early whenever someone tries to use TAP configurations
if (layer == Layer(Layer::OSI_LAYER_2))
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
opt.show_unused_options(OPENVPN_UNUSED_OPTIONS);
}
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());
// UI version
if (!config.gui_version.empty())
pi->emplace_back("IV_GUI_VER", config.gui_version);
// Supported SSO methods
if (!config.sso_methods.empty())
pi->emplace_back("IV_SSO", config.sso_methods);
// MAC address
if (pcc.pushPeerInfo())
{
std::string hwaddr = get_hwaddr();
if (!config.hw_addr_override.empty())
pi->emplace_back("IV_HWADDR", config.hw_addr_override);
else if (!hwaddr.empty())
pi->emplace_back("IV_HWADDR", hwaddr);
pi->emplace_back ("IV_SSL", get_ssl_library_version());
if (!config.platform_version.empty())
pi->emplace_back("IV_PLAT_VER", config.platform_version);
}
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;
}
bool retry_on_auth_failed() const
{
return retry_on_auth_failed_;
}
Client::Config::Ptr client_config(const bool relay_mode)
{
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(proto_config_cached(relay_mode)));
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_; }
bool asio_work_always_on() const { return asio_work_always_on_; }
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:
Client::ProtoConfig& proto_config_cached(const bool relay_mode)
{
if (relay_mode && cp_relay)
return *cp_relay;
else
return *cp_main;
}
Client::ProtoConfig::Ptr proto_config(const OptionList& opt,
const Config& config,
const ParseClientConfig& pcc,
const bool relay_mode)
{
// relay mode is null unless one of the below directives is defined
if (relay_mode && !opt.exists("relay-mode"))
return Client::ProtoConfig::Ptr();
// load flags
unsigned int lflags = SSLConfigAPI::LF_PARSE_MODE;
if (relay_mode)
lflags |= SSLConfigAPI::LF_RELAY_MODE;
if (opt.exists("allow-name-constraints"))
lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS;
// 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, lflags);
cc->set_tls_version_min_override(config.tls_version_min_override);
cc->set_tls_cert_profile_override(config.tls_cert_profile_override);
if (!cc->get_mode().is_client())
throw option_error("only client configuration supported");
// client ProtoContext config
Client::ProtoConfig::Ptr cp(new Client::ProtoConfig());
cp->relay_mode = relay_mode;
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->tls_crypt_factory.reset(new CryptoTLSCryptFactory<SSLLib::CryptoAPI>());
cp->tls_crypt_metadata_factory.reset(new CryptoTLSCryptMetadataFactory());
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->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;
return cp;
}
std::string load_transport_config()
{
// get current transport protocol
const Protocol& transport_protocol = remote_list->current_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
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
ExternalTransport::Config transconf;
transconf.remote_list = remote_list;
transconf.frame = frame;
transconf.stats = cli_stats;
transconf.socket_protect = socket_protect;
transconf.server_addr_float = server_addr_float;
transconf.synchronous_dns_lookup = synchronous_dns_lookup;
transconf.protocol = transport_protocol;
transport_factory = extern_transport_factory->new_transport_factory(transconf);
#ifdef OPENVPN_GREMLIN
udpconf->gremlin_config = gremlin_config;
#endif
#else
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()
#ifdef OPENVPN_TLS_LINK
|| transport_protocol.is_tls()
#endif
)
{
// 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_TLS_LINK
if (transport_protocol.is_tls())
tcpconf->use_tls = true;
tcpconf->tls_ca = tls_ca;
#endif
#ifdef OPENVPN_GREMLIN
tcpconf->gremlin_config = gremlin_config;
#endif
transport_factory = tcpconf;
}
else
throw option_error("internal error: unknown transport protocol");
}
#endif // OPENVPN_EXTERNAL_TRANSPORT_FACTORY
return remote_list->current_server_host();
}
Time now_; // current time
RandomAPI::Ptr rng;
RandomAPI::Ptr prng;
Frame::Ptr frame;
Layer layer;
Client::ProtoConfig::Ptr cp_main;
Client::ProtoConfig::Ptr cp_relay;
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;
std::string port_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;
bool asio_work_always_on_;
bool synchronous_dns_lookup;
bool retry_on_auth_failed_;
PushOptionsBase::Ptr push_base;
OptionList::FilterBase::Ptr pushed_options_filter;
ClientLifeCycle::Ptr client_lifecycle;
AltProxy::Ptr alt_proxy;
DCO::Ptr dco;
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
ExternalTransport::Factory* extern_transport_factory;
#endif
#ifdef OPENVPN_TLS_LINK
std::string tls_ca;
#endif
};
}
#endif
@@ -0,0 +1,722 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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 <utility>
#ifdef HAVE_CONFIG_JSONCPP
#include "json/json.h"
#endif /* HAVE_CONFIG_JSONCPP */
#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>
#include <openvpn/ssl/proto.hpp>
#include <openvpn/ssl/proto_context_options.hpp>
#include <openvpn/ssl/sslchoose.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()
{
reset_pod();
}
ParseClientConfig(const OptionList& options)
{
try {
// reset POD types
reset_pod();
// limits
const size_t max_server_list_size = ProfileParseLimits::MAX_SERVER_LIST_SIZE;
// 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_ = "ERR_PROFILE_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(std::move(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.reset(new RemoteList(options, "", 0, nullptr));
{
const RemoteList::Item* ri = remoteList->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 (remoteList)
profileName_ = remoteList->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(std::move(se));
}
}
}
// push-peer-info
{
if (options.exists("push-peer-info"))
pushPeerInfo_ = true;
if (pushPeerInfo_)
peerInfoUV_ = peer_info_uv;
}
// dev name
{
const Option *o = options.get_ptr("dev");
if (o)
{
dev = o->get(1, 256);
}
}
// protocol configuration
{
protoConfig.reset(new ProtoContext::Config());
protoConfig->tls_auth_factory.reset(new CryptoOvpnHMACFactory<SSLLib::CryptoAPI>());
protoConfig->tls_crypt_factory.reset(new CryptoTLSCryptFactory<SSLLib::CryptoAPI>());
protoConfig->load(options, ProtoContextOptions(), -1, false);
}
unsigned int lflags = SSLConfigAPI::LF_PARSE_MODE;
if (options.exists("allow-name-constraints"))
lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS;
// ssl lib configuration
try {
sslConfig.reset(new SSLLib::SSLAPI::Config());
sslConfig->load(options, lflags);
} catch (...) {
sslConfig.reset();
}
}
catch (const option_error& e)
{
error_ = true;
message_ = Unicode::utf8_printable<std::string>(std::string("ERR_PROFILE_OPTION: ") + e.what(), 256);
}
catch (const std::exception& e)
{
error_ = true;
message_ = Unicode::utf8_printable<std::string>(std::string("ERR_PROFILE_GENERIC: ") + e.what(), 256);
}
}
static ParseClientConfig parse(const std::string& content)
{
return parse(content, nullptr);
}
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(std::move(opt));
added = true;
}
// dev
if (!options.exists("dev"))
{
Option opt;
opt.push_back("dev");
opt.push_back("tun");
options.push_back(std::move(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>(std::string("ERR_PROFILE_GENERIC: ") + 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();
}
std::string to_string_config() const
{
std::ostringstream os;
os << "client" << std::endl;
os << "dev " << dev << std::endl;
os << "dev-type " << protoConfig->layer.dev_type() << std::endl;
for (size_t i = 0; i < remoteList->size(); i++)
{
const RemoteList::Item& item = remoteList->get_item(i);
os << "remote " << item.server_host << " " << item.server_port;
const char *proto = item.transport_protocol.protocol_to_string();
if (proto)
os << " " << proto;
os << std::endl;
}
if (protoConfig->tls_crypt_context)
{
os << "<tls-crypt>" << std::endl << protoConfig->tls_key.render() << "</tls-crypt>"
<< std::endl;
}
else if (protoConfig->tls_auth_context)
{
os << "<tls-auth>" << std::endl << protoConfig->tls_key.render() << "</tls-auth>"
<< std::endl;
os << "key_direction " << protoConfig->key_direction << std::endl;
}
// SSL parameters
if (sslConfig)
{
print_pem(os, "ca", sslConfig->extract_ca());
print_pem(os, "crl", sslConfig->extract_crl());
print_pem(os, "key", sslConfig->extract_private_key());
print_pem(os, "cert", sslConfig->extract_cert());
std::vector<std::string> extra_certs = sslConfig->extract_extra_certs();
if (extra_certs.size() > 0)
{
os << "<extra-certs>" << std::endl;
for (auto& cert : extra_certs)
{
os << cert;
}
os << "</extra-certs>" << std::endl;
}
}
os << "cipher " << CryptoAlgs::name(protoConfig->dc.cipher(), "none")
<< std::endl;
os << "auth " << CryptoAlgs::name(protoConfig->dc.digest(), "none")
<< std::endl;
const char *comp = protoConfig->comp_ctx.method_to_string();
if (comp)
os << "compress " << comp << std::endl;
os << "keepalive " << protoConfig->keepalive_ping.to_seconds() << " "
<< protoConfig->keepalive_timeout.to_seconds() << std::endl;
os << "tun-mtu " << protoConfig->tun_mtu << std::endl;
os << "reneg-sec " << protoConfig->renegotiate.to_seconds() << std::endl;
return os.str();
}
#ifdef HAVE_CONFIG_JSONCPP
std::string to_json_config() const
{
std::ostringstream os;
Json::Value root(Json::objectValue);
root["mode"] = Json::Value("client");
root["dev"] = Json::Value(dev);
root["dev-type"] = Json::Value(protoConfig->layer.dev_type());
root["remotes"] = Json::Value(Json::arrayValue);
for (size_t i = 0; i < remoteList->size(); i++)
{
const RemoteList::Item& item = remoteList->get_item(i);
Json::Value el = Json::Value(Json::objectValue);
el["address"] = Json::Value(item.server_host);
el["port"] = Json::Value((Json::UInt)std::stoi(item.server_port));
if (item.transport_protocol() == Protocol::NONE)
el["proto"] = Json::Value("adaptive");
else
el["proto"] = Json::Value(item.transport_protocol.str());
root["remotes"].append(el);
}
if (protoConfig->tls_crypt_context)
{
root["tls_wrap"] = Json::Value(Json::objectValue);
root["tls_wrap"]["mode"] = Json::Value("tls_crypt");
root["tls_wrap"]["key"] = Json::Value(protoConfig->tls_key.render());
}
else if (protoConfig->tls_auth_context)
{
root["tls_wrap"] = Json::Value(Json::objectValue);
root["tls_wrap"]["mode"] = Json::Value("tls_auth");
root["tls_wrap"]["key_direction"] = Json::Value((Json::UInt)protoConfig->key_direction);
root["tls_wrap"]["key"] = Json::Value(protoConfig->tls_key.render());
}
// SSL parameters
if (sslConfig)
{
json_pem(root, "ca", sslConfig->extract_ca());
json_pem(root, "crl", sslConfig->extract_crl());
json_pem(root, "cert", sslConfig->extract_cert());
// JSON config is aimed to users, therefore we do not export the raw private
// key, but only some basic info
PKType::Type priv_key_type = sslConfig->private_key_type();
if (priv_key_type != PKType::PK_NONE)
{
root["key"] = Json::Value(Json::objectValue);
root["key"]["type"] = Json::Value(sslConfig->private_key_type_string());
root["key"]["length"] = Json::Value((Json::UInt)sslConfig->private_key_length());
}
std::vector<std::string> extra_certs = sslConfig->extract_extra_certs();
if (extra_certs.size() > 0)
{
root["extra_certs"] = Json::Value(Json::arrayValue);
for (auto cert = extra_certs.begin(); cert != extra_certs.end(); cert++)
{
if (!cert->empty())
root["extra_certs"].append(Json::Value(*cert));
}
}
}
root["cipher"] = Json::Value(CryptoAlgs::name(protoConfig->dc.cipher(), "none"));
root["auth"] = Json::Value(CryptoAlgs::name(protoConfig->dc.digest(), "none"));
if (protoConfig->comp_ctx.type() != CompressContext::NONE)
root["compression"] = Json::Value(protoConfig->comp_ctx.str());
root["keepalive"] = Json::Value(Json::objectValue);
root["keepalive"]["ping"] = Json::Value((Json::UInt)protoConfig->keepalive_ping.to_seconds());
root["keepalive"]["timeout"] = Json::Value((Json::UInt)protoConfig->keepalive_timeout.to_seconds());
root["tun_mtu"] = Json::Value((Json::UInt)protoConfig->tun_mtu);
root["reneg_sec"] = Json::Value((Json::UInt)protoConfig->renegotiate.to_seconds());
return root.toStyledString();
}
#endif /* HAVE_CONFIG_JSONCPP */
private:
static void print_pem(std::ostream& os, std::string label, std::string pem)
{
if (pem.empty())
return;
os << "<" << label << ">" << std::endl << pem << "</" << label << ">" << std::endl;
}
#ifdef HAVE_CONFIG_JSONCPP
static void json_pem(Json::Value& obj, std::string key, std::string pem)
{
if (pem.empty())
return;
obj[key] = Json::Value(pem);
}
#endif /* HAVE_CONFIG_JSONCPP */
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;
}
}
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_;
RemoteList::Ptr remoteList;
RemoteItem firstRemoteListItem_;
PeerInfo::Set::Ptr peerInfoUV_;
ProtoContext::Config::Ptr protoConfig;
SSLLib::SSLAPI::Config::Ptr sslConfig;
std::string dev;
};
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+104
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
@@ -0,0 +1,926 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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 <thread>
#include <openvpn/io/io.hpp>
#include <openvpn/asio/asiowork.hpp>
#include <openvpn/common/bigmutex.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>
#include <openvpn/client/async_resolve.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(const EPRANGE& endpoint_range, RandomAPI* rng)
{
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 virtual RC<thread_unsafe_refcount>, AsyncResolvableTCP
{
public:
typedef RCPtr<PreResolve> Ptr;
struct NotifyCallback
{
// client callback when resolve operation is complete
virtual void pre_resolve_done() = 0;
};
PreResolve(openvpn_io::io_context& io_context_arg,
const RemoteList::Ptr& remote_list_arg,
const SessionStats::Ptr& stats_arg)
: AsyncResolvableTCP(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;
async_resolve_lock();
next();
}
else
notify_callback_arg->pre_resolve_done();
}
}
void cancel()
{
notify_callback = nullptr;
index = 0;
async_resolve_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
{
OPENVPN_LOG_REMOTELIST("*** PreResolve RESOLVE on " << item.server_host << " : " << item.server_port);
async_resolve_name(item.server_host, item.server_port);
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.
{
async_resolve_cancel();
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 openvpn_io::error_code& error,
openvpn_io::ip::tcp::resolver::results_type results) override
{
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();
}
}
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())
return;
for (auto &item : list)
{
item->server_host = server_override;
item->res_addr_list.reset();
}
reset_cache();
}
// override all server ports to port_override
void set_port_override(const std::string& port_override)
{
if (port_override.empty())
return;
for (auto &item : list)
{
item->server_port = port_override;
item->res_addr_list.reset();
}
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)
{
Item::Ptr item = remote_override->get();
if (item)
{
list.clear();
index.reset();
list.push_back(std::move(item));
return;
}
}
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
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() >= (size_t)(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
+158
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+30
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+212
View File
@@ -0,0 +1,212 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_COMMON_ACTION_H
#define OPENVPN_COMMON_ACTION_H
#include <vector>
#include <string>
#include <ostream>
#include <sstream>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/destruct.hpp>
#include <openvpn/common/jsonlib.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_JSON
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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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(openvpn_io::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());
}
openvpn_io::post(io_context, [self=Ptr(this), status]()
{
self->completion_post(status);
});
}
openvpn_io::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,35 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_COMMON_APPVERSION_H
#define OPENVPN_COMMON_APPVERSION_H
// BUILD_VERSION version can be passed on build command line
#include <openvpn/common/stringize.hpp>
#ifdef BUILD_VERSION
#define MY_VERSION OPENVPN_STRINGIZE(BUILD_VERSION)
#else
#define MY_VERSION "0.1.0"
#endif
#endif
+37
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// define 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
+133
View File
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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,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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+318
View File
@@ -0,0 +1,318 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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 ConstUCharWrap
{
public:
ConstUCharWrap(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_decode_out_of_bound_error);
private:
// Minimal class to minic the container our decode method expects
class UCharWrap
{
public:
UCharWrap(unsigned char *data, size_t size):
data(data), size(size), index(0)
{
}
void push_back(unsigned char c)
{
if (index >= size)
throw base64_decode_out_of_bound_error();
data[index++]=c;
}
unsigned char *data;
size_t size;
size_t index;
};
public:
OPENVPN_SIMPLE_EXCEPTION(base64_bad_map);
OPENVPN_SIMPLE_EXCEPTION(base64_decode_error);
// altmap is "+/=" by default
// another possible encoding for URLs: "-_."
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 = "+/=";
if (std::strlen(altmap) != 3)
throw base64_bad_map();
enc[62] = (unsigned char)altmap[0];
enc[63] = (unsigned char)altmap[1];
equal = (unsigned char)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] = (unsigned char)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 void *data, size_t size) const
{
return encode(ConstUCharWrap((const unsigned char *)data, size));
}
/**
* Decodes data from the passed string and stores the
* result in data
* @param data Destination of the decoded data
* @param len Length of the region in data
* @param str Base64 string to decode
* @return Number of bytes written to data
*/
size_t decode(void *data, size_t len, const std::string& str) const
{
UCharWrap ret((unsigned char*)data, len);
decode(ret, str);
return ret.index;
}
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);
}
}
template <typename V>
bool is_base64(const V& data, const size_t expected_decoded_length) const
{
const size_t size = data.size();
if (size != encoded_len(expected_decoded_length))
return false;
const size_t eq_begin = size - num_eq(expected_decoded_length);
for (size_t i = 0; i < size; ++i)
{
const char c = data[i];
if (i < eq_begin)
{
if (!is_base64_char(c))
return false;
}
else
{
if (c != equal)
return false;
}
}
return true;
}
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;
}
static size_t encoded_len(const size_t decoded_len)
{
return (decoded_len * 4 / 3 + 3) & ~3;
}
static size_t num_eq(const size_t decoded_len)
{
return (-1 - decoded_len) % 3;
}
unsigned char enc[64];
unsigned char dec[128];
unsigned char equal;
};
// provide a static Base64 object
OPENVPN_EXTERN const Base64* base64; // GLOBAL
OPENVPN_EXTERN const Base64* base64_urlsafe; // GLOBAL
inline void base64_init_static()
{
if (!base64)
base64 = new Base64();
if (!base64_urlsafe)
base64_urlsafe = new Base64("-_.");
}
inline void base64_uninit_static()
{
if (base64)
{
delete base64;
base64 = nullptr;
}
if (base64_urlsafe)
{
delete base64_urlsafe;
base64_urlsafe = nullptr;
}
}
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Macro to maintain thread-safety.
// Platforms like UWP and iOS may call core methods
// from another threads. Since core is not thread-safe,
// we provide OPENVPN_ASYNC_HANDLER macro which instantiates
// lock guard. It follows RIAA principle and locks global
// mutex in constructor and unlocks in destructor. This
// guarantees that code in block protected with this macro
// won't be called simultaneously from different threads.
#ifndef OPENVPN_COMMON_BIGMUTEX_H
#define OPENVPN_COMMON_BIGMUTEX_H
#include <mutex>
#include <openvpn/common/extern.hpp>
namespace openvpn {
namespace bigmutex {
OPENVPN_EXTERN std::recursive_mutex the_recursive_mutex;
}
#ifdef OPENVPN_ENABLE_BIGMUTEX
#define OPENVPN_ASYNC_HANDLER \
std::lock_guard<std::recursive_mutex> lg(bigmutex::the_recursive_mutex);
#else
#define OPENVPN_ASYNC_HANDLER
#endif
}
#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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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
+39
View File
@@ -0,0 +1,39 @@
// 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-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
// loose emulation of std::clamp for pre-C++17
namespace openvpn {
template <typename T>
T clamp(T value, T low, T high)
{
if (value < low)
return low;
else if (value > high)
return high;
else
return value;
}
}
@@ -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-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+70
View File
@@ -0,0 +1,70 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// 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()
{
int count = std::thread::hardware_concurrency();
// C++11 allows thread::hardware_concurrency() to return 0, fall back
// to specific solution if we detect this
if (count > 0)
return count;
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
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

Some files were not shown because too many files have changed in this diff Show More