0
0
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:
James Yonan 2020-04-17 15:01:00 -06:00 committed by David Sommerseth
parent 1e2ca13908
commit ad9feaffeb
No known key found for this signature in database
GPG Key ID: 86CF944C9671FDF2
5 changed files with 285 additions and 24 deletions

View File

@ -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)

View File

@ -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

View File

@ -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();

View File

@ -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();

View File

@ -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
}
}