diff --git a/client/common/changelog.txt b/client/common/changelog.txt index 7cdf289d..6e49c3ba 100644 --- a/client/common/changelog.txt +++ b/client/common/changelog.txt @@ -1,3 +1,10 @@ +2.10.12 (11/06/2024) +All: + * Improved OpenVPN and WireGuard anti-censorship. #1023 + * Improved wsnet to use TLS padding when anti-censorship is enabled. #1024 + * Fixed wsnet thread synchronization issue that could cause a crash. #1027 + + 2.10.11 (03/06/2024) All: * Fixed wsnet bug sometimes causing the library to freeze. #1012 diff --git a/client/common/version/windscribe_version.h b/client/common/version/windscribe_version.h index 109372f0..a4470200 100644 --- a/client/common/version/windscribe_version.h +++ b/client/common/version/windscribe_version.h @@ -2,11 +2,11 @@ #define WINDSCRIBE_MAJOR_VERSION 2 #define WINDSCRIBE_MINOR_VERSION 10 -#define WINDSCRIBE_BUILD_VERSION 11 +#define WINDSCRIBE_BUILD_VERSION 12 // only one of these should be enabled; neither -> stable -//#define WINDSCRIBE_IS_BETA -#define WINDSCRIBE_IS_GUINEA_PIG +#define WINDSCRIBE_IS_BETA +//#define WINDSCRIBE_IS_GUINEA_PIG #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) diff --git a/client/engine/engine/connectionmanager/connectionmanager.cpp b/client/engine/engine/connectionmanager/connectionmanager.cpp index 5b89947b..06ed7b62 100644 --- a/client/engine/engine/connectionmanager/connectionmanager.cpp +++ b/client/engine/engine/connectionmanager/connectionmanager.cpp @@ -137,8 +137,8 @@ QString ConnectionManager::udpStuffingWithNtp(const QString &ip, const quint16 p // Send "secret" packet first udpSocket.writeDatagram(simpleBuf, sizeof(simpleBuf), QHostAddress(ip), port); - // Send NTP packet, repeat up to 5 times. Bounded argument is exclusive. - for (int i=0; i<=QRandomGenerator::global()->bounded(5); i++) { + // Send NTP packet, repeat up to 40 times. Bounded argument is exclusive. + for (int i=0; i<=20+QRandomGenerator::global()->bounded(20); i++) { *ntpRand = QRandomGenerator::global()->generate64(); udpSocket.writeDatagram(ntpBuf, sizeof(ntpBuf), QHostAddress(ip), port); } diff --git a/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp b/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp index eb01bb0d..1406fbbe 100644 --- a/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp +++ b/libs/wsnet/src/httpnetworkmanager/curlnetworkmanager.cpp @@ -144,22 +144,26 @@ void CurlNetworkManager::run() curl_off_t totalTime; curl_easy_getinfo(curlEasyHandle, CURLINFO_TOTAL_TIME_T, &totalTime); + curl_multi_remove_handle(multiHandle_, curlEasyHandle); - std::uint64_t id = *pointerId; - auto it = activeRequests_.find(id); - assert(it != activeRequests_.end()); - assert(it->second->curlEasyHandle == curlEasyHandle); + CURLcode result = curlMsg->data.result; - if (curlMsg->data.result != CURLE_OK) { - spdlog::debug("Curl request error: {}", curl_easy_strerror(curlMsg->data.result)); + if (result != CURLE_OK) { + spdlog::debug("Curl request error: {}", curl_easy_strerror(result)); } - finishedCallback_(id, curlMsg->data.result == CURLE_OK); - + std::uint64_t id; //remove request from activeRequests - curl_multi_remove_handle(multiHandle_, curlEasyHandle); - delete it->second; - activeRequests_.erase(id); + { + std::lock_guard locker(mutex_); + id = *pointerId; + auto it = activeRequests_.find(id); + assert(it != activeRequests_.end()); + assert(it->second->curlEasyHandle == curlEasyHandle); + delete it->second; + activeRequests_.erase(id); + } + finishedCallback_(id, result == CURLE_OK); } } while(curlMsg); diff --git a/libs/wsnet/src/pingmanager/pingmanager.cpp b/libs/wsnet/src/pingmanager/pingmanager.cpp index b2299827..6fccca0e 100644 --- a/libs/wsnet/src/pingmanager/pingmanager.cpp +++ b/libs/wsnet/src/pingmanager/pingmanager.cpp @@ -10,9 +10,10 @@ namespace wsnet { -PingManager::PingManager(boost::asio::io_context &io_context, WSNetHttpNetworkManager *httpNetworkManager) : +PingManager::PingManager(boost::asio::io_context &io_context, WSNetHttpNetworkManager *httpNetworkManager, WSNetAdvancedParameters *advancedParameters) : io_context_(io_context), - httpNetworkManager_(httpNetworkManager) + httpNetworkManager_(httpNetworkManager), + advancedParameters_(advancedParameters) { #ifndef _WIN32 @@ -82,7 +83,7 @@ void PingManager::onPingMethodFinished(std::uint64_t id) IPingMethod *PingManager::createPingMethod(std::uint64_t id, const std::string &ip, const std::string &hostname, PingType pingType, PingFinishedCallback callback) { if (pingType == PingType::kHttp) { - return new PingMethodHttp(httpNetworkManager_, id, ip, hostname, false, callback, std::bind(&PingManager::onPingMethodFinished, this, std::placeholders::_1)); + return new PingMethodHttp(httpNetworkManager_, id, ip, hostname, false, callback, std::bind(&PingManager::onPingMethodFinished, this, std::placeholders::_1), advancedParameters_); } else if (pingType == PingType::kIcmp) { #ifdef _WIN32 diff --git a/libs/wsnet/src/pingmanager/pingmanager.h b/libs/wsnet/src/pingmanager/pingmanager.h index 6821efbf..5fb62b7a 100644 --- a/libs/wsnet/src/pingmanager/pingmanager.h +++ b/libs/wsnet/src/pingmanager/pingmanager.h @@ -5,6 +5,7 @@ #include #include "WSNetPingManager.h" #include "WSNetHttpNetworkManager.h" +#include "WSNetAdvancedParameters.h" #include "ipingmethod.h" #ifdef _WIN32 @@ -19,7 +20,7 @@ namespace wsnet { class PingManager : public WSNetPingManager { public: - explicit PingManager(boost::asio::io_context &io_context, WSNetHttpNetworkManager *httpNetworkManager); + explicit PingManager(boost::asio::io_context &io_context, WSNetHttpNetworkManager *httpNetworkManager, WSNetAdvancedParameters *advancedParameters); virtual ~PingManager(); std::shared_ptr ping(const std::string &ip, const std::string &hostname, @@ -30,6 +31,7 @@ class PingManager : public WSNetPingManager private: boost::asio::io_context &io_context_; WSNetHttpNetworkManager *httpNetworkManager_; + WSNetAdvancedParameters *advancedParameters_; #ifdef _WIN32 // Required for ICMP pings for Windows system diff --git a/libs/wsnet/src/pingmanager/pingmethod_http.cpp b/libs/wsnet/src/pingmanager/pingmethod_http.cpp index 95e4ce3c..900b4520 100644 --- a/libs/wsnet/src/pingmanager/pingmethod_http.cpp +++ b/libs/wsnet/src/pingmanager/pingmethod_http.cpp @@ -7,9 +7,10 @@ namespace wsnet { PingMethodHttp::PingMethodHttp(WSNetHttpNetworkManager *httpNetworkManager, std::uint64_t id, const std::string &ip, const std::string &hostname, bool isParallelPing, - PingFinishedCallback callback, PingMethodFinishedCallback pingMethodFinishedCallback) : + PingFinishedCallback callback, PingMethodFinishedCallback pingMethodFinishedCallback, WSNetAdvancedParameters *advancedParameters) : IPingMethod(id, ip, hostname, isParallelPing, callback, pingMethodFinishedCallback), - httpNetworkManager_(httpNetworkManager) + httpNetworkManager_(httpNetworkManager), + advancedParameters_(advancedParameters) { } @@ -42,6 +43,7 @@ void PingMethodHttp::ping(bool isFromDisconnectedVpnState) httpRequest->setOverrideIp(ip_); // We add all ips to the firewall exceptions at once in the client before we ping, so there is no need to do it in HttpNetworkManager httpRequest->setIsWhiteListIps(false); + httpRequest->setExtraTLSPadding(advancedParameters_->isAPIExtraTLSPadding()); using namespace std::placeholders; request_ = httpNetworkManager_->executeRequest(httpRequest, 0, std::bind(&PingMethodHttp::onNetworkRequestFinished, this, _1, _2, _3, _4)); } diff --git a/libs/wsnet/src/pingmanager/pingmethod_http.h b/libs/wsnet/src/pingmanager/pingmethod_http.h index 01ffed30..0e3517ad 100644 --- a/libs/wsnet/src/pingmanager/pingmethod_http.h +++ b/libs/wsnet/src/pingmanager/pingmethod_http.h @@ -2,6 +2,7 @@ #include "ipingmethod.h" #include "WSNetHttpNetworkManager.h" +#include "WSNetAdvancedParameters.h" namespace wsnet { @@ -10,7 +11,7 @@ class PingMethodHttp : public IPingMethod { public: PingMethodHttp(WSNetHttpNetworkManager *httpNetworkManager, std::uint64_t id, const std::string &ip, const std::string &hostname, bool isParallelPing, - PingFinishedCallback callback, PingMethodFinishedCallback pingMethodFinishedCallback); + PingFinishedCallback callback, PingMethodFinishedCallback pingMethodFinishedCallback, WSNetAdvancedParameters *advancedParameters); virtual ~PingMethodHttp(); void ping(bool isFromDisconnectedVpnState) override; @@ -19,6 +20,7 @@ class PingMethodHttp : public IPingMethod enum { PING_TIMEOUT = 2000 }; WSNetHttpNetworkManager *httpNetworkManager_; std::shared_ptr request_; + WSNetAdvancedParameters *advancedParameters_; void onNetworkRequestFinished(std::uint64_t requestId, std::uint32_t elapsedMs, NetworkError errCode, const std::string &data); int parseReplyString(const std::string &data); diff --git a/libs/wsnet/src/wsnet.cpp b/libs/wsnet/src/wsnet.cpp index dd06a966..a7a73521 100644 --- a/libs/wsnet/src/wsnet.cpp +++ b/libs/wsnet/src/wsnet.cpp @@ -63,7 +63,7 @@ class WSNet_impl : public WSNet advancedParameters_ = std::make_shared(); serverAPI_ = std::make_shared(io_context_, httpNetworkManager_.get(), failoverContainer_.get(), serverApiSettings, advancedParameters_.get(), connectState_); emergencyConnect_ = std::make_shared(io_context_, failoverContainer_.get(), dnsResolver_.get()); - pingManager_ = std::make_shared(io_context_, httpNetworkManager_.get()); + pingManager_ = std::make_shared(io_context_, httpNetworkManager_.get(), advancedParameters_.get()); utils_ = std::make_shared(io_context_, httpNetworkManager_.get(), failoverContainer_.get(), advancedParameters_.get()); return true; diff --git a/tools/vcpkg/ports/openvpn/anti-censorship.patch b/tools/vcpkg/ports/openvpn/anti-censorship.patch old mode 100644 new mode 100755 index fc1e7a83..b95a03f7 --- a/tools/vcpkg/ports/openvpn/anti-censorship.patch +++ b/tools/vcpkg/ports/openvpn/anti-censorship.patch @@ -1,5 +1,18 @@ +diff --git a/src/openvpn/init.c b/src/openvpn/init.c +index 079c4f5e..bad5e0fc 100644 +--- a/src/openvpn/init.c ++++ b/src/openvpn/init.c +@@ -3448,6 +3448,8 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) + /* let the TLS engine know if keys have to be installed in DCO or not */ + to.dco_enabled = dco_enabled(options); + ++ to.tcp_split_reset = !!(options->sockflags & SF_TCP_SPLITRESET && options->ce.proto != PROTO_UDP); ++ + /* + * Initialize OpenVPN's master TLS-mode object. + */ diff --git a/src/openvpn/options.c b/src/openvpn/options.c -index 7ca77a8e..1db60200 100644 +index 7ca77a8e..6a1c382b 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -3407,7 +3407,7 @@ options_postprocess_mutate_invariant(struct options *options) @@ -15,24 +28,24 @@ index 7ca77a8e..1db60200 100644 VERIFY_PERMISSION(OPT_P_GENERAL); options->server_flags |= SF_TCP_NODELAY_HELPER; } -+ else if (streq(p[0], "tcp-split-reset") && !p[1]) ++ else if (streq(p[0], "udp-stuffing") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); -+ options->sockflags |= SF_TCP_SPLITRESET; ++ options->sockflags |= SF_UDP_STUFFING; + } -+ else if (streq(p[0], "udp-stuffing") && !p[1]) ++ else if (streq(p[0], "tcp-split-reset")) + { + VERIFY_PERMISSION(OPT_P_GENERAL); -+ options->sockflags |= SF_UDP_STUFFING; ++ options->sockflags |= SF_TCP_SPLITRESET; + } else if (streq(p[0], "stale-routes-check") && p[1] && !p[3]) { int ageing_time, check_interval; diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c -index 91a6d53d..1110c5a4 100644 +index 91a6d53d..0c2b1899 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c -@@ -3409,7 +3409,43 @@ link_socket_write_tcp(struct link_socket *sock, +@@ -3409,7 +3409,51 @@ link_socket_write_tcp(struct link_socket *sock, dmsg(D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset); ASSERT(len <= sock->stream_buf.maxlen); len = htonps(len); @@ -43,32 +56,40 @@ index 91a6d53d..1110c5a4 100644 + + if (sock->sockflags & SF_TCP_SPLITRESET) + { -+ int saved_len; -+ int size; + if (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 + || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) + { -+ saved_len = buf->len; -+ buf->len = 1; ++ int size = 0; ++ uint8_t split_piece_len, split_piece_len_cur; ++ int left; ++ ++ rand_bytes((uint8_t*)&split_piece_len, sizeof(split_piece_len)); ++ split_piece_len = (split_piece_len % 8) + 2; ++ left = buf->len; + + socket_set_tcp_nodelay(sock->sd, 1); ++ while (left) { ++ split_piece_len_cur = (split_piece_len > left) ? left : split_piece_len; ++ buf->len = split_piece_len_cur; +#ifdef _WIN32 -+ size = link_socket_write_win32(sock, buf, to); ++ size += link_socket_write_win32(sock, buf, to); +#else -+ size = link_socket_write_tcp_posix(sock, buf, to); ++ size += link_socket_write_tcp_posix(sock, buf, to); +#endif -+ if (!(sock->sockflags & SF_TCP_NODELAY)) -+ { -+ socket_set_tcp_nodelay(sock->sd, 0); -+ } -+ buf->len = saved_len; -+ buf_advance(buf, 1); -+ ++ buf->len = left; ++ left -= split_piece_len_cur; ++ buf_advance(buf, split_piece_len_cur); +#ifdef _WIN32 -+ size += link_socket_write_win32(sock, buf, to); ++ Sleep(5); +#else -+ size += link_socket_write_tcp_posix(sock, buf, to); ++ usleep(5000); +#endif ++ } ++ ++ if (!(sock->sockflags & SF_TCP_NODELAY)) { ++ socket_set_tcp_nodelay(sock->sd, 0); ++ } ++ + return size; + } + } @@ -77,7 +98,7 @@ index 91a6d53d..1110c5a4 100644 return link_socket_write_win32(sock, buf, to); #else diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h -index bfc1253b..73635c5f 100644 +index bfc1253b..da66ca16 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -207,6 +207,8 @@ struct link_socket @@ -89,7 +110,7 @@ index bfc1253b..73635c5f 100644 unsigned int sockflags; int mark; const char *bind_dev; -@@ -1173,6 +1175,44 @@ link_socket_write_udp(struct link_socket *sock, +@@ -1173,6 +1175,57 @@ link_socket_write_udp(struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) { @@ -99,38 +120,121 @@ index bfc1253b..73635c5f 100644 +#ifndef P_CONTROL_HARD_RESET_CLIENT_V3 +#define P_CONTROL_HARD_RESET_CLIENT_V3 10 +#endif ++#ifndef P_CONTROL_HARD_RESET_SERVER_V2 ++#define P_CONTROL_HARD_RESET_SERVER_V2 8 /* initial key from server, forget previous state */ ++#endif +#ifndef P_OPCODE_SHIFT +#define P_OPCODE_SHIFT 3 +#endif ++#ifndef P_ACK_V1 ++#define P_ACK_V1 5 /* acknowledgement for packets received */ ++#endif + int rand_bytes(uint8_t *output, int len); + -+#define STUFFING_LEN_MAX 1200 ++#define STUFFING_LEN_MAX 900 ++#define STUFFING_NUM_MAX 100 + + uint8_t opcode = *BPTR(buf) >> P_OPCODE_SHIFT; -+ if ( -+ sock->sockflags & SF_UDP_STUFFING ++ ++ ++ if (sock->sockflags & SF_UDP_STUFFING + && (opcode == P_CONTROL_HARD_RESET_CLIENT_V2 -+ || opcode == P_CONTROL_HARD_RESET_CLIENT_V3) -+ ) ++ || opcode == P_CONTROL_HARD_RESET_CLIENT_V3 ++ || opcode == P_CONTROL_HARD_RESET_SERVER_V2)) + { -+ uint16_t stuffing_len; ++ uint16_t stuffing_len, stuffing_num; + rand_bytes((uint8_t*)&stuffing_len, sizeof(stuffing_len)); -+ stuffing_len = (stuffing_len % (STUFFING_LEN_MAX - 10)) + 10; ++ rand_bytes((uint8_t*)&stuffing_num, sizeof(stuffing_num)); ++ stuffing_num = (stuffing_num % (STUFFING_NUM_MAX - 10)) + 10; + + uint8_t stuffing_data[STUFFING_LEN_MAX] = {0}; -+ rand_bytes(stuffing_data, sizeof(stuffing_data)); -+ struct buffer stuffing_buf = alloc_buf(STUFFING_LEN_MAX); -+ buf_write(&stuffing_buf, stuffing_data, stuffing_len); + ++ for (int i=0; isend_reliable); +- if (!buf) +- { +- return false; +- } ++ uint8_t count = 1; ++ if (session->opt->tcp_split_reset) { ++ rand_bytes(&count, sizeof(count)); ++ count = 2 + (count % 2); ++ } ++ for (uint8_t i=0; isend_reliable); ++ if (!buf) ++ { ++ return false; ++ } + +- ks->initial = now; +- ks->must_negotiate = now + session->opt->handshake_window; +- ks->auth_deferred_expire = now + auth_deferred_expire_window(session->opt); ++ ks->initial = now; ++ ks->must_negotiate = now + session->opt->handshake_window; ++ ks->auth_deferred_expire = now + auth_deferred_expire_window(session->opt); + +- /* null buffer */ +- reliable_mark_active_outgoing(ks->send_reliable, buf, ks->initial_opcode); ++ /* null buffer */ ++ reliable_mark_active_outgoing(ks->send_reliable, buf, ks->initial_opcode); + +- /* If we want to skip sending the initial handshake packet we still generate +- * it to increase internal counters etc. but immediately mark it as done */ +- if (skip_initial_send) +- { +- reliable_mark_deleted(ks->send_reliable, buf); ++ /* If we want to skip sending the initial handshake packet we still generate ++ * it to increase internal counters etc. but immediately mark it as done */ ++ if (skip_initial_send) ++ { ++ reliable_mark_deleted(ks->send_reliable, buf); ++ } ++ INCR_GENERATED; + } +- INCR_GENERATED; + + ks->state = S_PRE_START; + +diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h +index 27b02947..f0378aa6 100644 +--- a/src/openvpn/ssl_common.h ++++ b/src/openvpn/ssl_common.h +@@ -434,6 +434,8 @@ struct tls_options + size_t ekm_size; + + bool dco_enabled; /**< Whether keys have to be installed in DCO or not */ ++ ++ bool tcp_split_reset; + }; + + /** @addtogroup control_processor +-- +2.45.1 + diff --git a/tools/vcpkg/ports/openvpn/vcpkg.json b/tools/vcpkg/ports/openvpn/vcpkg.json index f68a0195..1fdf58c6 100644 --- a/tools/vcpkg/ports/openvpn/vcpkg.json +++ b/tools/vcpkg/ports/openvpn/vcpkg.json @@ -1,7 +1,7 @@ { "name": "openvpn", "version": "2.6.8", - "port-version": 5, + "port-version": 6, "description": "OpenVPN is an open source VPN daemon", "homepage": "https://github.com/OpenVPN/openvpn", "dependencies": [