diff --git a/backend/windows/windscribe_service/split_tunneling/callout_filter.cpp b/backend/windows/windscribe_service/split_tunneling/callout_filter.cpp index cc3507bac..8c66e046d 100644 --- a/backend/windows/windscribe_service/split_tunneling/callout_filter.cpp +++ b/backend/windows/windscribe_service/split_tunneling/callout_filter.cpp @@ -6,11 +6,19 @@ #include "../utils.h" DEFINE_GUID( - WINDSCRIBE_CALLOUT_GUID, + WINDSCRIBE_BIND_CALLOUT_GUID, + 0x5DF29179, + 0x344E, + 0x4F9C, + 0xA4, 0x5D, 0xC3, 0x0F, 0x95, 0x9B, 0x01, 0x2D +); + +DEFINE_GUID( + WINDSCRIBE_TCP_CALLOUT_GUID, 0xB53C4ADE, 0x7A35, 0x4A63, - 0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x21, 0x03 + 0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x23, 0x03 ); DEFINE_GUID( @@ -29,24 +37,14 @@ DEFINE_GUID( 0xBC, 0x77, 0x1F, 0xA4, 0x6E, 0x99, 0x4A, 0x4B ); -#pragma pack(push,1) + typedef struct WINDSCRIBE_CALLOUT_DATA_ { UINT32 localIp; UINT32 vpnIp; UINT8 isExclude; - - // this is a list of addresses represented by pairs of addreses and masks: - // excludeAddresses[0] -> Address_0; excludeAddresses[1] -> Mask_0 - // ..... - // excludeAddresses[cntExcludeAddresses - 2] -> Address_last; excludeAddresses[cntExcludeAddresses - 1] -> Mask_last - UINT16 cntExcludeAddresses; - UINT32 excludeAddresses[1]; - } WINDSCRIBE_CALLOUT_DATA; -#pragma pack(pop) - CalloutFilter::CalloutFilter(FwpmWrapper &fwmpWrapper): fwmpWrapper_(fwmpWrapper), isEnabled_(false), prevLocalIp_(0), prevVpnIp_(0) { @@ -124,42 +122,12 @@ void CalloutFilter::disable() bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UINT32 localIp, UINT32 vpnIp, bool isExclude) { - const std::vector lanRanges = { - "10.0.0.0/9", // this and the following 10.x.x.x ranges represent the range 10.0.0.0 - 10.254.255.255, - "10.128.0.0/10", // specifically excluding the reserved range of 10.255.255.0/24 - "10.192.0.0/11", - "10.224.0.0/12", - "10.240.0.0/13", - "10.248.0.0/14", - "10.252.0.0/15", - "10.254.0.0/16", - "127.0.0.0/8", - "169.254.0.0/16", - "172.16.0.0/12", - "192.168.0.0/16", - "224.0.0.0/4", - }; - bool bRet = true; + WINDSCRIBE_CALLOUT_DATA proxyData; FWP_BYTE_BLOB byteBlob = { 0 }; - - // fill callout data struct - // here is some C style code needed to fill the structure and pass it to the driver - std::vector data; - data.resize(sizeof(WINDSCRIBE_CALLOUT_DATA) - sizeof(WINDSCRIBE_CALLOUT_DATA().excludeAddresses) + lanRanges.size() * 2 * sizeof(UINT32)); - WINDSCRIBE_CALLOUT_DATA *proxyData = reinterpret_cast(data.data()); - - proxyData->localIp = localIp; - proxyData->vpnIp = vpnIp; - proxyData->isExclude = isExclude; - proxyData->cntExcludeAddresses = lanRanges.size() * 2; - - int ind = 0; - for (const auto &it: lanRanges) { - Ip4AddressAndMask addr(it.c_str()); - proxyData->excludeAddresses[ind++] = addr.ipHostOrder(); - proxyData->excludeAddresses[ind++] = addr.maskHostOrder(); - } + proxyData.localIp = localIp; + proxyData.vpnIp = vpnIp; + proxyData.isExclude = isExclude; FWPM_PROVIDER_CONTEXT1 providerContext = { 0 }; UINT64 providerContextId; @@ -169,8 +137,8 @@ bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UI providerContext.type = FWPM_GENERAL_CONTEXT; providerContext.dataBuffer = &byteBlob; - providerContext.dataBuffer->size = data.size(); - providerContext.dataBuffer->data = (UINT8*)data.data(); + providerContext.dataBuffer->size = sizeof(proxyData); + providerContext.dataBuffer->data = (UINT8*)&proxyData; DWORD ret = FwpmProviderContextAdd1(engineHandle, &providerContext, 0, &providerContextId); if (ret != ERROR_SUCCESS) { @@ -183,16 +151,34 @@ bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UI bool CalloutFilter::addCallouts(HANDLE engineHandle) { - FWPM_CALLOUT0 callout = { 0 }; - UINT32 calloutId; - callout.calloutKey = WINDSCRIBE_CALLOUT_GUID; - callout.displayData.name = (wchar_t*)L"Windscribe split tunnel callout"; - callout.applicableLayer = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; - callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; - DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId); - if (ret != ERROR_SUCCESS) { - Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd failed"); - return false; + // Callout for non-TCP traffic + { + FWPM_CALLOUT0 callout = { 0 }; + UINT32 calloutId; + callout.calloutKey = WINDSCRIBE_BIND_CALLOUT_GUID; + callout.displayData.name = (wchar_t*)L"Windscribe split tunnel non-TCP callout"; + callout.applicableLayer = FWPM_LAYER_ALE_BIND_REDIRECT_V4; + callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId); + if (ret != ERROR_SUCCESS) { + Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd (non-TCP) failed"); + return false; + } + } + + // Callout for TCP traffic + { + FWPM_CALLOUT0 callout = { 0 }; + UINT32 calloutId; + callout.calloutKey = WINDSCRIBE_TCP_CALLOUT_GUID; + callout.displayData.name = (wchar_t*)L"Windscribe split tunnel TCP callout"; + callout.applicableLayer = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; + callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId); + if (ret != ERROR_SUCCESS) { + Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd (TCP) failed"); + return false; + } } return true; } @@ -215,45 +201,172 @@ bool CalloutFilter::addSubLayer(HANDLE engineHandle) bool CalloutFilter::addFilters(HANDLE engineHandle, bool withTcpFilters, const AppsIds &appsIds) { bool retValue = true; + const std::vector lanRanges = { + "10.0.0.0/9", // this and the following 10.x.x.x ranges represent the range 10.0.0.0 - 10.254.255.255, + "10.128.0.0/10", // specifically excluding the reserved range of 10.255.255.0/24 + "10.192.0.0/11", + "10.224.0.0/12", + "10.240.0.0/13", + "10.248.0.0/14", + "10.252.0.0/15", + "10.254.0.0/16", + "127.0.0.0/8", + "169.254.0.0/16", + "172.16.0.0/12", + "192.168.0.0/16", + "224.0.0.0/4", + }; + std::vector addrMasks(lanRanges.size()); + std::vector localAddrMasks; if (appsIds.count() == 0) { return true; } - // All traffic for apps goes to ALE_CONNECT_REDIRECT_V4 filter - std::vector conditions; - conditions.reserve(appsIds.count()); - - // Must match one of the apps - for (size_t i = 0; i < appsIds.count(); ++i) { - FWPM_FILTER_CONDITION condition; - condition.fieldKey = FWPM_CONDITION_ALE_APP_ID; - condition.matchType = FWP_MATCH_EQUAL; - condition.conditionValue.type = FWP_BYTE_BLOB_TYPE; - condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i); - conditions.push_back(condition); + // All other traffic for apps goes to BIND filter + { + std::vector conditions; + conditions.reserve(appsIds.count()); + for (size_t i = 0; i < appsIds.count(); ++i) { + FWPM_FILTER_CONDITION condition; + condition.fieldKey = FWPM_CONDITION_ALE_APP_ID; + condition.matchType = FWP_MATCH_EQUAL; + condition.conditionValue.type = FWP_BYTE_BLOB_TYPE; + condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i); + conditions.push_back(condition); + } + + FWPM_FILTER filter = { 0 }; + filter.subLayerKey = SUBLAYER_CALLOUT_GUID; + filter.layerKey = FWPM_LAYER_ALE_BIND_REDIRECT_V4; + filter.displayData.name = (wchar_t *)L"Windscribe bind filter for callout driver"; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0x00; + filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID; + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.numFilterConditions = static_cast(conditions.size()); + filter.filterCondition = &conditions[0]; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = WINDSCRIBE_BIND_CALLOUT_GUID; + + UINT64 filterId; + DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId); + retValue = (ret == ERROR_SUCCESS); + if (!retValue) { + Logger::instance().out(L"CalloutFilter::addFilter(), bind filter failed: %u", ret); + return retValue; + } } - - FWPM_FILTER filter = { 0 }; - filter.subLayerKey = SUBLAYER_CALLOUT_GUID; - filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; - filter.displayData.name = (wchar_t *)L"Windscribe ALE_CONNECT_REDIRECT_V4 filter for callout driver"; - filter.weight.type = FWP_UINT8; - filter.weight.uint8 = 0x00; - filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID; - filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; - filter.numFilterConditions = static_cast(conditions.size()); - filter.filterCondition = &conditions[0]; - filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; - filter.action.calloutKey = WINDSCRIBE_CALLOUT_GUID; - - UINT64 filterId; - DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId); - retValue = (ret == ERROR_SUCCESS); - if (!retValue) { - Logger::instance().out(L"CalloutFilter::addFilter(), ALE_CONNECT_REDIRECT_V4 filter failed: %u", ret); - return retValue; + // LAN TCP traffic goes to CONNECT filter + if (withTcpFilters) { + // first, we create a filter that explicitly allows traffic to the private VPN IPs, which + // on IKEv2/OpenVPN reside on a LAN range. + { + std::vector conditions; + AdaptersInfo ai; + std::vector taps = ai.getTAPAdapters(); + + for (int i = 0; i < taps.size(); i++) { + std::vector addrs = ai.getAdapterAddresses(taps[i]); + + for (int j = 0; j < addrs.size(); j++) { + FWPM_FILTER_CONDITION condition; + FWP_V4_ADDR_AND_MASK addrMask; + condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; + condition.matchType = FWP_MATCH_EQUAL; + condition.conditionValue.type = FWP_V4_ADDR_MASK; + Ip4AddressAndMask ipAddress(addrs[j].c_str()); + addrMask.addr = ipAddress.ipHostOrder(); + addrMask.mask = 0xffffffff; + localAddrMasks.push_back(addrMask); + condition.conditionValue.v4AddrMask = &localAddrMasks.back(); + conditions.push_back(condition); + } + } + + if (!conditions.empty()) { + FWPM_FILTER filter = { 0 }; + filter.subLayerKey = SUBLAYER_CALLOUT_GUID; + filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; + filter.displayData.name = (wchar_t *)L"Windscribe TCP exception filter for callout driver"; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0x01; + filter.numFilterConditions = static_cast(conditions.size()); + filter.filterCondition = &conditions[0]; + filter.action.type = FWP_ACTION_PERMIT; + + UINT64 filterId; + DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId); + retValue = (ret == ERROR_SUCCESS); + if (!retValue) { + Logger::instance().out(L"CalloutFilter::addFilter(), TCP exception filter failed: %u", ret); + return retValue; + } + } + } + + // now, we create the filter which allows other LAN traffic to be routed back to the primary interface + { + std::vector conditions; + + // Must match one of the apps + for (size_t i = 0; i < appsIds.count(); ++i) { + FWPM_FILTER_CONDITION condition; + condition.fieldKey = FWPM_CONDITION_ALE_APP_ID; + condition.matchType = FWP_MATCH_EQUAL; + condition.conditionValue.type = FWP_BYTE_BLOB_TYPE; + condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i); + conditions.push_back(condition); + } + + // must match TCP + { + FWPM_FILTER_CONDITION condition; + condition.fieldKey = FWPM_CONDITION_IP_PROTOCOL; + condition.matchType = FWP_MATCH_EQUAL; + condition.conditionValue.type = FWP_UINT8; + condition.conditionValue.uint8 = IPPROTO_TCP; + conditions.push_back(condition); + } + + // must match a LAN range + { + for (int i = 0; i < lanRanges.size(); i++) { + FWPM_FILTER_CONDITION condition; + condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; + condition.matchType = FWP_MATCH_EQUAL; + condition.conditionValue.type = FWP_V4_ADDR_MASK; + condition.conditionValue.v4AddrMask = &addrMasks[i]; + Ip4AddressAndMask ipAddress(lanRanges[i].c_str()); + addrMasks[i].addr = ipAddress.ipHostOrder(); + addrMasks[i].mask = ipAddress.maskHostOrder(); + addrMasks.push_back(addrMasks[i]); + conditions.push_back(condition); + } + } + + FWPM_FILTER filter = { 0 }; + filter.subLayerKey = SUBLAYER_CALLOUT_GUID; + filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; + filter.displayData.name = (wchar_t *)L"Windscribe TCP filter for callout driver"; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0x00; + filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID; + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.numFilterConditions = static_cast(conditions.size()); + filter.filterCondition = &conditions[0]; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = WINDSCRIBE_TCP_CALLOUT_GUID; + + UINT64 filterId; + DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId); + retValue = (ret == ERROR_SUCCESS); + if (!retValue) { + Logger::instance().out(L"CalloutFilter::addFilter(), TCP filter failed: %u", ret); + return retValue; + } + } } return retValue; @@ -273,10 +386,11 @@ bool CalloutFilter::removeAllFilters(FwpmWrapper &fwmpWrapper) HANDLE hEngine = fwmpWrapper.getHandleAndLock(); fwmpWrapper.beginTransaction(); - deleteSublayer(hEngine); + DWORD ret = deleteSublayer(hEngine); - FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_CALLOUT_GUID); - FwpmProviderContextDeleteByKey(hEngine, &CALLOUT_PROVIDER_CONTEXT_IP_GUID); + ret = FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_BIND_CALLOUT_GUID); + ret = FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_TCP_CALLOUT_GUID); + ret = FwpmProviderContextDeleteByKey(hEngine, &CALLOUT_PROVIDER_CONTEXT_IP_GUID); fwmpWrapper.endTransaction(); fwmpWrapper.unlock(); diff --git a/backend/windows/windscribe_split_tunnel/CalloutFunctions.c b/backend/windows/windscribe_split_tunnel/CalloutFunctions.c index 4860d3fdd..40907c560 100644 --- a/backend/windows/windscribe_split_tunnel/CalloutFunctions.c +++ b/backend/windows/windscribe_split_tunnel/CalloutFunctions.c @@ -9,18 +9,10 @@ #define IPV4_ADDRESS_LENGTH 4 -void HlprIPAddressV4ValueToString(UINT32 pIPv4Address, _Inout_ PWSTR pIPv4AddressString, _Inout_ ULONG *addressStringLength, USHORT port) -{ - RtlIpv4AddressToStringExW((const IN_ADDR *)&pIPv4Address, port, pIPv4AddressString, addressStringLength); -} -void SockaddrToString(SOCKADDR* address, wchar_t* str) +void HlprIPAddressV4ValueToString(UINT32 pIPv4Address, _Inout_ PWSTR pIPv4AddressString, _Inout_ ULONG *addressStringLength) { - ULONG len = INET_ADDRSTRLEN; - SOCKADDR_IN* sin = (SOCKADDR_IN* )address; - UINT32 addr; - RtlCopyMemory(&addr, INETADDR_ADDRESS(address), IPV4_ADDRESS_LENGTH); - HlprIPAddressV4ValueToString(addr, (PWSTR)str, &len, sin->sin_port); + RtlIpv4AddressToStringExW((const IN_ADDR *)&pIPv4Address, 0, pIPv4AddressString, addressStringLength); } void redirect(SOCKADDR *address, UINT32 newAddress) @@ -28,16 +20,18 @@ void redirect(SOCKADDR *address, UINT32 newAddress) UINT32 addr; RtlCopyMemory(&addr, INETADDR_ADDRESS(address), IPV4_ADDRESS_LENGTH); wchar_t strIp[INET_ADDRSTRLEN]; - SockaddrToString(address, strIp); + ULONG len = INET_ADDRSTRLEN; + HlprIPAddressV4ValueToString(addr, (PWSTR)strIp, &len); INETADDR_SET_ADDRESS(address, (const UCHAR *)&newAddress); UINT32 addr2; RtlCopyMemory(&addr2, INETADDR_ADDRESS(address), IPV4_ADDRESS_LENGTH); wchar_t strIp2[INET_ADDRSTRLEN]; - SockaddrToString(address, strIp2); + ULONG len2 = INET_ADDRSTRLEN; + HlprIPAddressV4ValueToString(addr2, (PWSTR)strIp2, &len2); - KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel2: replaced %S -> %S\n", strIp, strIp2)); + KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: replaced %S -> %S\n", strIp, strIp2)); } VOID NTAPI @@ -63,11 +57,13 @@ ClassifyFn( NT_ASSERT(filter->providerContext); NT_ASSERT(filter->providerContext->type == FWPM_GENERAL_CONTEXT); NT_ASSERT(filter->providerContext->dataBuffer); + NT_ASSERT(filter->providerContext->dataBuffer->size == sizeof(WINDSCRIBE_CALLOUT_DATA)); NT_ASSERT(filter->providerContext->dataBuffer->data); NTSTATUS status = STATUS_SUCCESS; UINT64 classifyHandle = 0; PVOID dataPointer = NULL; + FWPS_BIND_REQUEST *bindRequest = NULL; FWPS_CONNECT_REQUEST *connectRequest = NULL; WINDSCRIBE_CALLOUT_DATA *calloutData = (WINDSCRIBE_CALLOUT_DATA *)filter->providerContext->dataBuffer->data; @@ -82,18 +78,6 @@ ClassifyFn( goto error; } - if (inFixedValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V4) - goto cleanup; - - // skip modification if a remote address in the exclusion list(these are usually local address ranges) - UINT32 remoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS].value.uint32; - for (int i = 0; i < calloutData->cntExcludeAddresses / 2; i++) { - if ((remoteIp & calloutData->excludeAddresses[i*2 + 1]) == calloutData->excludeAddresses[i*2]) { // in local range? - KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: skipped: %X\n", remoteIp)); - goto cleanup; - } - } - status = FwpsAcquireWritableLayerDataPointer(classifyHandle, filter->filterId, 0, &dataPointer, classifyOut); if (status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: FwpsAcquireWritableLayerDataPointer failed\n")); @@ -101,29 +85,60 @@ ClassifyFn( goto error; } - connectRequest = (FWPS_CONNECT_REQUEST *)dataPointer; + if (inFixedValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4) { + connectRequest = (FWPS_CONNECT_REQUEST *)dataPointer; + + // Prevent infinite redirection + UINT32 timesRedirected = 0; + for (FWPS_CONNECT_REQUEST* pConnectRequest = connectRequest->previousVersion; pConnectRequest; pConnectRequest = pConnectRequest->previousVersion) { + if (pConnectRequest->modifierFilterId == filter->filterId) { + timesRedirected++; + } + + // Don't redirect the same socket more than 3 times + if (timesRedirected > 3) { + status = STATUS_TOO_MANY_COMMANDS; + goto error; + } + } - // Prevent infinite redirection - UINT32 timesRedirected = 0; - for (FWPS_CONNECT_REQUEST* pConnectRequest = connectRequest->previousVersion; pConnectRequest; pConnectRequest = pConnectRequest->previousVersion) { - if (pConnectRequest->modifierFilterId == filter->filterId) { - timesRedirected++; + UINT32 remoteAddr = 0; + RtlCopyMemory(&remoteAddr, INETADDR_ADDRESS((SOCKADDR *)&(connectRequest->remoteAddressAndPort)), IPV4_ADDRESS_LENGTH); + + if (!calloutData->isExclude) { + /* This packet was redirected, but it should go back to the original path */ + redirect((SOCKADDR *)&(connectRequest->localAddressAndPort), calloutData->localIp); } - // Don't redirect the same socket more than 3 times - if (timesRedirected > 3) { - status = STATUS_TOO_MANY_COMMANDS; - goto error; + } else if (inFixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4) { + if (inFixedValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_FLAGS].value.uint32 & FWP_CONDITION_FLAG_IS_REAUTHORIZE) { + goto cleanup; } - } - if (calloutData->isExclude) { - redirect((SOCKADDR*)&(connectRequest->localAddressAndPort), calloutData->localIp); - } - else { - redirect((SOCKADDR*)&(connectRequest->localAddressAndPort), calloutData->vpnIp); + bindRequest = (FWPS_BIND_REQUEST *)dataPointer; + + // Prevent infinite redirection + UINT32 timesRedirected = 0; + + for (FWPS_BIND_REQUEST* pBindRequest = bindRequest->previousVersion; pBindRequest; pBindRequest = pBindRequest->previousVersion) { + if (pBindRequest->modifierFilterId == filter->filterId) { + timesRedirected++; + } + + // Don't redirect the same socket more than 3 times + if (timesRedirected > 3) { + status = STATUS_TOO_MANY_COMMANDS; + goto error; + } + } + + if (calloutData->isExclude) { + redirect((SOCKADDR *)&(bindRequest->localAddressAndPort), calloutData->localIp); + } else { + redirect((SOCKADDR *)&(bindRequest->localAddressAndPort), calloutData->vpnIp); + } } - + error: if (status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: ClassifyFn failed\n")); diff --git a/backend/windows/windscribe_split_tunnel/Driver.c b/backend/windows/windscribe_split_tunnel/Driver.c index dd73fe8df..f5e0b096b 100644 --- a/backend/windows/windscribe_split_tunnel/Driver.c +++ b/backend/windows/windscribe_split_tunnel/Driver.c @@ -7,7 +7,8 @@ WDFDEVICE wdfDevice = NULL; // Variable for the run-time callout identifier -UINT32 CalloutId = 0; +UINT32 NonTcpCalloutId = 0; +UINT32 TcpCalloutId = 0; EVT_WDF_DRIVER_UNLOAD UnloadFunc; @@ -22,7 +23,8 @@ DriverEntry( WDFDRIVER driver; PWDFDEVICE_INIT deviceInit; PDEVICE_OBJECT deviceObject = NULL; - FWPS_CALLOUT1 callout = { 0 }; + FWPS_CALLOUT1 nonTcpCallout = { 0 }; + FWPS_CALLOUT1 tcpCallout = { 0 }; // Initialize the driver configuration object WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); @@ -70,11 +72,22 @@ DriverEntry( return status; } - callout.calloutKey = WINDSCRIBE_CALLOUT_GUID; - callout.classifyFn = ClassifyFn; - callout.notifyFn = NotifyFn; + nonTcpCallout.calloutKey = WINDSCRIBE_BIND_CALLOUT_GUID; + nonTcpCallout.classifyFn = ClassifyFn; + nonTcpCallout.notifyFn = NotifyFn; - status = FwpsCalloutRegister1(deviceObject, &callout, &CalloutId); + status = FwpsCalloutRegister1(deviceObject, &nonTcpCallout, &NonTcpCalloutId); + if (status != STATUS_SUCCESS) + { + KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: FwpsCalloutRegister1 (bind) failed\n")); + return status; + } + + tcpCallout.calloutKey = WINDSCRIBE_TCP_CALLOUT_GUID; + tcpCallout.classifyFn = ClassifyFn; + tcpCallout.notifyFn = NotifyFn; + + status = FwpsCalloutRegister1(deviceObject, &tcpCallout, &TcpCalloutId); if (status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: FwpsCalloutRegister1 (TCP) failed\n")); @@ -91,7 +104,12 @@ VOID UnloadFunc(_In_ WDFDRIVER Driver) NTSTATUS status = STATUS_SUCCESS; // Unregister the callout - status = FwpsCalloutUnregisterById0(CalloutId); + status = FwpsCalloutUnregisterById0(NonTcpCalloutId); + if (status != STATUS_SUCCESS) + { + KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: FwpsCalloutUnregisterById0 failed\n")); + } + status = FwpsCalloutUnregisterById0(TcpCalloutId); if (status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "WindscribeSplitTunnel: FwpsCalloutUnregisterById0 failed\n")); diff --git a/backend/windows/windscribe_split_tunnel/WindscribeCallout.h b/backend/windows/windscribe_split_tunnel/WindscribeCallout.h index 9ea768ae7..35e01fa71 100644 --- a/backend/windows/windscribe_split_tunnel/WindscribeCallout.h +++ b/backend/windows/windscribe_split_tunnel/WindscribeCallout.h @@ -3,26 +3,25 @@ #include DEFINE_GUID( - WINDSCRIBE_CALLOUT_GUID, + WINDSCRIBE_BIND_CALLOUT_GUID, + 0x5DF29179, + 0x344E, + 0x4F9C, + 0xA4, 0x5D, 0xC3, 0x0F, 0x95, 0x9B, 0x01, 0x2D +); + +DEFINE_GUID( + WINDSCRIBE_TCP_CALLOUT_GUID, 0xB53C4ADE, 0x7A35, 0x4A63, - 0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x21, 0x03 + 0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x23, 0x03 ); -#pragma pack(push,1) typedef struct WINDSCRIBE_CALLOUT_DATA_ { UINT32 localIp; UINT32 vpnIp; UINT8 isExclude; - - // this is a list of addresses represented by pairs of addreses and masks: - // excludeAddresses[0] -> Address_0; excludeAddresses[1] -> Mask_0 - // ..... - // excludeAddresses[cntExcludeAddresses - 2] -> Address_last; excludeAddresses[cntExcludeAddresses - 1] -> Mask_last - UINT16 cntExcludeAddresses; - UINT32 excludeAddresses[1]; - } WINDSCRIBE_CALLOUT_DATA; -#pragma pack(pop) + diff --git a/client/common/changelog.txt b/client/common/changelog.txt index 6e49c3baf..90cbfb21d 100644 --- a/client/common/changelog.txt +++ b/client/common/changelog.txt @@ -1,3 +1,11 @@ +2.10.14 (20/06/2024) +All: + * Improved best location selection. #1040 +Windows: + * Fixed installer window position during update. #1041 + * Fixed UDP traffic not being split tunneled correctly. #1039 + + 2.10.12 (11/06/2024) All: * Improved OpenVPN and WireGuard anti-censorship. #1023 diff --git a/client/common/version/windscribe_version.h b/client/common/version/windscribe_version.h index a4470200f..e1f9f02fe 100644 --- a/client/common/version/windscribe_version.h +++ b/client/common/version/windscribe_version.h @@ -2,7 +2,7 @@ #define WINDSCRIBE_MAJOR_VERSION 2 #define WINDSCRIBE_MINOR_VERSION 10 -#define WINDSCRIBE_BUILD_VERSION 12 +#define WINDSCRIBE_BUILD_VERSION 14 // only one of these should be enabled; neither -> stable #define WINDSCRIBE_IS_BETA diff --git a/client/engine/engine/locationsmodel/apilocationsmodel.cpp b/client/engine/engine/locationsmodel/apilocationsmodel.cpp index bbdff5b74..cf3ab73e6 100644 --- a/client/engine/engine/locationsmodel/apilocationsmodel.cpp +++ b/client/engine/engine/locationsmodel/apilocationsmodel.cpp @@ -173,6 +173,7 @@ void ApiLocationsModel::detectBestLocation(bool isAllNodesInDisconnectedState) { int minLatency = INT_MAX; LocationID locationIdWithMinLatency; + bool isPriorityBestLocation = false; // Commented debug entry out as this method is potentially called every minute and we don't // need to flood the log with this info. @@ -180,48 +181,57 @@ void ApiLocationsModel::detectBestLocation(bool isAllNodesInDisconnectedState) int prevBestLocationLatency = INT_MAX; - int ind = 0; - for (const api_responses::Location &l : locations_) - { - for (int i = 0; i < l.groupsCount(); ++i) - { + // #1040 YOLO: try to find a best location that is 'priority' (10gbps, not disabled, and latency < 30ms) first + for (const api_responses::Location &l : locations_) { + for (int i = 0; i < l.groupsCount(); ++i) { const api_responses::Group group = l.getGroup(i); - - if (group.isDisabled()) - { - continue; - } - - LocationID lid = LocationID::createApiLocationId(l.getId(), group.getCity(), group.getNick()); int latency = pingManager_.getPing(group.getPingIp()).toInt(); - // we assume a maximum ping time for three bars when no ping info - if (latency == PingTime::NO_PING_INFO) - { - latency = PingTime::LATENCY_STEP1; - } - else if (latency == PingTime::PING_FAILED) - { - latency = PingTime::MAX_LATENCY_FOR_PING_FAILED; + if (group.isDisabled() || group.getLinkSpeed() < 10000 || latency == PingTime::NO_PING_INFO || latency == PingTime::PING_FAILED || latency > 30) { + continue; } - if (bestLocation_.isValid() && lid == bestLocation_.getId()) - { - prevBestLocationLatency = latency; - } - if (latency != PingTime::PING_FAILED && latency < minLatency) - { + if (latency < minLatency) { minLatency = latency; locationIdWithMinLatency = LocationID::createApiLocationId(l.getId(), group.getCity(), group.getNick()) ; + isPriorityBestLocation = true; } } + } + + // If we didn't find a priority best location, then use the old logic + if (!locationIdWithMinLatency.isValid()) { + for (const api_responses::Location &l : locations_) { + for (int i = 0; i < l.groupsCount(); ++i) { + const api_responses::Group group = l.getGroup(i); + + if (group.isDisabled()) { + continue; + } - ind++; + LocationID lid = LocationID::createApiLocationId(l.getId(), group.getCity(), group.getNick()); + int latency = pingManager_.getPing(group.getPingIp()).toInt(); + + // we assume a maximum ping time for three bars when no ping info + if (latency == PingTime::NO_PING_INFO) { + latency = PingTime::LATENCY_STEP1; + } else if (latency == PingTime::PING_FAILED) { + latency = PingTime::MAX_LATENCY_FOR_PING_FAILED; + } + + if (bestLocation_.isValid() && lid == bestLocation_.getId()) { + prevBestLocationLatency = latency; + } + if (latency != PingTime::PING_FAILED && latency < minLatency) { + minLatency = latency; + locationIdWithMinLatency = LocationID::createApiLocationId(l.getId(), group.getCity(), group.getNick()) ; + } + } + } } LocationID prevBestLocationId; - if (bestLocation_.isValid()) - { + if (bestLocation_.isValid()) { prevBestLocationId = bestLocation_.getId(); // Commented debug entry out as this method is potentially called every minute and we don't // need to flood the log with this info. We will log it if the location actually changes. @@ -229,38 +239,26 @@ void ApiLocationsModel::detectBestLocation(bool isAllNodesInDisconnectedState) } - if (locationIdWithMinLatency.isValid()) // new best location found - { + if (locationIdWithMinLatency.isValid()) { // new best location found // Commented debug entry out as this method is potentially called every minute and we don't // need to flood the log with this info. We will log it if the location actually changes. //qCDebug(LOG_BEST_LOCATION) << "Detected min latency=" << minLatency << "; id=" << locationIdWithMinLatency.getHashString(); // check whether best location needs to be changed - if (!bestLocation_.isValid()) - { + if (!bestLocation_.isValid()) { bestLocation_.set(locationIdWithMinLatency, true, isAllNodesInDisconnectedState); - } - else // if best location is valid - { - if (!bestLocation_.isDetectedFromThisAppStart()) - { + } else { // if best location is valid + if (!bestLocation_.isDetectedFromThisAppStart()) { bestLocation_.set(locationIdWithMinLatency, true, isAllNodesInDisconnectedState); - } - else if (bestLocation_.isDetectedWithDisconnectedIps()) - { - if (isAllNodesInDisconnectedState) - { - // check ping time changed more than 10% compared to prev best location - if ((double)minLatency < ((double)prevBestLocationLatency * 0.9)) - { + } else if (bestLocation_.isDetectedWithDisconnectedIps()) { + if (isAllNodesInDisconnectedState) { + // check ping time changed more than 10% compared to prev best location, or we found a 10gbps location with latency < 30ms + if ((double)minLatency < ((double)prevBestLocationLatency * 0.9) || isPriorityBestLocation) { bestLocation_.set(locationIdWithMinLatency, true, isAllNodesInDisconnectedState); } } - } - else - { - if (isAllNodesInDisconnectedState) - { + } else { + if (isAllNodesInDisconnectedState) { bestLocation_.set(locationIdWithMinLatency, true, isAllNodesInDisconnectedState); } } @@ -268,8 +266,7 @@ void ApiLocationsModel::detectBestLocation(bool isAllNodesInDisconnectedState) } // send the signal to the GUI only if the location has actually changed - if (bestLocation_.isValid() && prevBestLocationId != bestLocation_.getId()) - { + if (bestLocation_.isValid() && prevBestLocationId != bestLocation_.getId()) { if (prevBestLocationId.isValid()) { qCDebug(LOG_BEST_LOCATION) << "prevBestLocationId=" << prevBestLocationId.getHashString() << "; prevBestLocationLatency=" << prevBestLocationLatency; } diff --git a/installer/common/mainwindow.cpp b/installer/common/mainwindow.cpp index 202756425..f470e1e38 100644 --- a/installer/common/mainwindow.cpp +++ b/installer/common/mainwindow.cpp @@ -61,7 +61,10 @@ MainWindow::MainWindow(bool isAdmin, InstallerOptions &options) : QWidget(nullpt int screenHeight = primaryScreen->geometry().height(); if (options_.centerX >= 0 && options_.centerY >= 0) { - setGeometry(options_.centerX - kWindowWidth/2, options_.centerY - kWindowHeight/2, kWindowWidth, kWindowHeight); + setGeometry(options_.centerX/primaryScreen->devicePixelRatio() - kWindowWidth/2, + options_.centerY/primaryScreen->devicePixelRatio() - kWindowHeight/2, + kWindowWidth, + kWindowHeight); } else { setGeometry((screenWidth - kWindowWidth) / 2, (screenHeight - kWindowHeight) / 2, kWindowWidth, kWindowHeight); } diff --git a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.cat b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.cat index 35821c4d3..839860da3 100644 Binary files a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.cat and b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.cat differ diff --git a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.inf b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.inf index 089624d16..37972edf0 100644 --- a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.inf +++ b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.inf @@ -1,58 +1,52 @@ [Version] - Signature = "$Windows NT$" - Class = WFPCALLOUTS - ClassGuid = {58465043-616C-6C6F-7574-5F636C617372} - Provider = %ManufacturerName% - CatalogFile = "WindscribeSplitTunnel.cat" - PnpLockdown = 1 -DriverVer = 04/16/2024,1.3.44.110 + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {58465043-616C-6C6F-7574-5F636C617372} + Provider = %ManufacturerName% + CatalogFile = "WindscribeSplitTunnel.cat" +DriverVer = 01/06/2023,8.53.52.705 [SourceDisksNames] - 1 = %DiskName%,,,"" + 1 = %DiskName%,,,"" [SourceDisksFiles] - WindscribeSplitTunnel.sys = 1,, + WindscribeSplitTunnel.sys = 1,, [DestinationDirs] - DefaultDestDir = 12 ; %windir%\system32\drivers - WindscribeSplitTunnel.DriverFiles = 12 ; %windir%\system32\drivers + DefaultDestDir = 12 ; %windir%\system32\drivers + WindscribeSplitTunnel.DriverFiles = 12 ; %windir%\system32\drivers -[DefaultInstall.NTamd64] | -[DefaultInstall.NTarm64] - OptionDesc = %ServiceDesc% - CopyFiles = WindscribeSplitTunnel.CopyDriverFiles +[DefaultInstall] + OptionDesc = %ServiceDesc% + CopyFiles = WindscribeSplitTunnel.CopyDriverFiles -[DefaultInstall.NTamd64.Services] | -[DefaultInstall.NTarm64.Services] - AddService = %ServiceName%,,WindscribeSplitTunnel.Service +[DefaultInstall.Services] + AddService = %ServiceName%,,WindscribeSplitTunnel.Service -[DefaultUninstall.NTamd64] | -[DefaultUninstall.NTarm64] - DelFiles = WindscribeSplitTunnel.DeleteDriverFiles - LegacyUninstall = 1 +[DefaultUninstall] + DelFiles = WindscribeSplitTunnel.DeleteDriverFiles -[DefaultUninstall.NTamd64.Services] | -[DefaultUninstall.NTarm64.Services] - DelService = %ServiceName%,0x200 ; SPSVCINST_STOPSERVICE +[DefaultUninstall.Services] + DelService = %ServiceName%,0x200 ; SPSVCINST_STOPSERVICE [WindscribeSplitTunnel.DeleteDriverFiles] - WindscribeSplitTunnel.sys,,,0x00010001 ;(DELFLG_IN_USE | DELFLG_IN_USE1) + WindscribeSplitTunnel.sys,,,0x00010001 ;(DELFLG_IN_USE | DELFLG_IN_USE1) [WindscribeSplitTunnel.CopyDriverFiles] - WindscribeSplitTunnel.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + WindscribeSplitTunnel.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY [WindscribeSplitTunnel.Service] - DisplayName = %ServiceName% - Description = %ServiceDesc% - ServiceType = 1 ; SERVICE_KERNEL_DRIVER; - StartType = 3 ; SERVICE_BOOT_START(0) or SERVICE_DEMAND_START(3) - ErrorControl = 1 ; SERVICE_ERROR_NORMAL - ServiceBinary = %12%\WindscribeSplitTunnel.sys + DisplayName = %ServiceName% + Description = %ServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER; + StartType = 3 ; SERVICE_BOOT_START(0) or SERVICE_DEMAND_START(3) + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\WindscribeSplitTunnel.sys LoadOrderGroup = NDIS Dependencies = TCPIP [Strings] - ManufacturerName = "Windscribe Limited" - DiskName = "Windscribe Split Tunnel Installation Disk" - ServiceName = "WindscribeSplitTunnel" - ServiceDesc = "Windscribe Split Tunnel Callout Driver" + ManufacturerName = "Windscribe Limited" + DiskName = "Windscribe Split Tunnel Installation Disk" + ServiceName = "WindscribeSplitTunnel" + ServiceDesc = "Windscribe Split Tunnel Callout Driver" diff --git a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.sys b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.sys index ee19e1f11..36dfc958a 100644 Binary files a/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.sys and b/installer/windows/additional_files/splittunnel/amd64/windscribesplittunnel.sys differ diff --git a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.cat b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.cat index b941cde7f..186ece82f 100644 Binary files a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.cat and b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.cat differ diff --git a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.inf b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.inf index feaa83449..c04adbd50 100644 --- a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.inf +++ b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.inf @@ -1,58 +1,58 @@ [Version] - Signature = "$Windows NT$" - Class = WFPCALLOUTS - ClassGuid = {58465043-616C-6C6F-7574-5F636C617372} - Provider = %ManufacturerName% - CatalogFile = "WindscribeSplitTunnel.cat" - PnpLockdown = 1 -DriverVer = 04/16/2024,1.3.27.705 + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {58465043-616C-6C6F-7574-5F636C617372} + Provider = %ManufacturerName% + CatalogFile = "WindscribeSplitTunnel.cat" + PnpLockdown = 1 +DriverVer = 05/23/2023,13.24.14.483 [SourceDisksNames] - 1 = %DiskName%,,,"" + 1 = %DiskName%,,,"" [SourceDisksFiles] - WindscribeSplitTunnel.sys = 1,, + WindscribeSplitTunnel.sys = 1,, [DestinationDirs] - DefaultDestDir = 12 ; %windir%\system32\drivers - WindscribeSplitTunnel.DriverFiles = 12 ; %windir%\system32\drivers + DefaultDestDir = 12 ; %windir%\system32\drivers + WindscribeSplitTunnel.DriverFiles = 12 ; %windir%\system32\drivers [DefaultInstall.NTamd64] | [DefaultInstall.NTarm64] - OptionDesc = %ServiceDesc% - CopyFiles = WindscribeSplitTunnel.CopyDriverFiles + OptionDesc = %ServiceDesc% + CopyFiles = WindscribeSplitTunnel.CopyDriverFiles [DefaultInstall.NTamd64.Services] | [DefaultInstall.NTarm64.Services] - AddService = %ServiceName%,,WindscribeSplitTunnel.Service + AddService = %ServiceName%,,WindscribeSplitTunnel.Service [DefaultUninstall.NTamd64] | [DefaultUninstall.NTarm64] - DelFiles = WindscribeSplitTunnel.DeleteDriverFiles - LegacyUninstall = 1 + DelFiles = WindscribeSplitTunnel.DeleteDriverFiles + LegacyUninstall = 1 [DefaultUninstall.NTamd64.Services] | [DefaultUninstall.NTarm64.Services] - DelService = %ServiceName%,0x200 ; SPSVCINST_STOPSERVICE + DelService = %ServiceName%,0x200 ; SPSVCINST_STOPSERVICE [WindscribeSplitTunnel.DeleteDriverFiles] - WindscribeSplitTunnel.sys,,,0x00010001 ;(DELFLG_IN_USE | DELFLG_IN_USE1) + WindscribeSplitTunnel.sys,,,0x00010001 ;(DELFLG_IN_USE | DELFLG_IN_USE1) [WindscribeSplitTunnel.CopyDriverFiles] - WindscribeSplitTunnel.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + WindscribeSplitTunnel.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY [WindscribeSplitTunnel.Service] - DisplayName = %ServiceName% - Description = %ServiceDesc% - ServiceType = 1 ; SERVICE_KERNEL_DRIVER; - StartType = 3 ; SERVICE_BOOT_START(0) or SERVICE_DEMAND_START(3) - ErrorControl = 1 ; SERVICE_ERROR_NORMAL - ServiceBinary = %12%\WindscribeSplitTunnel.sys + DisplayName = %ServiceName% + Description = %ServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER; + StartType = 3 ; SERVICE_BOOT_START(0) or SERVICE_DEMAND_START(3) + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\WindscribeSplitTunnel.sys LoadOrderGroup = NDIS Dependencies = TCPIP [Strings] - ManufacturerName = "Windscribe Limited" - DiskName = "Windscribe Split Tunnel Installation Disk" - ServiceName = "WindscribeSplitTunnel" - ServiceDesc = "Windscribe Split Tunnel Callout Driver" + ManufacturerName = "Windscribe Limited" + DiskName = "Windscribe Split Tunnel Installation Disk" + ServiceName = "WindscribeSplitTunnel" + ServiceDesc = "Windscribe Split Tunnel Callout Driver" diff --git a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.sys b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.sys index 0d9a70c11..04615c35e 100644 Binary files a/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.sys and b/installer/windows/additional_files/splittunnel/arm64/windscribesplittunnel.sys differ