0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00

fix handling of --proto option

Make it possible to enforce the protocol family by appending 4/6 to
to the protocol, e.g. tcp6 or udp4. While it is already possible to
have protocol options like these in the configuration, they are not
enforced so far. Thus you could still be connected to a v6 address
even though the config requested v4 only.

Since v2.3 the openvpn 2.x series behaves like this. So, this is also
to catch up with the behavior there.

Signed-off-by: Heiko Hund <heiko@openvpn.net>
This commit is contained in:
Heiko Hund 2021-03-23 03:46:49 +01:00 committed by David Sommerseth
parent 69ed0a6f10
commit f1bdbe5088
No known key found for this signature in database
GPG Key ID: 86CF944C9671FDF2
3 changed files with 95 additions and 60 deletions

View File

@ -153,6 +153,10 @@ namespace openvpn {
res_addr_list.reset(new ResolvedAddrList());
for (const auto &i : endpoint_range)
{
// Skip addresses with incompatible family
if ((transport_protocol.is_ipv6() && i.endpoint().address().is_v4())
|| (transport_protocol.is_ipv4() && i.endpoint().address().is_v6()))
continue;
ResolvedAddr::Ptr addr(new ResolvedAddr());
addr->addr = IP::Addr::from_asio(i.endpoint().address());
res_addr_list->push_back(addr);
@ -316,21 +320,10 @@ namespace openvpn {
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 " << sitem.actual_host());
item.res_addr_list = sitem->res_addr_list;
item.random_host = sitem->random_host;
}
else
{
const std::string& host = item.actual_host();
OPENVPN_LOG_REMOTELIST("*** PreResolve RESOLVE on " << host << " : " << item.server_port);
async_resolve_name(host, item.server_port);
return;
}
const std::string& host = item.actual_host();
OPENVPN_LOG_REMOTELIST("*** PreResolve RESOLVE on " << host << " : " << item.server_port);
async_resolve_name(host, item.server_port);
return;
}
++index;
}
@ -354,17 +347,27 @@ namespace openvpn {
{
if (notify_callback && index < remote_list->list.size())
{
Item& item = *remote_list->list[index++];
const auto resolve_item(remote_list->list[index++]);
if (!error)
{
// resolve succeeded
// Set results to Items, where applicable
auto rand = remote_list->random ? remote_list->rng.get() : nullptr;
item.set_endpoint_range(results, rand);
for (auto& item : remote_list->list)
{
// Skip already resolved items and items with different hostname
if (item->res_addr_list_defined()
|| item->server_host != resolve_item->server_host)
continue;
item->set_endpoint_range(results, rand);
item->random_host = resolve_item->random_host;
}
}
else
{
// resolve failed
OPENVPN_LOG("DNS pre-resolve error on " << item.actual_host() << ": " << error.message());
OPENVPN_LOG("DNS pre-resolve error on " << resolve_item->actual_host()
<< ": " << error.message());
if (stats)
stats->error(Error::RESOLVE_ERROR);
}
@ -739,18 +742,6 @@ namespace openvpn {
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;
}
// return true if at least one remote entry is of type proto
bool contains_protocol(const Protocol& proto)
{

View File

@ -46,9 +46,9 @@ namespace openvpn {
UnixStream, // unix domain socket (stream)
UnixDGram, // unix domain socket (datagram)
NamedPipe, // named pipe (Windows only)
UDP=UDPv4,
TCP=TCPv4,
TLS=TLSv4,
UDP,
TCP,
TLS,
};
enum AllowSuffix {
@ -65,10 +65,11 @@ namespace openvpn {
void reset() { type_ = NONE; }
bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; }
bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; }
bool is_tls() const { return type_ == TLSv4 || type_ == TLSv6; }
bool is_udp() const { return type_ == UDP || type_ == UDPv4 || type_ == UDPv6; }
bool is_tcp() const { return type_ == TCP || type_ == TCPv4 || type_ == TCPv6; }
bool is_tls() const { return type_ == TLS || type_ == TLSv4 || type_ == TLSv6; }
bool is_reliable() const { return is_tcp() || is_tls(); }
bool is_ipv4() const { return type_ == UDPv4 || type_ == TCPv4 || type_ == TLSv4; }
bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6 || type_ == TLSv6; }
bool is_unix() const { return type_ == UnixStream || type_ == UnixDGram; }
bool is_named_pipe() const { return type_ == NamedPipe; }
@ -151,12 +152,12 @@ namespace openvpn {
{
switch (type_)
{
case UDP:
case UDPv4:
return 0;
case TCPv4:
return 1;
case UDPv6:
return 0;
case TCP:
case TCPv4:
case TCPv6:
return 1;
case UnixDGram:
@ -165,6 +166,7 @@ namespace openvpn {
return 3;
case NamedPipe:
return 4;
case TLS:
case TLSv4:
case TLSv6:
return 5;
@ -177,14 +179,20 @@ namespace openvpn {
{
switch (type_)
{
case UDP:
return "UDP";
case UDPv4:
return "UDPv4";
case TCPv4:
return "TCPv4";
case UDPv6:
return "UDPv6";
case TCP:
return "TCP";
case TCPv4:
return "TCPv4";
case TCPv6:
return "TCPv6";
case TLS:
return "TLS/TCP";
case TLSv4:
return "TLS/TCPv4";
case TLSv6:
@ -206,14 +214,20 @@ namespace openvpn {
{
switch (type_)
{
case UDP:
return "udp";
case UDPv4:
return "udp4";
case TCPv4:
return "tcp4";
case UDPv6:
return "udp6";
case TCP:
return "tcp";
case TCPv4:
return "tcp4";
case TCPv6:
return "tcp6";
case TLS:
return "tls";
case TLSv4:
return "tls4";
case TLSv6:
@ -235,14 +249,20 @@ namespace openvpn {
{
switch (type_)
{
case UDP:
return "UDP";
case UDPv4:
return "UDPv4";
case TCPv4:
return "TCPv4_CLIENT";
case UDPv6:
return force_ipv4 ? "UDPv4" : "UDPv6";
case TCP:
return "TCP_CLIENT";
case TCPv4:
return "TCPv4_CLIENT";
case TCPv6:
return force_ipv4 ? "TCPv4_CLIENT" : "TCPv6_CLIENT";
case TLS:
return "TLS";
case TLSv4:
return "TLSv4";
case TLSv6:
@ -281,11 +301,20 @@ namespace openvpn {
}
else if (s == "named-pipe") // Windows named pipe
ret = NamedPipe;
else if (s.length() >= 3) // udp/tcp
else if (s.length() >= 3) // udp/tcp/tls
{
const std::string s1 = s.substr(0, 3);
const std::string s2 = s.substr(3);
if (s2 == "" || s2 == "4" || s2 == "v4")
if (s2 == "")
{
if (s1 == "udp")
ret = UDP;
else if (s1 == "tcp")
ret = TCP;
else if (s1 == "tls")
ret = TLS;
}
else if (s2 == "4" || s2 == "v4")
{
if (s1 == "udp")
ret = UDPv4;

View File

@ -113,7 +113,7 @@ TEST(RemoteList, CtorRemoteList)
ASSERT_EQ(rl.get_item(0).transport_protocol, Protocol(Protocol::TCPv6));
ASSERT_EQ(rl.get_item(1).server_host, "1.domain.invalid");
ASSERT_EQ(rl.get_item(1).server_port, "1111");
ASSERT_EQ(rl.get_item(1).transport_protocol, Protocol(Protocol::UDPv4));
ASSERT_EQ(rl.get_item(1).transport_protocol, Protocol(Protocol::UDP));
ASSERT_EQ(rl.get_item(2).server_host, "2.domain.invalid");
ASSERT_EQ(rl.get_item(2).server_port, "8888");
ASSERT_EQ(rl.get_item(2).transport_protocol, Protocol(Protocol::TCPv6));
@ -234,8 +234,8 @@ TEST(RemoteList, RemoteListPreResolve)
"remote 1.1.1.1 1111 udp\n"
"remote 2:cafe::1 2222 tcp\n"
"remote 3.domain.tld 3333 udp4\n"
"remote 3.domain.tld 33333 udp6\n"
"remote 4.domain.tld 4444 udp4\n"
"remote 3.domain.tld 33333 udp\n"
"remote 4.domain.tld 4444 udp6\n"
"remote 5.noresolve.tld 5555 udp4\n"
, nullptr);
cfg.update_map();
@ -277,18 +277,16 @@ TEST(RemoteList, RemoteListPreResolve)
ASSERT_EQ(rl->get_item(1).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(1).res_addr_list->at(0)->to_string(), "2:cafe::1");
ASSERT_EQ(rl->get_item(2).res_addr_list_defined(), true);
ASSERT_EQ(rl->get_item(2).res_addr_list->size(), 2);
ASSERT_EQ(rl->get_item(2).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(2).res_addr_list->at(0)->to_string(), "3.3.3.3");
ASSERT_EQ(rl->get_item(2).res_addr_list->at(1)->to_string(), "3::3");
ASSERT_EQ(rl->get_item(3).res_addr_list_defined(), true);
ASSERT_EQ(rl->get_item(3).res_addr_list->size(), 2);
ASSERT_EQ(rl->get_item(3).res_addr_list->at(0)->to_string(), "3.3.3.3");
ASSERT_EQ(rl->get_item(3).res_addr_list->at(1)->to_string(), "3::3");
ASSERT_EQ(rl->get_item(3).actual_host(), rl->get_item(2).actual_host());
ASSERT_EQ(rl->get_item(4).res_addr_list_defined(), true);
ASSERT_EQ(rl->get_item(4).res_addr_list->size(), 2);
ASSERT_EQ(rl->get_item(4).res_addr_list->at(0)->to_string(), "4.4.4.4");
ASSERT_EQ(rl->get_item(4).res_addr_list->at(1)->to_string(), "4::4");
ASSERT_EQ(rl->get_item(4).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(4).res_addr_list->at(0)->to_string(), "4::4");
// in case it gets randomized before the other 3.domain.tld
fake_preres.set_results("3.domain.tld", "33333", { {"3.3.3.3", 33333}, {"3::3", 33333} });
@ -317,8 +315,23 @@ TEST(RemoteList, RemoteListPreResolve)
ASSERT_EQ(rl->get_item(i).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(i).res_addr_list->at(0)->to_string(), "2:cafe::1");
}
else
ASSERT_EQ(rl->get_item(i).res_addr_list->size(), 2);
else if (rl->get_item(i).server_host[0] == '3')
{
if (rl->get_item(i).transport_protocol.is_ipv4())
{
ASSERT_EQ(rl->get_item(i).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(i).res_addr_list->at(0)->to_string(), "3.3.3.3");
}
else
{
ASSERT_EQ(rl->get_item(i).res_addr_list->size(), 2);
}
}
else if (rl->get_item(i).server_host[0] == '4')
{
ASSERT_EQ(rl->get_item(i).res_addr_list->size(), 1);
ASSERT_EQ(rl->get_item(i).res_addr_list->at(0)->to_string(), "4::4");
}
}
for (size_t i=0; i < rl->size(); ++i)
@ -331,8 +344,10 @@ TEST(RemoteList, RemoteListPreResolve)
ASSERT_EQ(rl->endpoint_available(&host, &port, &proto), true);
ASSERT_EQ(rl->get_item(i).actual_host(), host);
ASSERT_EQ(rl->get_item(i).server_port, port);
// proto is not yet honored by Item when adding resolver results
//ASSERT_EQ(rl->current_transport_protocol(), proto);
if (rl->current_transport_protocol().is_ipv4()
|| rl->current_transport_protocol().is_ipv6()) {
ASSERT_EQ(rl->current_transport_protocol(), proto);
}
auto ep1 = fake_preres.init_endpoint();
auto ep2 = fake_preres.init_endpoint();