diff --git a/openvpn/common/version.hpp b/openvpn/common/version.hpp index 5f839af08..52b5a4082 100644 --- a/openvpn/common/version.hpp +++ b/openvpn/common/version.hpp @@ -24,5 +24,5 @@ #pragma once #ifndef OPENVPN_VERSION -#define OPENVPN_VERSION "3.9_git:master" +#define OPENVPN_VERSION "3.9_qa" #endif diff --git a/openvpn/log/sessionstats.hpp b/openvpn/log/sessionstats.hpp index 574ef192c..07eb43aac 100644 --- a/openvpn/log/sessionstats.hpp +++ b/openvpn/log/sessionstats.hpp @@ -214,6 +214,12 @@ class SessionStats : public RC if (dco_) { const DCOTransportSource::Data data = dco_->dco_transport_stats_delta(); + + if (data.transport_bytes_in > 0) + { + update_last_packet_received(Time::now()); + } + stats_[BYTES_IN] += data.transport_bytes_in; stats_[BYTES_OUT] += data.transport_bytes_out; stats_[TUN_BYTES_IN] += data.tun_bytes_in; diff --git a/openvpn/tun/win/client/tunsetup.hpp b/openvpn/tun/win/client/tunsetup.hpp index c921d4fa9..ce07851f5 100644 --- a/openvpn/tun/win/client/tunsetup.hpp +++ b/openvpn/tun/win/client/tunsetup.hpp @@ -607,8 +607,9 @@ class Setup : public SetupBase const bool block_local_traffic = (pull.reroute_gw.flags & RedirectGatewayFlags::RG_BLOCK_LOCAL) != 0; if (use_wfp && block_local_traffic && !openvpn_app_path.empty()) { - create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, false, wfp)); - destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, false, wfp)); + WFP::Block block_type = (allow_local_dns_resolvers ? WFP::Block::AllButLocalDns : WFP::Block::All); + create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, block_type, wfp)); + destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, block_type, wfp)); } // The process id for NRPT rules @@ -634,12 +635,6 @@ class Setup : public SetupBase { domains.push_back("." + dom.domain); } - if (domains.empty() && allow_local_dns_resolvers) - { - // This empty domain tells the NRPT code that - // no '.' rule should be created - domains.push_back(""); - } const bool dnssec = server.dnssec == DnsServer::Security::Yes; @@ -651,16 +646,23 @@ class Setup : public SetupBase delimiter = ","; } - create.add(new NRPT::ActionCreate(pid, domains, addresses, dnssec)); + // To keep local resolvers working, only split rules must be created + if (!allow_local_dns_resolvers || !domains.empty()) + { + create.add(new NRPT::ActionCreate(pid, domains, addresses, dnssec)); + destroy.add(new NRPT::ActionDelete(pid)); + } + create.add(new DNS::ActionCreate(tap.name, search_domains)); - destroy.add(new NRPT::ActionDelete(pid)); destroy.add(new DNS::ActionDelete(tap.name, search_domains)); - // block local DNS lookup unless all traffic is blocked already - if (use_wfp && pull.block_outside_dns && !block_local_traffic && !openvpn_app_path.empty()) + // Use WFP for DNS leak protection unless local traffic is blocked already. + // Block DNS on all interfaces except the TAP adapter. + if (use_wfp && pull.block_outside_dns && !block_local_traffic + && !allow_local_dns_resolvers && !openvpn_app_path.empty()) { - create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, true, wfp)); - destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, true, wfp)); + create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, WFP::Block::Dns, wfp)); + destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, WFP::Block::Dns, wfp)); } } else @@ -732,7 +734,7 @@ class Setup : public SetupBase if (use_nrpt && (dns.ipv4() || dns.ipv6())) { // domain suffix list - std::vector dsfx; + std::vector split_domains; // Only add DNS routing suffixes if not rerouting gateway. // Otherwise, route all DNS requests with wildcard ("."). @@ -746,23 +748,22 @@ class Setup : public SetupBase // each DNS suffix must begin with '.' if (dom[0] != '.') dom = "." + dom; - dsfx.push_back(std::move(dom)); + split_domains.push_back(std::move(dom)); } } } - // This empty domain tells the NRPT code that - // no '.' rule should be created - if (dsfx.empty() && allow_local_dns_resolvers) - dsfx.emplace_back(""); - // DNS server list std::vector dserv; for (const auto &ds : pull.dns_servers) dserv.push_back(ds.address); - create.add(new NRPT::ActionCreate(pid, dsfx, dserv, false)); - destroy.add(new NRPT::ActionDelete(pid)); + // To keep local resolvers working, only split rules must be created + if (!allow_local_dns_resolvers || !split_domains.empty()) + { + create.add(new NRPT::ActionCreate(pid, split_domains, dserv, false)); + destroy.add(new NRPT::ActionDelete(pid)); + } } // Set a default TAP-adapter domain suffix using @@ -776,13 +777,12 @@ class Setup : public SetupBase // Use WFP for DNS leak protection unless local traffic is blocked already. - // If we added DNS servers, block DNS on all interfaces except - // the TAP adapter and loopback. - if (use_wfp && !block_local_traffic - && !split_dns && !openvpn_app_path.empty() && (dns.ipv4() || dns.ipv6())) + // Block DNS on all interfaces except the TAP adapter. + if (use_wfp && !split_dns && !block_local_traffic && !allow_local_dns_resolvers + && !openvpn_app_path.empty() && (dns.ipv4() || dns.ipv6())) { - create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, true, wfp)); - destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, true, wfp)); + create.add(new WFP::ActionBlock(openvpn_app_path, tap.index, WFP::Block::Dns, wfp)); + destroy.add(new WFP::ActionUnblock(openvpn_app_path, tap.index, WFP::Block::Dns, wfp)); } // flush DNS cache diff --git a/openvpn/tun/win/nrpt.hpp b/openvpn/tun/win/nrpt.hpp index 0df0f4a23..26b1ceaf2 100644 --- a/openvpn/tun/win/nrpt.hpp +++ b/openvpn/tun/win/nrpt.hpp @@ -424,11 +424,6 @@ class Nrpt */ void execute(std::ostream &log) override { - // Don't add anything if there is only one empty domain. This - // is the way to tell us that no '.' rules should be added - if (domains_.size() == 1 && domains_[0] == "") - return; - // Convert domains into a wide MULTI_SZ string std::wstring domains; if (domains_.empty()) diff --git a/openvpn/tun/win/wfp.hpp b/openvpn/tun/win/wfp.hpp index be8a1bdba..022206323 100644 --- a/openvpn/tun/win/wfp.hpp +++ b/openvpn/tun/win/wfp.hpp @@ -154,6 +154,16 @@ class WFP : public RC OPENVPN_EXCEPTION(wfp_error); + /** + * @brief Enum for type of local traffic to block + */ + enum class Block + { + All, + AllButLocalDns, + Dns, + }; + class ActionBase; /** @@ -169,12 +179,12 @@ class WFP : public RC void block(const std::wstring &openvpn_app_path, const NET_IFINDEX itf_index, - const bool dns_only, + const Block block_type, std::ostream &log) { unblock(log); wfp.reset(new WFP()); - wfp->block(openvpn_app_path, itf_index, dns_only, log); + wfp->block(openvpn_app_path, itf_index, block_type, log); } void unblock(std::ostream &log) @@ -209,7 +219,7 @@ class WFP : public RC { log << to_string() << std::endl; if (block_) - ctx_->block(openvpn_app_path_, itf_index_, dns_only_, log); + ctx_->block(openvpn_app_path_, itf_index_, block_type_, log); else ctx_->unblock(log); } @@ -225,12 +235,12 @@ class WFP : public RC ActionBase(const bool block, const std::wstring &openvpn_app_path, const NET_IFINDEX itf_index, - const bool dns_only, + const Block block_type, const Context::Ptr &ctx) : block_(block), openvpn_app_path_(openvpn_app_path), itf_index_(itf_index), - dns_only_(dns_only), + block_type_(block_type), ctx_(ctx) { } @@ -239,7 +249,7 @@ class WFP : public RC const bool block_; const std::wstring openvpn_app_path_; const NET_IFINDEX itf_index_; - const bool dns_only_; + const Block block_type_; Context::Ptr ctx_; }; @@ -247,9 +257,9 @@ class WFP : public RC { ActionBlock(const std::wstring &openvpn_app_path, const NET_IFINDEX itf_index, - const bool dns_only, + const Block block_type, const Context::Ptr &wfp) - : ActionBase(true, openvpn_app_path, itf_index, dns_only, wfp) + : ActionBase(true, openvpn_app_path, itf_index, block_type, wfp) { } }; @@ -258,9 +268,9 @@ class WFP : public RC { ActionUnblock(const std::wstring &openvpn_app_path, const NET_IFINDEX itf_index, - const bool dns_only, + const Block block_type, const Context::Ptr &wfp) - : ActionBase(false, openvpn_app_path, itf_index, dns_only, wfp) + : ActionBase(false, openvpn_app_path, itf_index, block_type, wfp) { } }; @@ -280,12 +290,12 @@ class WFP : public RC * * @param openvpn_app_path path to the openvpn executable * @param itf_index interface index of the VPN interface - * @param dns_only whether only port 53 should be blocked + * @param block_type which type of traffic should be blocked * @param log the log ostream to use for diagnostics */ void block(const std::wstring &openvpn_app_path, NET_IFINDEX itf_index, - bool dns_only, + Block block_type, std::ostream &log) { // WFP filter/conditions @@ -294,6 +304,7 @@ class WFP : public RC FWPM_FILTER_CONDITION0 match_openvpn = {0}; FWPM_FILTER_CONDITION0 match_port_53 = {0}; FWPM_FILTER_CONDITION0 match_interface = {0}; + FWPM_FILTER_CONDITION0 match_loopback = {0}; FWPM_FILTER_CONDITION0 match_not_loopback = {0}; UINT64 filterid = 0; @@ -334,6 +345,11 @@ class WFP : public RC match_interface.conditionValue.type = FWP_UINT64; match_interface.conditionValue.uint64 = &itf_luid.Value; + match_loopback.fieldKey = FWPM_CONDITION_FLAGS; + match_loopback.matchType = FWP_MATCH_FLAGS_ALL_SET; + match_loopback.conditionValue.type = FWP_UINT32; + match_loopback.conditionValue.uint32 = FWP_CONDITION_FLAG_IS_LOOPBACK; + match_not_loopback.fieldKey = FWPM_CONDITION_FLAGS; match_not_loopback.matchType = FWP_MATCH_FLAGS_NONE_SET; match_not_loopback.conditionValue.type = FWP_UINT32; @@ -351,11 +367,6 @@ class WFP : public RC filter.action.type = FWP_ACTION_PERMIT; filter.numFilterConditions = 1; condition[0] = match_openvpn; - if (dns_only) - { - filter.numFilterConditions = 2; - condition[1] = match_port_53; - } add_filter(&filter, NULL, &filterid); log << "permit IPv4 requests from OpenVPN app" << std::endl; @@ -366,17 +377,22 @@ class WFP : public RC log << "permit IPv6 requests from OpenVPN app" << std::endl; - // Filter #3 -- block IPv4 requests (except to loopback) from other apps + // Filter #3 -- block IPv4 (DNS) requests, except to loopback, from other apps filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; filter.action.type = FWP_ACTION_BLOCK; filter.weight.type = FWP_EMPTY; filter.numFilterConditions = 1; - condition[0] = dns_only ? match_port_53 : match_not_loopback; + condition[0] = match_not_loopback; + if (block_type == Block::Dns) + { + filter.numFilterConditions = 2; + condition[1] = match_port_53; + } add_filter(&filter, NULL, &filterid); log << "block IPv4 requests from other apps" << std::endl; - // Filter #4 -- block IPv6 requests (except to loopback) from other apps + // Filter #4 -- block IPv6 (DNS) requests, except to loopback, from other apps filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; add_filter(&filter, NULL, &filterid); log << "block IPv6 requests from other apps" << std::endl; @@ -387,11 +403,6 @@ class WFP : public RC filter.action.type = FWP_ACTION_PERMIT; filter.numFilterConditions = 1; condition[0] = match_interface; - if (dns_only) - { - filter.numFilterConditions = 2; - condition[1] = match_port_53; - } add_filter(&filter, NULL, &filterid); log << "allow IPv4 traffic from TAP" << std::endl; @@ -400,6 +411,25 @@ class WFP : public RC filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; add_filter(&filter, NULL, &filterid); log << "allow IPv6 traffic from TAP" << std::endl; + + if (block_type != Block::AllButLocalDns) + { + // Filter #7 -- block IPv4 DNS requests to loopback from other apps + filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + filter.action.type = FWP_ACTION_BLOCK; + filter.weight.type = FWP_EMPTY; + filter.numFilterConditions = 2; + condition[0] = match_loopback; + condition[1] = match_port_53; + add_filter(&filter, NULL, &filterid); + log << "block IPv4 DNS requests to loopback from other apps" << std::endl; + + + // Filter #8 -- block IPv6 DNS requests to loopback from other apps + filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + add_filter(&filter, NULL, &filterid); + log << "block IPv6 DNS requests to loopback from other apps" << std::endl; + } } /**