Based on release/2.3 branch, this patch is cross-compiled in MinGW and
tested on Windows XP/10. The VC project file is left untouched - you might
want to add rpcrt4.lib to compile and link it under MSVC.

Also, I didn't figure out how to keep ValdikSS's authorship of his code in
the patch (sorry :-( ). Definitely no expert in git... Maybe I should
generate a patch on top of his latest patch?

Take ValdikSS<i...@valdikss.org.ru>'s patch for "block-outside-dns" option
and
make it Windows-version-agnostic. My code is pretty messy right now, will
send
out a secondary patch later after tidying WFP-related code.
---
 doc/openvpn.8               |  12 +-
 src/openvpn/Makefile.am     |   2 +-
 src/openvpn/init.c          |  13 ++
 src/openvpn/openvpn.vcxproj |   0
 src/openvpn/options.c       |  11 +
 src/openvpn/options.h       |   1 +
 src/openvpn/win32.c         | 520
++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/win32.h         |   4 +
 8 files changed, 560 insertions(+), 3 deletions(-)
 mode change 100755 => 100644 src/openvpn/openvpn.vcxproj

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 8968c2e..9e4b01e 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -1119,8 +1119,8 @@ When used with
 .B \-\-client
 or
 .B \-\-pull,
-accept options pushed by server EXCEPT for routes and dhcp options
-like DNS servers.
+accept options pushed by server EXCEPT for routes, block-outside-dns and
dhcp
+options like DNS servers.

 When used on the client, this option effectively bars the
 server from adding routes to the client's routing table,
@@ -5448,6 +5448,14 @@ adapter list to the syslog or log file after the
TUN/TAP adapter
 has been brought up and any routes have been added.
 .\"*********************************************************
 .TP
+.B \-\-block\-outside\-dns
+Block DNS servers on other network adapters to prevent
+DNS leaks. This option prevents any application from accessing
+TCP or UDP port 53 except one inside the tunnel. It uses
+Windows Filtering Platform (WFP) and works on Windows Vista or
+later.
+.\"*********************************************************
+.TP
 .B \-\-dhcp\-renew
 Ask Windows to renew the TAP adapter lease on startup.
 This option is normally unnecessary, as Windows automatically
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 2e602f1..149a533 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -123,5 +123,5 @@ openvpn_LDADD = \
  $(OPTIONAL_DL_LIBS)
 if WIN32
 openvpn_SOURCES += openvpn_win32_resources.rc
-openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm
+openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lrpcrt4
-lwinmm
 endif
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index ceef4f7..960535d 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1468,6 +1468,13 @@ do_open_tun (struct context *c)
      "up",
      c->c2.es);

+      if (c->options.block_outside_dns)
+      {
+        dmsg (D_LOW, "Blocking outside DNS");
+        if (!win_wfp_block_dns(c->c1.tuntap->adapter_index))
+            msg (M_FATAL, "Blocking DNS failed!");
+      }
+
       /* possibly add routes */
       if (!c->options.route_delay_defined)
  do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
@@ -1594,6 +1601,12 @@ do_close_tun (struct context *c, bool force)
          "down",
          c->c2.es);

+            if (c->options.block_outside_dns)
+            {
+                if (!win_wfp_uninit())
+                    msg (M_FATAL, "Uninitialising WFP failed!");
+            }
+
    /* actually close tun/tap device based on --down-pre flag */
    if (c->options.down_pre)
      do_close_tun_simple (c);
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
old mode 100755
new mode 100644
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index f609aa6..564e670 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -715,6 +715,7 @@ static const char usage_message[] =
   "                       optional parameter controls the initial state of
ex.\n"
   "--show-net-up   : Show " PACKAGE_NAME "'s view of routing table and net
adapter list\n"
   "                  after TAP adapter is up and routes have been added.\n"
+  "--block-outside-dns   : Block DNS on other network adapters to prevent
DNS leaks\n"
   "Windows Standalone Options:\n"
   "\n"
   "--show-adapters : Show all TAP-Windows adapters.\n"
