mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
IP address/route classes: cleanup title usage with template approach
The concept of "title" in IP address/route parsing is that when parse errors occur, we want a human-readable string that can be included in the error message that gives the context for the error. For example if a bad IP address is specified in JSON data, we might want the name of the dictionary in the JSON to be given as title, so it can be a part of the error message. Previously we did this by implementing multiple constructors that accepted title as a std::string, const char *, or allowed the title to be omitted. The new model is to templatize title, so that title can be anything: a std::string, const char *, nullptr, or a custom class (such as IndexedTitle) that supports to_string() and empty() methods. Having a custom class for title is useful for performance because then you can use lazy evaluation techniques that don't have to expensively pre-format a std::string for every possible instance of title on the off-chance that you might throw an error. The formatting only occurs when the to_string() method is called, after an error has already been confirmed. Note: since this code has a lot of users, some of which I haven't considered (such as Swig), I'm leaving an out where you can revert back to the previous code by defining OPENVPN_LEGACY_TITLE_ABSTRACTION. Signed-off-by: James Yonan <james@openvpn.net>
This commit is contained in:
parent
1e2ca13908
commit
ad9feaffeb
@ -56,6 +56,109 @@ namespace openvpn {
|
||||
V6_SIZE = IPv6::Addr::SIZE,
|
||||
};
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
Addr(const Addr& other, const TITLE& title, const Version required_version)
|
||||
: 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;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
Addr(const Addr& other, const TITLE& title)
|
||||
: Addr(other, title, UNSPEC)
|
||||
{
|
||||
}
|
||||
|
||||
Addr(const Addr& other)
|
||||
: Addr(other, nullptr, UNSPEC)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
Addr(const std::string& ipstr, const TITLE& title, const Version required_version)
|
||||
: Addr(from_string(ipstr, title, required_version))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
Addr(const std::string& ipstr, const TITLE& title)
|
||||
: Addr(from_string(ipstr, title, UNSPEC))
|
||||
{
|
||||
}
|
||||
|
||||
Addr(const std::string& ipstr)
|
||||
: Addr(from_string(ipstr, nullptr, UNSPEC))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
static Addr from_string(const std::string& ipstr,
|
||||
const TITLE& title,
|
||||
const Version required_version)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
static Addr from_string(const std::string& ipstr, const TITLE& title)
|
||||
{
|
||||
return from_string(ipstr, title, UNSPEC);
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr)
|
||||
{
|
||||
return from_string(ipstr, nullptr, UNSPEC);
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
static std::string validate(const std::string& ipstr,
|
||||
const TITLE& title,
|
||||
const Version required_version)
|
||||
{
|
||||
Addr a = from_string(ipstr, title, required_version);
|
||||
return a.to_string();
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
static std::string validate(const std::string& ipstr, const TITLE& title)
|
||||
{
|
||||
return validate(ipstr, title, UNSPEC);
|
||||
}
|
||||
|
||||
static std::string validate(const std::string& ipstr)
|
||||
{
|
||||
return validate(ipstr, nullptr, UNSPEC);
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
void validate_version(const TITLE& title, const 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"));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Addr(const Addr& other, const char *title = nullptr, Version required_version = UNSPEC)
|
||||
: ver(other.ver)
|
||||
{
|
||||
@ -113,6 +216,20 @@ namespace openvpn {
|
||||
{
|
||||
return validate(ipstr, title.c_str(), required_version);
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool is_valid(const std::string& ipstr)
|
||||
@ -136,18 +253,6 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -19,17 +19,59 @@
|
||||
// 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
|
||||
// Called internally by IP, IPv4, and IPv6 classes
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
#include <openvpn/common/stringtempl2.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
namespace internal {
|
||||
// Called internally by IP, IPv4, and IPv6 classes
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
inline std::string format_error(const std::string& ipstr,
|
||||
const TITLE& title,
|
||||
const char *ipver,
|
||||
const std::string& message)
|
||||
{
|
||||
std::string err = "error parsing";
|
||||
if (!StringTempl::empty(title))
|
||||
{
|
||||
err += ' ';
|
||||
err += StringTempl::to_string(title);
|
||||
}
|
||||
err += " IP";
|
||||
err += ipver;
|
||||
err += " address '";
|
||||
err += ipstr;
|
||||
err += '\'';
|
||||
if (!message.empty())
|
||||
{
|
||||
err += " : ";
|
||||
err += message;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
inline std::string format_error(const std::string& ipstr,
|
||||
const TITLE& title,
|
||||
const char *ipver,
|
||||
const openvpn_io::error_code& ec)
|
||||
{
|
||||
return format_error(ipstr, title, ipver, ec.message());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline std::string format_error(const std::string& ipstr, const char *title, const char *ipver, const openvpn_io::error_code& ec)
|
||||
{
|
||||
@ -68,8 +110,8 @@ namespace openvpn {
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -206,6 +206,25 @@ namespace openvpn {
|
||||
return netmask_from_prefix_len(SIZE - lb);
|
||||
}
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
static Addr from_string(const std::string& ipstr, const TITLE& title)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr)
|
||||
{
|
||||
return from_string(ipstr, nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static Addr from_string(const std::string& ipstr, const char *title = nullptr)
|
||||
{
|
||||
openvpn_io::error_code ec;
|
||||
@ -215,6 +234,8 @@ namespace openvpn {
|
||||
return from_asio(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const openvpn_io::ip::address_v4 a = to_asio();
|
||||
|
@ -100,6 +100,25 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
static Addr from_string(const std::string& ipstr, const TITLE& title)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static Addr from_string(const std::string& ipstr)
|
||||
{
|
||||
return from_string(ipstr, nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static Addr from_string(const std::string& ipstr, const char *title = nullptr)
|
||||
{
|
||||
openvpn_io::error_code ec;
|
||||
@ -109,6 +128,8 @@ namespace openvpn {
|
||||
return from_asio(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
const openvpn_io::ip::address_v6 a = to_asio();
|
||||
|
@ -55,6 +55,52 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
RouteType(const ADDR& addr_arg,
|
||||
const unsigned int prefix_len_arg)
|
||||
: addr(addr_arg),
|
||||
prefix_len(prefix_len_arg)
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
RouteType(const std::string& rtstr, const TITLE& title)
|
||||
: RouteType(RouteType::from_string(rtstr, title))
|
||||
{
|
||||
}
|
||||
|
||||
RouteType(const std::string& rtstr)
|
||||
: RouteType(RouteType::from_string(rtstr, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
static RouteType from_string(const std::string& rtstr, const TITLE& title)
|
||||
{
|
||||
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, (!StringTempl::empty(title) ? title : "route") << " : bad prefix length : " << rtstr);
|
||||
}
|
||||
else
|
||||
r.prefix_len = r.addr.size();
|
||||
return r;
|
||||
}
|
||||
|
||||
static RouteType from_string(const std::string& rtstr)
|
||||
{
|
||||
return from_string(rtstr, nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
RouteType(const std::string& rtstr, const char *title = nullptr)
|
||||
: RouteType(RouteType::from_string(rtstr, title))
|
||||
{
|
||||
@ -65,13 +111,6 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
@ -90,6 +129,8 @@ namespace openvpn {
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return addr.defined();
|
||||
@ -311,6 +352,34 @@ namespace openvpn {
|
||||
OPENVPN_OSTREAM(Route4List, to_string);
|
||||
OPENVPN_OSTREAM(Route6List, to_string);
|
||||
|
||||
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
|
||||
|
||||
template <typename TITLE>
|
||||
inline Route route_from_string_prefix(const std::string& addrstr,
|
||||
const unsigned int prefix_len,
|
||||
const TITLE& 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;
|
||||
}
|
||||
|
||||
template <typename TITLE>
|
||||
inline Route route_from_string(const std::string& rtstr,
|
||||
const TITLE& title,
|
||||
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
|
||||
{
|
||||
Route r(rtstr, title);
|
||||
r.addr.validate_version(title, required_version);
|
||||
return r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline Route route_from_string_prefix(const std::string& addrstr,
|
||||
const unsigned int prefix_len,
|
||||
const std::string& title,
|
||||
@ -332,6 +401,9 @@ namespace openvpn {
|
||||
r.addr.validate_version(title, required_version);
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user