From ed98f2568b15d5548ef8f373b45a3f7078ce7a7e Mon Sep 17 00:00:00 2001 From: Sergey Abramchuk Date: Sun, 24 Feb 2019 15:02:57 +0300 Subject: [PATCH] Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from 6608878d5..934f4e741 934f4e741 Merge remote-tracking branch 'origin/qa' 8c87c7696 [UCONNECT-1027] use proper io_context when initializing AsyncResolve class c3026c65a Merge remote-tracking branch 'origin/qa' f33fe7665 [UCONNECT-1027] perform async DNS resolution in a detached thread 0c0af6781 [OVPN3-342] Generate ICMP "packet too big" reply c93af60a7 Move files from ovpn3-common to openvpn3 repo d5eeb78ed ClientAPI: print core version when starting 04de9c425 Merge branch 'qa' 2c0dbc6c3 buildep.py: add asio patching 600c68012 Allow updating auth-token during session 7391096b9 [OC-85] tunprop: exclude routes for additional remotes also on macOS 3587628d7 [OC-84] tunprop: exclude routes for additional remotes also on Windows 25471635d Revert "[UCONNECT-868] When no network is present pause instead of stopping" 5713ff34a Fixed some breakage caused by recent endian/ffs commits a9ce44a22 endian.hpp: break out endian compile-time tests to endian_platform.hpp 72181f9e7 [UCONNECT-868] When no network is present pause instead of stopping 10d636cfe version: switch to 3.2 git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 934f4e741f760160dc65a6f4b29af57bb5be8f93 --- client/ovpncli.cpp | 3 + openvpn/client/cliopt.hpp | 10 ++ openvpn/client/cliproto.hpp | 26 ++-- openvpn/client/remotelist.hpp | 91 ++++++++++++-- openvpn/common/version.hpp | 2 +- openvpn/dco/dcocli.hpp | 16 ++- openvpn/ip/csum.hpp | 171 +++++++++++++++++++++++++++ openvpn/ip/icmp4.hpp | 11 +- openvpn/ip/icmp6.hpp | 6 +- openvpn/ip/ping4.hpp | 113 ++++++++++++++++++ openvpn/ip/ping6.hpp | 171 +++++++++++++++++++++++++++ openvpn/ip/ptb.hpp | 136 +++++++++++++++++++++ openvpn/transport/client/httpcli.hpp | 18 ++- openvpn/transport/client/tcpcli.hpp | 18 ++- openvpn/transport/client/udpcli.hpp | 19 ++- win/buildep.py | 8 +- win/ovpn3-core.vcxproj | 4 + win/ovpn3-core.vcxproj.filters | 4 + 18 files changed, 759 insertions(+), 68 deletions(-) create mode 100644 openvpn/ip/csum.hpp create mode 100644 openvpn/ip/ping4.hpp create mode 100644 openvpn/ip/ping6.hpp create mode 100644 openvpn/ip/ptb.hpp diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index 97ffe2a..71182c9 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -858,6 +858,9 @@ namespace openvpn { #endif Log::Context log_context(this); #endif + + OPENVPN_LOG(ClientAPI::OpenVPNClient::platform()); + return do_connect(); } diff --git a/openvpn/client/cliopt.hpp b/openvpn/client/cliopt.hpp index 933bcc8..e399df3 100644 --- a/openvpn/client/cliopt.hpp +++ b/openvpn/client/cliopt.hpp @@ -395,7 +395,12 @@ namespace openvpn { tunconf->stats = cli_stats; tunconf->stop = config.stop; if (config.tun_persist) + { tunconf->tun_persist.reset(new TunMac::TunPersist(true, false, nullptr)); + tunconf->tun_prop.remote_bypass = true; + /* remote_list is required by remote_bypass to work */ + tunconf->tun_prop.remote_list = remote_list; + } client_lifecycle.reset(new MacLifeCycle); #ifdef OPENVPN_COMMAND_AGENT tunconf->tun_setup_factory = UnixCommandAgent::new_agent(opt); @@ -414,7 +419,12 @@ namespace openvpn { tunconf->stats = cli_stats; tunconf->stop = config.stop; if (config.tun_persist) + { tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr)); + tunconf->tun_prop.remote_bypass = true; + /* remote_list is required by remote_bypass to work */ + tunconf->tun_prop.remote_list = remote_list; + } #ifdef OPENVPN_COMMAND_AGENT tunconf->tun_setup_factory = WinCommandAgent::new_agent(opt); #endif diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index db5072d..e7b2f5f 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -376,15 +377,24 @@ namespace openvpn { // encrypt packet if (buf.size()) { - Base::data_encrypt(buf); - if (buf.size()) + const ProtoContext::Config& c = Base::conf(); + if (c.mss_inter > 0 && buf.size() > c.mss_inter) { - // send packet via transport to destination - OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf)); - if (transport->transport_send(buf)) - Base::update_last_sent(); - else if (halt) - return; + Ptb::generate_icmp_ptb(buf, c.mss_inter); + tun->tun_send(buf); + } + else + { + Base::data_encrypt(buf); + if (buf.size()) + { + // send packet via transport to destination + OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf)); + if (transport->transport_send(buf)) + Base::update_last_sent(); + else if (halt) + return; + } } } diff --git a/openvpn/client/remotelist.hpp b/openvpn/client/remotelist.hpp index 34d80a8..b6de7b5 100644 --- a/openvpn/client/remotelist.hpp +++ b/openvpn/client/remotelist.hpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -53,6 +54,79 @@ #endif namespace openvpn { + template + class AsyncResolvable: public virtual RC + { + private: + typedef RCPtr Ptr; + + openvpn_io::io_context& io_context; + std::unique_ptr asio_work; + + public: + AsyncResolvable(openvpn_io::io_context& io_context_arg) + : io_context(io_context_arg) + { + } + + 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) + { + // 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. + asio_work.reset(new AsioWork(io_context)); + + std::thread resolve_thread([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); + + openvpn_io::post(self->io_context, [self, results, error]() { + OPENVPN_ASYNC_HANDLER; + self->resolve_callback(error, results); + }); + + // the AsioWork can be released now that we have posted + // something else to the main io_context queue + self->asio_work.reset(); + }); + + // detach the thread so that the client won't need to wait for + // it to join. + resolve_thread.detach(); + } + + // 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() + { + asio_work.reset(); + } + }; + + typedef AsyncResolvable AsyncResolvableUDP; + typedef AsyncResolvable AsyncResolvableTCP; + class RemoteList : public RC { @@ -262,7 +336,7 @@ namespace openvpn { // 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 RC + class PreResolve : public virtual RC, AsyncResolvableTCP { public: typedef RCPtr Ptr; @@ -276,7 +350,7 @@ namespace openvpn { PreResolve(openvpn_io::io_context& io_context_arg, const RemoteList::Ptr& remote_list_arg, const SessionStats::Ptr& stats_arg) - : resolver(io_context_arg), + : AsyncResolvableTCP(io_context_arg), notify_callback(nullptr), remote_list(remote_list_arg), stats(stats_arg), @@ -312,7 +386,7 @@ namespace openvpn { { notify_callback = nullptr; index = 0; - resolver.cancel(); + async_resolve_cancel(); } private: @@ -335,14 +409,8 @@ namespace openvpn { } else { - // call into Asio to do the resolve operation OPENVPN_LOG_REMOTELIST("*** PreResolve RESOLVE on " << item.server_host << " : " << item.server_port); - resolver.async_resolve(item.server_host, item.server_port, - [self=Ptr(this)](const openvpn_io::error_code& error, openvpn_io::ip::tcp::resolver::results_type results) - { - OPENVPN_ASYNC_HANDLER; - self->resolve_callback(error, results); - }); + async_resolve_name(item.server_host, item.server_port); return; } } @@ -363,7 +431,7 @@ namespace openvpn { // callback on resolve completion void resolve_callback(const openvpn_io::error_code& error, - openvpn_io::ip::tcp::resolver::results_type results) + openvpn_io::ip::tcp::resolver::results_type results) override { if (notify_callback && index < remote_list->list.size()) { @@ -384,7 +452,6 @@ namespace openvpn { } } - openvpn_io::ip::tcp::resolver resolver; NotifyCallback* notify_callback; RemoteList::Ptr remote_list; SessionStats::Ptr stats; diff --git a/openvpn/common/version.hpp b/openvpn/common/version.hpp index 99720f8..e7dd107 100644 --- a/openvpn/common/version.hpp +++ b/openvpn/common/version.hpp @@ -24,5 +24,5 @@ #pragma once #ifndef OPENVPN_VERSION -#define OPENVPN_VERSION "3.git:master" +#define OPENVPN_VERSION "3.2 (qa:d87f5bbc04)" #endif diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index 37843e9..33eaace 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -117,7 +117,8 @@ namespace openvpn { class Client : public TransportClient, public TunClient, public KoRekey::Receiver, - public SessionStats::DCOTransportSource + public SessionStats::DCOTransportSource, + public AsyncResolvableUDP { friend class ClientConfig; @@ -473,7 +474,8 @@ namespace openvpn { Client(openvpn_io::io_context& io_context_arg, ClientConfig* config_arg, TransportClientParent* parent_arg) - : io_context(io_context_arg), + : AsyncResolvableUDP(io_context_arg), + io_context(io_context_arg), halt(false), state(new TunProp::State()), config(config_arg), @@ -498,17 +500,13 @@ namespace openvpn { else { transport_parent->transport_pre_resolve(); - udp().resolver.async_resolve(server_host, server_port, - [self=Ptr(this)](const openvpn_io::error_code& error, openvpn_io::ip::udp::resolver::results_type results) - { - self->do_resolve_udp(error, results); - }); + async_resolve_name(server_host, server_port); } } // called after DNS resolution has succeeded or failed - void do_resolve_udp(const openvpn_io::error_code& error, - openvpn_io::ip::udp::resolver::results_type results) + void resolve_callback(const openvpn_io::error_code& error, + openvpn_io::ip::udp::resolver::results_type results) { if (!halt) { diff --git a/openvpn/ip/csum.hpp b/openvpn/ip/csum.hpp new file mode 100644 index 0000000..87b64c3 --- /dev/null +++ b/openvpn/ip/csum.hpp @@ -0,0 +1,171 @@ +// 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-2018 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 . + +// IP checksum based on Linux kernel implementation + +#pragma once + +#include + +#include +#include +#include + +namespace openvpn { + namespace IPChecksum { + + inline std::uint16_t fold(std::uint32_t sum) + { + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return sum; + } + + inline std::uint16_t cfold(const std::uint32_t sum) + { + return ~fold(sum); + } + + inline std::uint32_t unfold(const std::uint16_t sum) + { + return sum; + } + + inline std::uint32_t cunfold(const std::uint16_t sum) + { + return ~unfold(sum); + } + + inline std::uint32_t compute(const std::uint8_t *buf, size_t len) + { + std::uint32_t result = 0; + + if (!len) + return 0; + + const bool odd = size_t(buf) & 1; + if (odd) + { +#ifdef OPENVPN_LITTLE_ENDIAN + result += (*buf << 8); +#else + result = *buf; +#endif + len--; + buf++; + } + + if (len >= 2) + { + if (size_t(buf) & 2) + { + result += *(std::uint16_t *)buf; + len -= 2; + buf += 2; + } + if (len >= 4) + { + const uint8_t *end = buf + (len & ~3); + std::uint32_t carry = 0; + do { + std::uint32_t w = *(std::uint32_t *)buf; + buf += 4; + result += carry; + result += w; + carry = (w > result); + } while (buf < end); + result += carry; + result = (result & 0xffff) + (result >> 16); + } + if (len & 2) + { + result += *(std::uint16_t *)buf; + buf += 2; + } + } + if (len & 1) + { +#ifdef OPENVPN_LITTLE_ENDIAN + result += *buf; +#else + result += (*buf << 8); +#endif + } + result = fold(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); + return result; + } + + inline std::uint32_t compute(const void *buf, const size_t len) + { + return compute((const std::uint8_t *)buf, len); + } + + inline std::uint32_t partial(const void *buf, const size_t len, const std::uint32_t sum) + { + std::uint32_t result = compute(buf, len); + + /* add in old sum, and carry.. */ + result += sum; + if (sum > result) + result += 1; + return result; + } + + inline std::uint32_t diff16(const std::uint32_t *old, + const std::uint32_t *new_, + const std::uint32_t oldsum) + { + std::uint32_t diff[8] = { ~old[0], ~old[1], ~old[2], ~old[3], + new_[0], new_[1], new_[2], new_[3] }; + return partial(diff, sizeof(diff), oldsum); + } + + inline std::uint32_t diff16(const std::uint8_t *old, + const std::uint8_t *new_, + const std::uint32_t oldsum) + { + return diff16((const std::uint32_t *)old, (const std::uint32_t *)new_, oldsum); + } + + inline std::uint32_t diff4(const std::uint32_t old, + const std::uint32_t new_, + const std::uint32_t oldsum) + { + std::uint32_t diff[2] = { ~old, new_ }; + return partial(diff, sizeof(diff), oldsum); + } + + inline std::uint32_t diff2(const std::uint16_t old, + const std::uint16_t new_, + const std::uint32_t oldsum) + { + std::uint16_t diff[2] = { std::uint16_t(~old), new_ }; + return partial(diff, sizeof(diff), oldsum); + } + + inline std::uint16_t checksum(const void *data, const size_t size) + { + return cfold(compute(data, size)); + } + } +} \ No newline at end of file diff --git a/openvpn/ip/icmp4.hpp b/openvpn/ip/icmp4.hpp index d08c028..2d20ade 100644 --- a/openvpn/ip/icmp4.hpp +++ b/openvpn/ip/icmp4.hpp @@ -33,8 +33,11 @@ namespace openvpn { struct ICMPv4 { enum { - ECHO_REQUEST = 8, - ECHO_REPLY = 0, + ECHO_REQUEST = 8, + ECHO_REPLY = 0, + DEST_UNREACH = 3, + FRAG_NEEDED = 4, + MIN_DATA_SIZE = 8 }; struct IPv4Header head; @@ -53,6 +56,10 @@ namespace openvpn { std::uint16_t id; std::uint16_t seq_num; }; + struct { + std::uint16_t unused; + std::uint16_t nexthop_mtu; + }; }; }; } diff --git a/openvpn/ip/icmp6.hpp b/openvpn/ip/icmp6.hpp index c23e03e..2813004 100644 --- a/openvpn/ip/icmp6.hpp +++ b/openvpn/ip/icmp6.hpp @@ -34,8 +34,9 @@ namespace openvpn { struct ICMPv6 { enum { - ECHO_REQUEST = 128, - ECHO_REPLY = 129, + ECHO_REQUEST = 128, + ECHO_REPLY = 129, + PACKET_TOO_BIG = 2 }; struct IPv6Header head; @@ -54,6 +55,7 @@ namespace openvpn { std::uint16_t id; std::uint16_t seq_num; }; + std::uint32_t mtu; }; }; } diff --git a/openvpn/ip/ping4.hpp b/openvpn/ip/ping4.hpp new file mode 100644 index 0000000..50eaaff --- /dev/null +++ b/openvpn/ip/ping4.hpp @@ -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-2018 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 . + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace openvpn { + namespace Ping4 { + + inline void generate_echo_request(Buffer& buf, + const IPv4::Addr& src, + const IPv4::Addr& dest, + const void *extra_data, + const size_t extra_data_size, + const unsigned int id, + const unsigned int seq_num, + const size_t total_size, + std::string* log_info) + { + const unsigned int data_size = std::max(int(extra_data_size), int(total_size) - int(sizeof(ICMPv4))); + + if (log_info) + *log_info = "PING4 " + src.to_string() + " -> " + dest.to_string() + " id=" + std::to_string(id) + " seq_num=" + std::to_string(seq_num) + " data_size=" + std::to_string(data_size); + + std::uint8_t *b = buf.write_alloc(sizeof(ICMPv4) + data_size); + ICMPv4 *icmp = (ICMPv4 *)b; + + // IP Header + icmp->head.version_len = IPv4Header::ver_len(4, sizeof(IPv4Header)); + icmp->head.tos = 0; + icmp->head.tot_len = htons(sizeof(ICMPv4) + data_size); + icmp->head.id = 0; + icmp->head.frag_off = 0; + icmp->head.ttl = 64; + icmp->head.protocol = IPCommon::ICMPv4; + icmp->head.check = 0; + icmp->head.saddr = src.to_uint32_net(); + icmp->head.daddr = dest.to_uint32_net(); + icmp->head.check = IPChecksum::checksum(b, sizeof(IPv4Header)); + + // ICMP header + icmp->type = ICMPv4::ECHO_REQUEST; + icmp->code = 0; + icmp->checksum = 0; + icmp->id = ntohs(id); + icmp->seq_num = ntohs(seq_num); + + // Data + std::uint8_t *data = b + sizeof(ICMPv4); + for (size_t i = 0; i < data_size; ++i) + data[i] = (std::uint8_t)i; + + // Extra data + std::memcpy(data, extra_data, extra_data_size); + + // ICMP checksum + icmp->checksum = IPChecksum::checksum(b + sizeof(IPv4Header), + sizeof(ICMPv4) - sizeof(IPv4Header) + data_size); + + //std::cout << dump_hex(buf); + } + + // assumes that buf is a validated ECHO_REQUEST + inline void generate_echo_reply(Buffer& buf, + std::string* log_info) + { + if (buf.size() < sizeof(ICMPv4)) + { + if (log_info) + *log_info = "Invalid ECHO4_REQUEST"; + return; + } + + ICMPv4* icmp = (ICMPv4*) buf.c_data(); + std::swap(icmp->head.saddr, icmp->head.daddr); + const std::uint16_t old_type_code = icmp->type_code; + icmp->type = ICMPv4::ECHO_REPLY; + icmp->checksum = IPChecksum::cfold(IPChecksum::diff2(old_type_code, icmp->type_code, IPChecksum::cunfold(icmp->checksum))); + + if (log_info) + *log_info = "ECHO4_REPLY size=" + std::to_string(buf.size()) + ' ' + IPv4::Addr::from_uint32_net(icmp->head.saddr).to_string() + " -> " + IPv4::Addr::from_uint32_net(icmp->head.daddr).to_string(); + } + } +} \ No newline at end of file diff --git a/openvpn/ip/ping6.hpp b/openvpn/ip/ping6.hpp new file mode 100644 index 0000000..19933ee --- /dev/null +++ b/openvpn/ip/ping6.hpp @@ -0,0 +1,171 @@ +// 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-2018 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 . + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace openvpn { + namespace Ping6 { + + inline static const std::uint16_t* get_addr16(const struct in6_addr *addr) + { +#if defined(_MSC_VER) + return addr->u.Word; +#elif defined(__APPLE__) + return addr->__u6_addr.__u6_addr16; +#else + return addr->s6_addr16; +#endif + } + + inline std::uint16_t csum_ipv6_pseudo(const struct in6_addr *saddr, + const struct in6_addr *daddr, + const std::uint32_t len, + const std::uint16_t proto, + std::uint32_t sum) + { + int carry = 0; + std::uint32_t val = 0; + + const std::uint16_t* addr = get_addr16(saddr); + for (int i = 0; i < 4; ++i) + { + val = (std::uint32_t)(addr[i * 2] << 16) + addr[i * 2 + 1]; + sum += val; + carry = (sum < val); + sum += carry; + } + + addr = get_addr16(daddr); + for (int i = 0; i < 4; ++i) + { + val = (std::uint32_t)(addr[i * 2] << 16) + addr[i * 2 + 1]; + sum += val; + carry = (sum < val); + sum += carry; + } + + const std::uint32_t ulen = (std::uint32_t)htonl((std::uint32_t) len); + sum += ulen; + carry = (sum < ulen); + sum += carry; + + const std::uint32_t uproto = (std::uint32_t)htonl(proto); + sum += uproto; + carry = (sum < uproto); + sum += carry; + + return IPChecksum::cfold(sum); + } + + // len must be >= sizeof(ICMPv6) + inline std::uint16_t csum_icmp(const ICMPv6 *icmp, const size_t len) + { + return csum_ipv6_pseudo(&icmp->head.saddr, + &icmp->head.daddr, + len - sizeof(IPv6Header), + IPCommon::ICMPv6, + IPChecksum::compute((std::uint8_t *)icmp + sizeof(IPv6Header), len - sizeof(IPv6Header))); + } + + inline void generate_echo_request(Buffer& buf, + const IPv6::Addr& src, + const IPv6::Addr& dest, + const void *extra_data, + const size_t extra_data_size, + const unsigned int id, + const unsigned int seq_num, + const size_t total_size, + std::string* log_info) + { + const unsigned int data_size = std::max(int(extra_data_size), int(total_size) - int(sizeof(ICMPv6))); + + if (log_info) + *log_info = "PING6 " + src.to_string() + " -> " + dest.to_string() + " id=" + std::to_string(id) + " seq_num=" + std::to_string(seq_num) + " data_size=" + std::to_string(data_size); + + std::uint8_t *b = buf.write_alloc(sizeof(ICMPv6) + data_size); + ICMPv6 *icmp = (ICMPv6 *)b; + + // IP Header + icmp->head.version_prio = (6 << 4); + icmp->head.flow_lbl[0] = 0; + icmp->head.flow_lbl[1] = 0; + icmp->head.flow_lbl[2] = 0; + icmp->head.payload_len = htons(sizeof(ICMPv6) - sizeof(IPv6Header) + data_size); + icmp->head.nexthdr = IPCommon::ICMPv6; + icmp->head.hop_limit = 64; + icmp->head.saddr = src.to_in6_addr(); + icmp->head.daddr = dest.to_in6_addr(); + + // ICMP header + icmp->type = ICMPv6::ECHO_REQUEST; + icmp->code = 0; + icmp->checksum = 0; + icmp->id = ntohs(id); + icmp->seq_num = ntohs(seq_num); + + // Data + std::uint8_t *data = b + sizeof(ICMPv6); + for (size_t i = 0; i < data_size; ++i) + data[i] = (std::uint8_t)i; + + // Extra data + std::memcpy(data, extra_data, extra_data_size); + + // ICMP checksum + icmp->checksum = csum_icmp(icmp, sizeof(ICMPv6) + data_size); + + //std::cout << dump_hex(buf); + } + + // assumes that buf is a validated ECHO_REQUEST + inline void generate_echo_reply(Buffer& buf, + std::string* log_info) + { + if (buf.size() < sizeof(ICMPv6)) + { + if (log_info) + *log_info = "Invalid ECHO6_REQUEST"; + return; + } + + ICMPv6* icmp = (ICMPv6*) buf.c_data(); + std::swap(icmp->head.saddr, icmp->head.daddr); + const std::uint16_t old_type_code = icmp->type_code; + icmp->type = ICMPv6::ECHO_REPLY; + icmp->checksum = IPChecksum::cfold(IPChecksum::diff2(old_type_code, icmp->type_code, IPChecksum::cunfold(icmp->checksum))); + + if (log_info) + *log_info = "ECHO6_REPLY size=" + std::to_string(buf.size()) + ' ' + IPv6::Addr::from_in6_addr(&icmp->head.saddr).to_string() + " -> " + IPv6::Addr::from_in6_addr(&icmp->head.daddr).to_string(); + } + } +} \ No newline at end of file diff --git a/openvpn/ip/ptb.hpp b/openvpn/ip/ptb.hpp new file mode 100644 index 0000000..1d73d28 --- /dev/null +++ b/openvpn/ip/ptb.hpp @@ -0,0 +1,136 @@ +// 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 . + +// Generates ICMP "packet too big" response + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvpn { + class Ptb { + public: + static void generate_icmp_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + if (buf.empty()) + return; + + switch (IPCommon::version(buf[0])) + { + case IPCommon::IPv4: + if (buf.length() <= sizeof(struct IPv4Header)) + break; + + generate_icmp4_ptb(buf, nexthop_mtu); + break; + + case IPCommon::IPv6: + if (buf.length() <= sizeof(struct IPv6Header)) + break; + + generate_icmp6_ptb(buf, nexthop_mtu); + break; + } + } + + private: + static void generate_icmp6_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + // ICMPv6 data includes original IPv6 header and as many bytes of payload as possible + int data_size = std::min(buf.length(), (size_t)(nexthop_mtu - sizeof(ICMPv6))); + + // sanity check + // we use headroom for adding IPv6 + ICMPv6 headers + if ((buf.offset() < sizeof(ICMPv6)) || (buf.capacity() < (sizeof(ICMPv6) + data_size))) + return; + + IPv6Header* ipv6 = (IPv6Header*)buf.c_data(); + + uint8_t *b = buf.prepend_alloc(sizeof(ICMPv6)); + ICMPv6 *icmp = (ICMPv6 *)b; + + // IPv6 header + icmp->head.version_prio = (6 << 4); + icmp->head.flow_lbl[0] = 0; + icmp->head.flow_lbl[1] = 0; + icmp->head.flow_lbl[2] = 0; + icmp->head.payload_len = htons(sizeof(ICMPv6) - sizeof(IPv6Header) + data_size); + icmp->head.nexthdr = IPCommon::ICMPv6; + icmp->head.hop_limit = 64; + icmp->head.saddr = ipv6->daddr; + icmp->head.daddr = ipv6->saddr; + + // ICMP header + icmp->type = ICMPv6::PACKET_TOO_BIG; + icmp->code = 0; + icmp->mtu = htonl(nexthop_mtu); + icmp->checksum = 0; + icmp->checksum = Ping6::csum_icmp(icmp, sizeof(ICMPv6) + data_size); + + buf.set_size(sizeof(ICMPv6) + data_size); + } + + static void generate_icmp4_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + // ICMP data includes original IP header and first 8 bytes of payload + int data_size = sizeof(IPv4Header) + ICMPv4::MIN_DATA_SIZE; + + // sanity check + // we use headroom for adding IPv4 + ICMPv4 headers + if ((buf.offset() < sizeof(ICMPv4)) || (buf.capacity() < (sizeof(ICMPv4) + data_size))) + return; + + IPv4Header* ipv4 = (IPv4Header*)buf.c_data(); + + uint8_t *b = buf.prepend_alloc(sizeof(ICMPv4)); + ICMPv4 *icmp = (ICMPv4 *)b; + + icmp->head.saddr = ipv4->daddr; + icmp->head.daddr = ipv4->saddr; + icmp->head.version_len = IPv4Header::ver_len(IPCommon::IPv4, sizeof(IPv4Header)); + icmp->head.tos = 0; + icmp->head.tot_len = htons(sizeof(ICMPv4) + data_size); + icmp->head.id = 0; + icmp->head.frag_off = 0; + icmp->head.ttl = 64; + icmp->head.protocol = IPCommon::ICMPv4; + icmp->head.check = 0; + icmp->head.check = IPChecksum::checksum(b, sizeof(IPv4Header)); + + icmp->type = ICMPv4::DEST_UNREACH; + icmp->code = ICMPv4::FRAG_NEEDED; + icmp->unused = 0; + icmp->nexthop_mtu = htons(nexthop_mtu); + icmp->checksum = 0; + icmp->checksum = IPChecksum::checksum(b + sizeof(IPv4Header), sizeof(ICMPv4) - sizeof(IPv4Header) + data_size); + + buf.set_size(sizeof(ICMPv4) + data_size); + } + }; +} diff --git a/openvpn/transport/client/httpcli.hpp b/openvpn/transport/client/httpcli.hpp index 8b7f15d..46d8795 100644 --- a/openvpn/transport/client/httpcli.hpp +++ b/openvpn/transport/client/httpcli.hpp @@ -209,7 +209,7 @@ namespace openvpn { {} }; - class Client : public TransportClient + class Client : public TransportClient, AsyncResolvableTCP { typedef RCPtr Ptr; @@ -245,12 +245,8 @@ namespace openvpn { { // resolve it parent->transport_pre_resolve(); - resolver.async_resolve(proxy_host, proxy_port, - [self=Ptr(this)](const openvpn_io::error_code& error, openvpn_io::ip::tcp::resolver::results_type results) - { - OPENVPN_ASYNC_HANDLER; - self->do_resolve_(error, results); - }); + + async_resolve_name(proxy_host, proxy_port); } } } @@ -340,7 +336,7 @@ namespace openvpn { Client(openvpn_io::io_context& io_context_arg, ClientConfig* config_arg, TransportClientParent* parent_arg) - : io_context(io_context_arg), + : AsyncResolvableTCP(io_context_arg), socket(io_context_arg), config(config_arg), parent(parent_arg), @@ -860,12 +856,13 @@ namespace openvpn { socket.close(); resolver.cancel(); + async_resolve_cancel(); } } // do DNS resolve - void do_resolve_(const openvpn_io::error_code& error, - openvpn_io::ip::tcp::resolver::results_type results) + void resolve_callback(const openvpn_io::error_code& error, + openvpn_io::ip::tcp::resolver::results_type results) override { if (!halt) { @@ -1008,7 +1005,6 @@ namespace openvpn { std::string server_host; std::string server_port; - openvpn_io::io_context& io_context; openvpn_io::ip::tcp::socket socket; ClientConfig::Ptr config; TransportClientParent* parent; diff --git a/openvpn/transport/client/tcpcli.hpp b/openvpn/transport/client/tcpcli.hpp index a1d3831..c1f18d7 100644 --- a/openvpn/transport/client/tcpcli.hpp +++ b/openvpn/transport/client/tcpcli.hpp @@ -74,7 +74,7 @@ namespace openvpn { {} }; - class Client : public TransportClient + class Client : public TransportClient, AsyncResolvableTCP { typedef RCPtr Ptr; @@ -102,12 +102,8 @@ namespace openvpn { else { parent->transport_pre_resolve(); - resolver.async_resolve(server_host, server_port, - [self=Ptr(this)](const openvpn_io::error_code& error, openvpn_io::ip::tcp::resolver::results_type results) - { - OPENVPN_ASYNC_HANDLER; - self->do_resolve_(error, results); - }); + + async_resolve_name(server_host, server_port); } } } @@ -175,7 +171,8 @@ namespace openvpn { Client(openvpn_io::io_context& io_context_arg, ClientConfig* config_arg, TransportClientParent* parent_arg) - : io_context(io_context_arg), + : AsyncResolvableTCP(io_context_arg), + io_context(io_context_arg), socket(io_context_arg), config(config_arg), parent(parent_arg), @@ -249,12 +246,13 @@ namespace openvpn { socket.close(); resolver.cancel(); + async_resolve_cancel(); } } // do DNS resolve - void do_resolve_(const openvpn_io::error_code& error, - openvpn_io::ip::tcp::resolver::results_type results) + void resolve_callback(const openvpn_io::error_code& error, + openvpn_io::ip::tcp::resolver::results_type results) override { if (!halt) { diff --git a/openvpn/transport/client/udpcli.hpp b/openvpn/transport/client/udpcli.hpp index b5887b2..be94e66 100644 --- a/openvpn/transport/client/udpcli.hpp +++ b/openvpn/transport/client/udpcli.hpp @@ -74,7 +74,7 @@ namespace openvpn { {} }; - class Client : public TransportClient + class Client : public TransportClient, AsyncResolvableUDP { typedef RCPtr Ptr; @@ -101,16 +101,11 @@ namespace openvpn { { openvpn_io::error_code error; openvpn_io::ip::udp::resolver::results_type results = resolver.resolve(server_host, server_port, error); - do_resolve_(error, results); + resolve_callback(error, results); } else { - resolver.async_resolve(server_host, server_port, - [self=Ptr(this)](const openvpn_io::error_code& error, openvpn_io::ip::udp::resolver::results_type results) - { - OPENVPN_ASYNC_HANDLER; - self->do_resolve_(error, results); - }); + async_resolve_name(server_host, server_port); } } } @@ -181,7 +176,8 @@ namespace openvpn { Client(openvpn_io::io_context& io_context_arg, ClientConfig* config_arg, TransportClientParent* parent_arg) - : socket(io_context_arg), + : AsyncResolvableUDP(io_context_arg), + socket(io_context_arg), config(config_arg), parent(parent_arg), resolver(io_context_arg), @@ -236,12 +232,13 @@ namespace openvpn { impl->stop(); socket.close(); resolver.cancel(); + async_resolve_cancel(); } } // called after DNS resolution has succeeded or failed - void do_resolve_(const openvpn_io::error_code& error, - openvpn_io::ip::udp::resolver::results_type results) + void resolve_callback(const openvpn_io::error_code& error, + openvpn_io::ip::udp::resolver::results_type results) override { if (!halt) { diff --git a/win/buildep.py b/win/buildep.py index 1d48489..c01eb6e 100644 --- a/win/buildep.py +++ b/win/buildep.py @@ -25,10 +25,14 @@ def build_asio(parms): sys.exit("Checksum mismatch, expected %s, actual %s" % (parms["ASIO_CSUM"], checksum)) with ModEnv('PATH', "%s\\bin;%s" % (parms.get('GIT'), os.environ['PATH'])): extract(arch_path, "gz") - rmtree("asio") - os.rename("asio-%s" % asio_ver, "asio") + dist = os.path.realpath('asio') + rmtree(dist) + os.rename("asio-%s" % asio_ver, dist) rm(arch_path) + for patch_file in glob.glob(os.path.join(parms.get('OVPN3'), "core", "deps", "asio", "patches", "*.patch")): + call(["git", "apply", "--whitespace=nowarn", "--ignore-space-change", "--verbose", patch_file], cwd=dist) + def build_mbedtls(parms): print "**************** MBEDTLS" with Cd(build_dir(parms)): diff --git a/win/ovpn3-core.vcxproj b/win/ovpn3-core.vcxproj index 9f82684..040f60c 100644 --- a/win/ovpn3-core.vcxproj +++ b/win/ovpn3-core.vcxproj @@ -233,6 +233,7 @@ + @@ -240,6 +241,9 @@ + + + diff --git a/win/ovpn3-core.vcxproj.filters b/win/ovpn3-core.vcxproj.filters index b57ff85..a6ce3b7 100644 --- a/win/ovpn3-core.vcxproj.filters +++ b/win/ovpn3-core.vcxproj.filters @@ -424,6 +424,10 @@ + + + +