@@ -814,6 +815,7 @@ init_options (struct options *o, const bool init_gc)
   o->tuntap_options.dhcp_lease_time = 31536000; /* one year */
   o->tuntap_options.dhcp_masq_offset = 0;       /* use network address as
internal DHCP server address */
   o->route_method = ROUTE_METHOD_ADAPTIVE;
+  o->block_outside_dns = false;
 #endif
 #if P2MP_SERVER
   o->real_hash_size = 256;
@@ -1678,6 +1680,7 @@ show_settings (const struct options *o)
 #ifdef WIN32
   SHOW_BOOL (show_net_up);
   SHOW_INT (route_method);
+  SHOW_BOOL (block_outside_dns);
   show_tuntap_options (&o->tuntap_options);
 #endif
 #endif
@@ -6241,6 +6244,14 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_IPWIN32);
       options->tuntap_options.register_dns = true;
     }
+  else if (streq (p[0], "block-outside-dns") && !p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_IPWIN32);
+      if (win_wfp_init_funcs())
+      {
+        options->block_outside_dns = true;
+      }
+    }
   else if (streq (p[0], "rdns-internal"))
      /* standalone method for internal use
       *
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 40cf71e..622706a 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -593,6 +593,7 @@ struct options
   bool exit_event_initial_state;
   bool show_net_up;
   int route_method;
+  bool block_outside_dns;
 #endif

   bool use_peer_id;
diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c
index 5e7f13c..55eec95 100644
--- a/src/openvpn/win32.c
+++ b/src/openvpn/win32.c
@@ -47,6 +47,74 @@
 #include "memdbg.h"

 /*
+ * WFP-related defines and GUIDs.
+ */
+// #include <fwpmu.h>
+#include <initguid.h>
+// #include <fwpmtypes.h>
+#include <iphlpapi.h>
+
+#ifndef FWPM_SESSION_FLAG_DYNAMIC
+#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001
+#endif
+
+// c38d57d1-05a7-4c33-904f-7fbceee60e82
+DEFINE_GUID(
+   FWPM_LAYER_ALE_AUTH_CONNECT_V4,
+   0xc38d57d1,
+   0x05a7,
+   0x4c33,
+   0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82
+);
+
+// 4a72393b-319f-44bc-84c3-ba54dcb3b6b4
+DEFINE_GUID(
+   FWPM_LAYER_ALE_AUTH_CONNECT_V6,
+   0x4a72393b,
+   0x319f,
+   0x44bc,
+   0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4
+);
+
+// d78e1e87-8644-4ea5-9437-d809ecefc971
+DEFINE_GUID(
+   FWPM_CONDITION_ALE_APP_ID,
+   0xd78e1e87,
+   0x8644,
+   0x4ea5,
+   0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71
+);
+
+// c35a604d-d22b-4e1a-91b4-68f674ee674b
+DEFINE_GUID(
+   FWPM_CONDITION_IP_REMOTE_PORT,
+   0xc35a604d,
+   0xd22b,
+   0x4e1a,
+   0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b
+);
+
+// 4cd62a49-59c3-4969-b7f3-bda5d32890a4
+DEFINE_GUID(
+   FWPM_CONDITION_IP_LOCAL_INTERFACE,
+   0x4cd62a49,
+   0x59c3,
+   0x4969,
+   0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4
+);
+
+/*
+ * WFP firewall name.
+ */
+WCHAR * FIREWALL_NAME = L"OpenVPN"; /* GLOBAL */
+
+/*
+ * WFP handle and GUID.
+ */
+static HANDLE m_hEngineHandle = NULL; /* GLOBAL */
+static GUID m_subLayerGUID; /* GLOBAL */
+
+/*
  * Windows internal socket API state (opaque).
  */
 static struct WSAData wsa_state; /* GLOBAL */
@@ -1077,4 +1145,456 @@ win_get_tempdir()
   WideCharToMultiByte (CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof (tmpdir),
NULL, NULL);
   return tmpdir;
 }
