mirror of
https://github.com/amnezia-vpn/win-split-tunnel.git
synced 2026-05-17 08:16:00 +03:00
Move RewriteBind()
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
#include "firewall.h"
|
||||
#include "context.h"
|
||||
#include "identifiers.h"
|
||||
#include "filters.h" // TODO-NOW: Correct this, since its only for RewriteBind()
|
||||
#include "asyncbind.h"
|
||||
#include "callouts.h"
|
||||
#include "../util.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
@@ -82,6 +82,174 @@ RegisterCalloutTx
|
||||
return FwpsCalloutRegister1(DeviceObject, &aCallout, NULL);
|
||||
}
|
||||
|
||||
//
|
||||
// RewriteBind()
|
||||
//
|
||||
// This is where the splitting happens.
|
||||
// Move socket binds from tunnel interface to the internet connected interface.
|
||||
//
|
||||
void
|
||||
RewriteBind
|
||||
(
|
||||
CONTEXT *Context,
|
||||
const FWPS_INCOMING_VALUES0 *FixedValues,
|
||||
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
|
||||
UINT64 FilterId,
|
||||
const void *ClassifyContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(MetaValues);
|
||||
|
||||
UINT64 classifyHandle = 0;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0
|
||||
(
|
||||
const_cast<void*>(ClassifyContext),
|
||||
0,
|
||||
&classifyHandle
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed 0x%X\n", status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FWPS_BIND_REQUEST0 *bindRequest = NULL;
|
||||
|
||||
status = FwpsAcquireWritableLayerDataPointer0
|
||||
(
|
||||
classifyHandle,
|
||||
FilterId,
|
||||
0,
|
||||
(PVOID*)&bindRequest,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireWritableLayerDataPointer0() failed 0x%X\n", status);
|
||||
|
||||
goto Cleanup_handle;
|
||||
}
|
||||
|
||||
//
|
||||
// According to documentation, FwpsAcquireWritableLayerDataPointer0() will update the
|
||||
// `actionType` and `rights` fields with poorly chosen values:
|
||||
//
|
||||
// ```
|
||||
// classifyOut->actionType = FWP_ACTION_BLOCK
|
||||
// classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE
|
||||
// ```
|
||||
//
|
||||
// However, in practice it seems to not make any changes to those fields.
|
||||
// But if it did we'd want to ensure the fields have sane values.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_CONTINUE;
|
||||
ClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
|
||||
|
||||
//
|
||||
// There's a list with redirection history.
|
||||
//
|
||||
// This only ever comes into play if several callouts are fighting to redirect the bind.
|
||||
//
|
||||
// To prevent recursion, we need to check if we're on the list, and abort if so.
|
||||
//
|
||||
|
||||
for (auto history = bindRequest->previousVersion;
|
||||
history != NULL;
|
||||
history = history->previousVersion)
|
||||
{
|
||||
if (history->modifierFilterId == FilterId)
|
||||
{
|
||||
DbgPrint("Aborting bind processing because already redirected by us\n");
|
||||
|
||||
goto Cleanup_data;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Rewrite bind as applicable.
|
||||
//
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4;
|
||||
|
||||
WdfWaitLockAcquire(Context->IpAddresses.Lock, NULL);
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: %d.%d.%d.%d:%d\n",
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b1,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b2,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b3,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b4,
|
||||
ntohs(bindTarget->sin_port)
|
||||
);
|
||||
|
||||
if (IN4_IS_ADDR_UNSPECIFIED(&(bindTarget->sin_addr))
|
||||
|| IN4_ADDR_EQUAL(&(bindTarget->sin_addr), &(Context->IpAddresses.Addresses.TunnelIpv4)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
|
||||
bindTarget->sin_addr = Context->IpAddresses.Addresses.InternetIpv4;
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN6*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: [%X:%X:%X:%X:%X:%X:%X:%X]:%d\n",
|
||||
ntohs(bindTarget->sin6_addr.u.Word[0]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[1]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[2]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[3]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[4]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[5]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[6]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[7]),
|
||||
ntohs(bindTarget->sin6_port)
|
||||
);
|
||||
|
||||
static const IN6_ADDR IN6_ADDR_ANY = { 0 };
|
||||
|
||||
if (IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &IN6_ADDR_ANY)
|
||||
|| IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &(Context->IpAddresses.Addresses.TunnelIpv6)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
|
||||
bindTarget->sin6_addr = Context->IpAddresses.Addresses.InternetIpv6;
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
WdfWaitLockRelease(Context->IpAddresses.Lock);
|
||||
|
||||
Cleanup_data:
|
||||
|
||||
//
|
||||
// Call the "apply" function even in instances where we've made no changes
|
||||
// to the data, because it was deemed not necessary, or aborting for some other reason.
|
||||
//
|
||||
// This is the correct logic according to documentation.
|
||||
//
|
||||
|
||||
FwpsApplyModifiedLayerData0(classifyHandle, (PVOID*)&bindRequest, 0);
|
||||
|
||||
Cleanup_handle:
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
}
|
||||
|
||||
void
|
||||
ClassifyUnknownBind
|
||||
(
|
||||
|
||||
@@ -6,174 +6,6 @@
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
//
|
||||
// RewriteBind()
|
||||
//
|
||||
// This is where the splitting happens.
|
||||
// Move socket binds from tunnel interface to the internet connected interface.
|
||||
//
|
||||
void
|
||||
RewriteBind
|
||||
(
|
||||
CONTEXT *Context,
|
||||
const FWPS_INCOMING_VALUES0 *FixedValues,
|
||||
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
|
||||
UINT64 FilterId,
|
||||
const void *ClassifyContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(MetaValues);
|
||||
|
||||
UINT64 classifyHandle = 0;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0
|
||||
(
|
||||
const_cast<void*>(ClassifyContext),
|
||||
0,
|
||||
&classifyHandle
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed 0x%X\n", status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FWPS_BIND_REQUEST0 *bindRequest = NULL;
|
||||
|
||||
status = FwpsAcquireWritableLayerDataPointer0
|
||||
(
|
||||
classifyHandle,
|
||||
FilterId,
|
||||
0,
|
||||
(PVOID*)&bindRequest,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireWritableLayerDataPointer0() failed 0x%X\n", status);
|
||||
|
||||
goto Cleanup_handle;
|
||||
}
|
||||
|
||||
//
|
||||
// According to documentation, FwpsAcquireWritableLayerDataPointer0() will update the
|
||||
// `actionType` and `rights` fields with poorly chosen values:
|
||||
//
|
||||
// ```
|
||||
// classifyOut->actionType = FWP_ACTION_BLOCK
|
||||
// classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE
|
||||
// ```
|
||||
//
|
||||
// However, in practice it seems to not make any changes to those fields.
|
||||
// But if it did we'd want to ensure the fields have sane values.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_CONTINUE;
|
||||
ClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
|
||||
|
||||
//
|
||||
// There's a list with redirection history.
|
||||
//
|
||||
// This only ever comes into play if several callouts are fighting to redirect the bind.
|
||||
//
|
||||
// To prevent recursion, we need to check if we're on the list, and abort if so.
|
||||
//
|
||||
|
||||
for (auto history = bindRequest->previousVersion;
|
||||
history != NULL;
|
||||
history = history->previousVersion)
|
||||
{
|
||||
if (history->modifierFilterId == FilterId)
|
||||
{
|
||||
DbgPrint("Aborting bind processing because already redirected by us\n");
|
||||
|
||||
goto Cleanup_data;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Rewrite bind as applicable.
|
||||
//
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4;
|
||||
|
||||
WdfWaitLockAcquire(Context->IpAddresses.Lock, NULL);
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: %d.%d.%d.%d:%d\n",
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b1,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b2,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b3,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b4,
|
||||
ntohs(bindTarget->sin_port)
|
||||
);
|
||||
|
||||
if (IN4_IS_ADDR_UNSPECIFIED(&(bindTarget->sin_addr))
|
||||
|| IN4_ADDR_EQUAL(&(bindTarget->sin_addr), &(Context->IpAddresses.Addresses.TunnelIpv4)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
|
||||
bindTarget->sin_addr = Context->IpAddresses.Addresses.InternetIpv4;
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN6*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: [%X:%X:%X:%X:%X:%X:%X:%X]:%d\n",
|
||||
ntohs(bindTarget->sin6_addr.u.Word[0]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[1]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[2]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[3]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[4]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[5]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[6]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[7]),
|
||||
ntohs(bindTarget->sin6_port)
|
||||
);
|
||||
|
||||
static const IN6_ADDR IN6_ADDR_ANY = { 0 };
|
||||
|
||||
if (IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &IN6_ADDR_ANY)
|
||||
|| IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &(Context->IpAddresses.Addresses.TunnelIpv6)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
|
||||
bindTarget->sin6_addr = Context->IpAddresses.Addresses.InternetIpv6;
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
WdfWaitLockRelease(Context->IpAddresses.Lock);
|
||||
|
||||
Cleanup_data:
|
||||
|
||||
//
|
||||
// Call the "apply" function even in instances where we've made no changes
|
||||
// to the data, because it was deemed not necessary, or aborting for some other reason.
|
||||
//
|
||||
// This is the correct logic according to documentation.
|
||||
//
|
||||
|
||||
FwpsApplyModifiedLayerData0(classifyHandle, (PVOID*)&bindRequest, 0);
|
||||
|
||||
Cleanup_handle:
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RegisterFilterBindRedirectIpv4Tx
|
||||
(
|
||||
|
||||
@@ -6,19 +6,6 @@
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
// TODO-NOW Move this out to some place more suitable
|
||||
// Perhaps its own implementation file
|
||||
void
|
||||
RewriteBind
|
||||
(
|
||||
CONTEXT *Context,
|
||||
const FWPS_INCOMING_VALUES0 *FixedValues,
|
||||
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
|
||||
UINT64 FilterId,
|
||||
const void *ClassifyContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
//
|
||||
// RegisterFilterBindRedirectIpv4Tx()
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user