+
+/* From fwptypes.h */
+
+#define FWP_ACTION_FLAG_TERMINATING     (0x00001000)
+#define FWP_ACTION_FLAG_NON_TERMINATING (0x00002000)
+
+#define FWP_ACTION_BLOCK  (0x1 | FWP_ACTION_FLAG_TERMINATING)
+#define FWP_ACTION_PERMIT (0x2 | FWP_ACTION_FLAG_TERMINATING)
+
+typedef UINT32 FWP_ACTION_TYPE;
+
+typedef enum FWP_DATA_TYPE_ {
+    FWP_EMPTY = 0,
+    FWP_UINT8 = 1,
+    FWP_UINT16 = 2,
+    FWP_UINT32 = 3,
+    FWP_UINT64 = 4,
+    FWP_INT8 = 5,
+    FWP_INT16 = 6,
+    FWP_INT32 = 7,
+    FWP_INT64 = 8,
+    FWP_FLOAT = 9,
+    FWP_DOUBLE = 10,
+    FWP_BYTE_ARRAY16_TYPE = 11,
+    FWP_BYTE_BLOB_TYPE = 12,
+    FWP_SID = 13,
+    FWP_SECURITY_DESCRIPTOR_TYPE = 14,
+    FWP_TOKEN_INFORMATION_TYPE = 15,
+    FWP_TOKEN_ACCESS_INFORMATION_TYPE = 16,
+    FWP_UNICODE_STRING_TYPE = 17,
+    FWP_BYTE_ARRAY6_TYPE = 18,
+    FWP_SINGLE_DATA_TYPE_MAX = 0xff,
+    FWP_V4_ADDR_MASK = 0x100,
+    FWP_V6_ADDR_MASK = 0x101,
+    FWP_RANGE_TYPE = 0x102,
+    FWP_DATA_TYPE_MAX = 0x103
+} FWP_DATA_TYPE;
+
+typedef enum FWP_MATCH_TYPE_ {
+    FWP_MATCH_EQUAL = 0,
+    FWP_MATCH_GREATER = 1,
+    FWP_MATCH_LESS = 2,
+    FWP_MATCH_GREATER_OR_EQUAL = 3,
+    FWP_MATCH_LESS_OR_EQUAL = 4,
+    FWP_MATCH_RANGE = 5,
+    FWP_MATCH_FLAGS_ALL_SET = 6,
+    FWP_MATCH_FLAGS_ANY_SET = 7,
+    FWP_MATCH_FLAGS_NONE_SET = 8,
+    FWP_MATCH_EQUAL_CASE_INSENSITIVE = 9,
+    FWP_MATCH_NOT_EQUAL = 10,
+    FWP_MATCH_TYPE_MAX = 11
+} FWP_MATCH_TYPE;
+
+typedef struct FWP_BYTE_ARRAY6_ {
+    UINT8 byteArray6[6];
+} FWP_BYTE_ARRAY6;
+
+typedef struct FWP_BYTE_ARRAY16_ {
+    UINT8 byteArray16[16];
+} FWP_BYTE_ARRAY16;
+
+typedef struct FWP_BYTE_BLOB_ {
+    UINT32 size;
+    UINT8 *data;
+} FWP_BYTE_BLOB;
+
+typedef struct FWP_TOKEN_INFORMATION_ {
+    ULONG sidCount;
+    PSID_AND_ATTRIBUTES sids;
+    ULONG restrictedSidCount;
+    PSID_AND_ATTRIBUTES restrictedSids;
+} FWP_TOKEN_INFORMATION;
+
+typedef struct FWP_VALUE0_ {
+    FWP_DATA_TYPE type;
+    __C89_NAMELESS union {
+        UINT8 uint8;
+        UINT16 uint16;
+        UINT32 uint32;
+        UINT64 *uint64;
+        INT8 int8;
+        INT16 int16;
+        INT32 int32;
+        INT64 *int64;
+        float float32;
+        double *double64;
+        FWP_BYTE_ARRAY16 *byteArray16;
+        FWP_BYTE_BLOB *byteBlob;
+        SID *sid;
+        FWP_BYTE_BLOB *sd;
+        FWP_TOKEN_INFORMATION *tokenInformation;
+        FWP_BYTE_BLOB *tokenAccessInformation;
+        LPWSTR unicodeString;
+        FWP_BYTE_ARRAY6 *byteArray6;
+    } __C89_NAMELESSUNIONNAME;
+} FWP_VALUE0;
+
+typedef struct FWP_V4_ADDR_AND_MASK_ {
+    UINT32 addr;
+    UINT32 mask;
+} FWP_V4_ADDR_AND_MASK;
+
+typedef struct FWP_V6_ADDR_AND_MASK_ {
+    UINT8 addr[16];
+    UINT8 prefixLength;
+} FWP_V6_ADDR_AND_MASK;
+
+typedef struct FWP_RANGE0_ {
+    FWP_VALUE0 valueLow;
+    FWP_VALUE0 valueHigh;
+} FWP_RANGE0;
+
+typedef struct FWP_CONDITION_VALUE0_ {
+    FWP_DATA_TYPE type;
+    __C89_NAMELESS union {
+        UINT8 uint8;
+        UINT16 uint16;
+        UINT32 uint32;
+        UINT64 *uint64;
+        INT8 int8;
+        INT16 int16;
+        INT32 int32;
+        INT64 *int64;
+        float float32;
+        double *double64;
+        FWP_BYTE_ARRAY16 *byteArray16;
+        FWP_BYTE_BLOB *byteBlob;
+        SID *sid;
+        FWP_BYTE_BLOB *sd;
+        FWP_TOKEN_INFORMATION *tokenInformation;
+        FWP_BYTE_BLOB *tokenAccessInformation;
+        LPWSTR unicodeString;
+        FWP_BYTE_ARRAY6 *byteArray6;
+        FWP_V4_ADDR_AND_MASK *v4AddrMask;
+        FWP_V6_ADDR_AND_MASK *v6AddrMask;
+        FWP_RANGE0 *rangeValue;
+    } __C89_NAMELESSUNIONNAME;
+} FWP_CONDITION_VALUE0;
+
+typedef struct FWPM_DISPLAY_DATA0_ {
+    wchar_t *name;
+    wchar_t *description;
+} FWPM_DISPLAY_DATA0;
+
+/* From fwpmtypes.h */
+
+typedef struct FWPM_ACTION0_ {
+  FWP_ACTION_TYPE type;
+  __C89_NAMELESS union {
+    GUID filterType;
+    GUID calloutKey;
+  };
+} FWPM_ACTION0;
+
+typedef struct FWPM_SESSION0_ {
+  GUID               sessionKey;
+  FWPM_DISPLAY_DATA0 displayData;
+  UINT32             flags;
+  UINT32             txnWaitTimeoutInMSec;
+  DWORD              processId;
+  SID                *sid;
+  wchar_t            *username;
+  WINBOOL            kernelMode;
+} FWPM_SESSION0;
+
+typedef struct FWPM_SUBLAYER0_ {
+  GUID               subLayerKey;
+  FWPM_DISPLAY_DATA0 displayData;
+  UINT16             flags;
+  GUID               *providerKey;
+  FWP_BYTE_BLOB      providerData;
+  UINT16             weight;
+} FWPM_SUBLAYER0;
+
+typedef struct FWPM_FILTER_CONDITION0_ {
+  GUID                 fieldKey;
+  FWP_MATCH_TYPE       matchType;
+  FWP_CONDITION_VALUE0 conditionValue;
+} FWPM_FILTER_CONDITION0;
+
+typedef struct FWPM_FILTER0_ {
+  GUID                   filterKey;
+  FWPM_DISPLAY_DATA0     displayData;
+  UINT32                 flags;
+  GUID                   *providerKey;
+  FWP_BYTE_BLOB          providerData;
+  GUID                   layerKey;
+  GUID                   subLayerKey;
+  FWP_VALUE0             weight;
+  UINT32                 numFilterConditions;
+  FWPM_FILTER_CONDITION0 *filterCondition;
+  FWPM_ACTION0           action;
+  __C89_NAMELESS union {
+    UINT64 rawContext;
+    GUID   providerContextKey;
+  };
+  GUID                   *reserved;
+  UINT64                 filterId;
+  FWP_VALUE0             effectiveWeight;
+} FWPM_FILTER0;
+
+#include <rpc.h>
+#include <rpcdce.h>
+
+/* Typedefs of WFP functions */
+
+#define NETIO_STATUS DWORD
+
+typedef NETIO_STATUS WINAPI *(*func_ConvertInterfaceIndexToLuid)(
+  NET_IFINDEX InterfaceIndex,
+  PNET_LUID InterfaceLuid
+);
+
+typedef DWORD WINAPI *(*func_FwpmEngineOpen0)(
+  const wchar_t *serverName,
+  UINT32 authnService,
+  SEC_WINNT_AUTH_IDENTITY_W *authIdentity,
+  const FWPM_SESSION0 *session,
+  HANDLE *engineHandle
+);
+
+typedef DWORD WINAPI *(*func_FwpmEngineClose0)(
+  HANDLE engineHandle
+);
+
+typedef DWORD WINAPI *(*func_FwpmFilterAdd0)(
+  HANDLE engineHandle,
+  const FWPM_FILTER0 *filter,
+  PSECURITY_DESCRIPTOR sd,
+  UINT64 *id
+);
+
+typedef DWORD WINAPI *(*func_FwpmSubLayerAdd0)(
+  HANDLE engineHandle,
+  const FWPM_SUBLAYER0 *subLayer,
+  PSECURITY_DESCRIPTOR sd
+);
+
+typedef DWORD WINAPI *(*func_FwpmSubLayerDeleteByKey0)(
+  HANDLE engineHandle,
+  const GUID *key
+);
+
+typedef void WINAPI *(*func_FwpmFreeMemory0)(
+  void **p
+);
+
+typedef DWORD WINAPI *(*func_FwpmGetAppIdFromFileName0)(
+  const wchar_t *fileName,
+  FWP_BYTE_BLOB **appId
+);
+
+/* WFP function pointers */
+func_ConvertInterfaceIndexToLuid ConvertInterfaceIndexToLuid = NULL;
+func_FwpmEngineOpen0 FwpmEngineOpen0 = NULL;
+func_FwpmEngineClose0 FwpmEngineClose0 = NULL;
+func_FwpmFilterAdd0 FwpmFilterAdd0 = NULL;
+func_FwpmSubLayerAdd0 FwpmSubLayerAdd0 = NULL;
+func_FwpmSubLayerDeleteByKey0 FwpmSubLayerDeleteByKey0 = NULL;
+func_FwpmFreeMemory0 FwpmFreeMemory0 = NULL;
+func_FwpmGetAppIdFromFileName0 FwpmGetAppIdFromFileName0 = NULL;
+
+bool
+win_wfp_init_funcs ()
+{
+  /* Initialize all WFP-related function pointers */
+  HMODULE iphlpapiHandle = LoadLibrary("iphlpapi.dll");
+  if (iphlpapiHandle == NULL)
+  {
+    msg (M_NONFATAL, "Can't load iphlpapi.dll");
+    return false;
+  }
+
+  HMODULE fwpuclntHandle = LoadLibrary("fwpuclnt.dll");
+  if (fwpuclntHandle == NULL)
+  {
+    msg (M_NONFATAL, "Can't load fwpuclnt.dll");
+    return false;
+  }
+
+  ConvertInterfaceIndexToLuid =
(func_ConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHandle,
"ConvertInterfaceIndexToLuid");
+  FwpmFilterAdd0 = (func_FwpmFilterAdd0)GetProcAddress(fwpuclntHandle,
"FwpmFilterAdd0");
+  FwpmEngineOpen0 = (func_FwpmEngineOpen0)GetProcAddress(fwpuclntHandle,
"FwpmEngineOpen0");
+  FwpmEngineClose0 = (func_FwpmEngineClose0)GetProcAddress(fwpuclntHandle,
"FwpmEngineClose0");
+  FwpmSubLayerAdd0 = (func_FwpmSubLayerAdd0)GetProcAddress(fwpuclntHandle,
"FwpmSubLayerAdd0");
+  FwpmSubLayerDeleteByKey0 =
(func_FwpmSubLayerDeleteByKey0)GetProcAddress(fwpuclntHandle,
"FwpmSubLayerDeleteByKey0");
+  FwpmFreeMemory0 = (func_FwpmFreeMemory0)GetProcAddress(fwpuclntHandle,
"FwpmFreeMemory0");
+  FwpmGetAppIdFromFileName0 =
(func_FwpmGetAppIdFromFileName0)GetProcAddress(fwpuclntHandle,
"FwpmGetAppIdFromFileName0");
+
+  if (!ConvertInterfaceIndexToLuid ||
+      !FwpmFilterAdd0 ||
+      !FwpmEngineOpen0 ||
+      !FwpmEngineClose0 ||
+      !FwpmSubLayerAdd0 ||
+      !FwpmSubLayerDeleteByKey0 ||
+      !FwpmFreeMemory0 ||
+      !FwpmGetAppIdFromFileName0)
+  {
+    msg (M_NONFATAL, "Can't get address for all WFP-related procedures.");
+    return false;
+  }
+
+  return true;
+}
+
+bool
+win_wfp_add_filter (HANDLE engineHandle,
+                    const FWPM_FILTER0 *filter,
+                    PSECURITY_DESCRIPTOR sd,
+                    UINT64 *id)
+{
+    if (FwpmFilterAdd0(engineHandle, filter, sd, id) != ERROR_SUCCESS)
+    {
+        msg (M_NONFATAL, "Can't add WFP filter");
+        return false;
+    }
+    return true;
+}
+
+bool
+win_wfp_block_dns (const NET_IFINDEX index)
+{
+    CLEAR(m_subLayerGUID);
+    FWPM_SESSION0 session = {0};
+    FWPM_SUBLAYER0 SubLayer = {0};
+    NET_LUID tapluid;
+    UINT64 filterid;
+    WCHAR openvpnpath[MAX_PATH];
+    FWP_BYTE_BLOB *openvpnblob = NULL;
+    FWPM_FILTER0 Filter = {0};
+    FWPM_FILTER_CONDITION0 Condition[2] = {0};
+
+    /* Add temporary filters which don't survive reboots or crashes. */
+    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
+
+    dmsg (D_LOW, "Opening WFP engine");
+
+    if (FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
&m_hEngineHandle) != ERROR_SUCCESS)
+    {
+        msg (M_NONFATAL, "Can't open WFP engine");
+        return false;
+    }
+
+    if (UuidCreate(&SubLayer.subLayerKey) != NO_ERROR)
+        return false;
+    memcpy(&m_subLayerGUID, &SubLayer.subLayerKey,
sizeof(SubLayer.subLayerKey));
+
+    /* Populate packet filter layer information. */
+    SubLayer.displayData.name = FIREWALL_NAME;
+    SubLayer.displayData.description = FIREWALL_NAME;
+    SubLayer.flags = 0;
+    SubLayer.weight = 0x100;
+
+    /* Add packet filter to our interface. */
+    dmsg (D_LOW, "Adding WFP sublayer");
+    if (FwpmSubLayerAdd0(m_hEngineHandle, &SubLayer, NULL) !=
ERROR_SUCCESS)
+    {
+        msg (M_NONFATAL, "Can't add WFP sublayer");
+        return false;
+    }
+
+    dmsg (D_LOW, "Blocking DNS using WFP");
+    if (ConvertInterfaceIndexToLuid(index, &tapluid) != NO_ERROR)
+    {
+        msg (M_NONFATAL, "Can't convert interface index to LUID");
+        return false;
+    }
+    dmsg (D_LOW, "Tap Luid: %I64d", tapluid.Value);
+
+    /* Get OpenVPN path. */
+    GetModuleFileNameW(NULL, openvpnpath, MAX_PATH);
+
+    if (FwpmGetAppIdFromFileName0(openvpnpath, &openvpnblob) !=
ERROR_SUCCESS)
+        return false;
+
+    /* Prepare filter. */
+    Filter.subLayerKey = m_subLayerGUID;
+    Filter.displayData.name = FIREWALL_NAME;
+    Filter.weight.type = FWP_EMPTY;
+    Filter.filterCondition = Condition;
+    Filter.numFilterConditions = 2;
+
+    /* First filter. Block IPv4 DNS queries except from OpenVPN itself. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+    Filter.action.type = FWP_ACTION_BLOCK;
+
+    Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+    Condition[0].matchType = FWP_MATCH_EQUAL;
+    Condition[0].conditionValue.type = FWP_UINT16;
+    Condition[0].conditionValue.uint16 = 53;
+
+    Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
+    Condition[1].matchType = FWP_MATCH_NOT_EQUAL;
+    Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
+    Condition[1].conditionValue.byteBlob = openvpnblob;
+
+    /* Add filter condition to our interface. */
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
+        goto err;
+    dmsg (D_LOW, "Filter (Block IPv4 DNS) added with ID=%I64d", filterid);
+
+    /* Second filter. Block IPv6 DNS queries except from OpenVPN itself. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+
+    /* Add filter condition to our interface. */
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
+        goto err;
+    dmsg (D_LOW, "Filter (Block IPv6 DNS) added with ID=%I64d", filterid);
+
+    /* Third filter. Permit IPv4 DNS queries from TAP. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+    Filter.action.type = FWP_ACTION_PERMIT;
+
+    Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
+    Condition[1].matchType = FWP_MATCH_EQUAL;
+    Condition[1].conditionValue.type = FWP_UINT64;
+    Condition[1].conditionValue.uint64 = &tapluid.Value;
+
+    /* Add filter condition to our interface. */
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
+        goto err;
+    dmsg (D_LOW, "Filter (Permit IPv4 DNS queries from TAP) added with
ID=%I64d", filterid);
+
+    /* Forth filter. Permit IPv6 DNS queries from TAP. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+
+    /* Add filter condition to our interface. */
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
+        goto err;
+    dmsg (D_LOW, "Filter (Permit IPv6 DNS queries from TAP) added with
ID=%I64d", filterid);
+
+    FwpmFreeMemory0((void **)&openvpnblob);
+    return true;
+
+    err:
+        FwpmFreeMemory0((void **)&openvpnblob);
+        return false;
+}
+
+bool
+win_wfp_uninit()
+{
+    dmsg (D_LOW, "Uninitializing WFP");
+    if (m_hEngineHandle) {
+        FwpmSubLayerDeleteByKey0(m_hEngineHandle, &m_subLayerGUID);
+        CLEAR(m_subLayerGUID);
+        FwpmEngineClose0(m_hEngineHandle);
+        m_hEngineHandle = NULL;
+    }
+    return true;
+}
+
 #endif
diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h
index cc18f02..776ef36 100644
--- a/src/openvpn/win32.h
+++ b/src/openvpn/win32.h
@@ -271,5 +271,9 @@ const char *win_get_tempdir();
 /* Convert a string from UTF-8 to UCS-2 */
 WCHAR *wide_string (const char* utf8, struct gc_arena *gc);

+bool win_wfp_init_funcs();
+bool win_wfp_block_dns(const NET_IFINDEX index);
+bool win_wfp_uninit();
+
 #endif
 #endif
-- 
1.9.1

Reply via email to