From 2c4e9580780f334fa1368d95f825a69169f1bb55 Mon Sep 17 00:00:00 2001 From: henrypp Date: Fri, 5 Apr 2019 02:23:25 +0600 Subject: [PATCH] code refactoring --- simplewall.vcxproj | 31 +- simplewall.vcxproj.filters | 39 + src/global.hpp | 95 ++ src/helper.cpp | 2188 ++++++++++++++++++++++++++++++++++++ src/helper.hpp | 54 + src/log.cpp | 898 +++++++++++++++ src/log.hpp | 27 + src/notifications.cpp | 1198 ++++++++++++++++++++ src/notifications.hpp | 29 + src/profile.cpp | 1228 ++++++++++++++++++++ src/profile.hpp | 40 + src/timer.cpp | 160 +++ src/timer.hpp | 12 + src/wfp.cpp | 1797 +++++++++++++++++++++++++++++ src/wfp.hpp | 41 + 15 files changed, 7828 insertions(+), 9 deletions(-) create mode 100644 src/global.hpp create mode 100644 src/helper.cpp create mode 100644 src/helper.hpp create mode 100644 src/log.cpp create mode 100644 src/log.hpp create mode 100644 src/notifications.cpp create mode 100644 src/notifications.hpp create mode 100644 src/profile.cpp create mode 100644 src/profile.hpp create mode 100644 src/timer.cpp create mode 100644 src/timer.hpp create mode 100644 src/wfp.cpp create mode 100644 src/wfp.hpp diff --git a/simplewall.vcxproj b/simplewall.vcxproj index 608dc632..715c2095 100644 --- a/simplewall.vcxproj +++ b/simplewall.vcxproj @@ -21,7 +21,7 @@ {7FB76828-A353-4113-82D7-CD728EED81DF} simplewall - 10.0.17763.0 + 10.0 simplewall @@ -30,7 +30,7 @@ Application true - v141 + v142 Unicode true false @@ -38,7 +38,7 @@ Application false - v141 + v142 true Unicode false @@ -46,7 +46,7 @@ Application true - v141 + v142 Unicode true false @@ -54,7 +54,7 @@ Application false - v141 + v142 true Unicode false @@ -139,7 +139,7 @@ Sync Guard true - _UNICODE;UNICODE;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;%(PreprocessorDefinitions) + _UNICODE;UNICODE;_APP_BETA;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;%(PreprocessorDefinitions) true false false @@ -181,7 +181,7 @@ Sync Guard true - _UNICODE;UNICODE;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;%(PreprocessorDefinitions) + _UNICODE;UNICODE;_APP_BETA;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;%(PreprocessorDefinitions) true false false @@ -224,7 +224,7 @@ MultiThreaded Sync ProgramDatabase - _UNICODE;UNICODE;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;_APP_HAVE_SIZING;%(PreprocessorDefinitions) + _UNICODE;UNICODE;_APP_BETA;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;_APP_HAVE_SIZING;%(PreprocessorDefinitions) true @@ -271,7 +271,7 @@ Sync true ProgramDatabase - _UNICODE;UNICODE;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;_APP_HAVE_SIZING;%(PreprocessorDefinitions) + _UNICODE;UNICODE;_APP_BETA;_APP_NO_GUEST;_APP_NO_WINXP;_APP_HAVE_AUTORUN;_APP_HAVE_SKIPUAC;_APP_HAVE_TRAY;_APP_HAVE_NTDLL;_APP_HAVE_SETTINGS;_APP_HAVE_UPDATES;_APP_HAVE_MINSIZE;_APP_HAVE_SIZING;%(PreprocessorDefinitions) false @@ -305,7 +305,13 @@ + + + + + + @@ -314,9 +320,16 @@ + + + + + + + diff --git a/simplewall.vcxproj.filters b/simplewall.vcxproj.filters index 97b26ef3..f1df705d 100644 --- a/simplewall.vcxproj.filters +++ b/simplewall.vcxproj.filters @@ -27,6 +27,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -56,6 +74,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/src/global.hpp b/src/global.hpp new file mode 100644 index 00000000..d2c64d62 --- /dev/null +++ b/src/global.hpp @@ -0,0 +1,95 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app.hpp" +#include "routine.hpp" +#include "rapp.hpp" +#include "main.hpp" + +typedef std::vector MFILTER_APPS; +typedef std::vector MFILTER_RULES; +typedef std::vector MTHREADPOOL; +typedef std::unordered_map MAPPS_MAP; +typedef std::unordered_map MCACHE_MAP; +typedef std::unordered_map MCACHETYPES_MAP; + +#include "helper.hpp" +#include "log.hpp" +#include "notifications.hpp" +#include "profile.hpp" +#include "timer.hpp" +#include "wfp.hpp" + +#include "pugiconfig.hpp" +#include "..\..\pugixml\src\pugixml.hpp" + +#include "resource.hpp" + +extern rapp app; + +extern STATIC_DATA config; + +extern FWPM_SESSION session; + +extern MAPPS_MAP apps; +extern std::vector rules_arr; +extern std::unordered_map rules_config; + +extern MCACHE_MAP cache_signatures; +extern MCACHE_MAP cache_versions; +extern MCACHE_MAP cache_dns; +extern MCACHE_MAP cache_hosts; +extern MCACHETYPES_MAP cache_types; + +extern MTHREADPOOL threads_pool; + +extern std::vector colors; +extern std::vector protocols; +extern std::vector items; +extern std::vector timers; + +extern std::vector notifications; + +extern MARRAY filter_ids; + +extern ITEM_LIST_HEAD log_stack; + +extern _R_FASTLOCK lock_access; +extern _R_FASTLOCK lock_apply; +extern _R_FASTLOCK lock_cache; +extern _R_FASTLOCK lock_checkbox; +extern _R_FASTLOCK lock_logbusy; +extern _R_FASTLOCK lock_logthread; +extern _R_FASTLOCK lock_notification; +extern _R_FASTLOCK lock_threadpool; +extern _R_FASTLOCK lock_transaction; +extern _R_FASTLOCK lock_writelog; + +// dropped events callback subscription (win7+) +#ifndef FWP_DIRECTION_IN +#define FWP_DIRECTION_IN 0x00003900L +#endif + +#ifndef FWP_DIRECTION_OUT +#define FWP_DIRECTION_OUT 0x00003901L +#endif diff --git a/src/helper.cpp b/src/helper.cpp new file mode 100644 index 00000000..7e552ecd --- /dev/null +++ b/src/helper.cpp @@ -0,0 +1,2188 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +UINT _app_gettab_id (HWND hwnd, size_t page_id) +{ + TCITEM tci = {0}; + + tci.mask = TCIF_PARAM; + + SendDlgItemMessage (hwnd, IDC_TAB, TCM_GETITEM, ((page_id == LAST_VALUE) ? SendDlgItemMessage (hwnd, IDC_TAB, TCM_GETCURSEL, 0, 0) : page_id), (LPARAM)& tci); + + return (UINT)tci.lParam; +} + +void _app_settab_id (HWND hwnd, size_t page_id) +{ + for (size_t i = 0; i < (size_t)SendDlgItemMessage (hwnd, IDC_TAB, TCM_GETITEMCOUNT, 0, 0); i++) + { + const UINT listview_id = _app_gettab_id (hwnd, i); + + if (listview_id == page_id) + { + SendDlgItemMessage (hwnd, IDC_TAB, TCM_SETCURSEL, i, 0); + + NMHDR hdr = {0}; + + hdr.code = TCN_SELCHANGE; + hdr.idFrom = IDC_TAB; + + SendMessage (hwnd, WM_NOTIFY, 0, (LPARAM)& hdr); + + break; + } + } +} + +void _app_applycasestyle (LPWSTR buffer, size_t length) +{ + if (buffer && length && wcschr (buffer, OBJ_NAME_PATH_SEPARATOR)) + { + buffer[0] = _r_str_upper (buffer[0]); + + for (size_t i = 1; i < length; i++) + buffer[i] = _r_str_lower (buffer[i]); + } +} + +bool _app_formataddress (PITEM_LOG const ptr_log, FWP_DIRECTION dir, UINT16 port, LPWSTR * ptr_dest, bool is_appenddns) +{ + if (!ptr_log || !ptr_dest) + return false; + + bool result = false; + + WCHAR formatted_address[512] = {0}; + + PIN_ADDR addrv4 = nullptr; + PIN6_ADDR addrv6 = nullptr; + + if (ptr_log->af == AF_INET) + { + addrv4 = (dir == FWP_DIRECTION_OUTBOUND || dir == FWP_DIRECTION_OUT) ? &ptr_log->remote_addr : &ptr_log->local_addr; + + if (InetNtop (AF_INET, addrv4, formatted_address, _countof (formatted_address))) + result = !IN4_IS_ADDR_UNSPECIFIED (addrv4); + } + else if (ptr_log->af == AF_INET6) + { + addrv6 = (dir == FWP_DIRECTION_OUTBOUND || dir == FWP_DIRECTION_OUT) ? &ptr_log->remote_addr6 : &ptr_log->local_addr6; + + if (InetNtop (AF_INET6, addrv6, formatted_address, _countof (formatted_address))) + result = !IN6_IS_ADDR_UNSPECIFIED (addrv6); + } + + if (port) + StringCchCat (formatted_address, _countof (formatted_address), _r_fmt (L":%d", port)); + + if (result && is_appenddns && app.ConfigGet (L"IsNetworkResolutionsEnabled", false).AsBool () && config.is_wsainit) + { + const size_t hash = _r_str_hash (formatted_address); + + if (hash) + { + _r_fastlock_acquireshared (&lock_cache); + const bool is_exists = cache_hosts.find (hash) != cache_hosts.end (); + _r_fastlock_releaseshared (&lock_cache); + + if (is_exists) + { + _r_fastlock_acquireshared (&lock_cache); + + if (cache_hosts[hash]) + StringCchCat (formatted_address, _countof (formatted_address), _r_fmt (L" (%s)", cache_hosts[hash])); + + _r_fastlock_releaseshared (&lock_cache); + } + else + { + WCHAR hostBuff[NI_MAXHOST] = {0}; + LPWSTR cache_ptr = nullptr; + + if (_app_resolveaddress (ptr_log->af, (ptr_log->af == AF_INET) ? (LPVOID)addrv4 : (LPVOID)addrv6, hostBuff, _countof (hostBuff))) + { + StringCchCat (formatted_address, _countof (formatted_address), _r_fmt (L" (%s)", hostBuff)); + + _r_str_alloc (&cache_ptr, _r_str_length (hostBuff), hostBuff); + } + + _r_fastlock_acquireexclusive (&lock_cache); + + _app_freecache (&cache_hosts); + + cache_hosts[hash] = cache_ptr; + + _r_fastlock_releaseexclusive (&lock_cache); + } + } + } + + _r_str_alloc (ptr_dest, _r_str_length (formatted_address), formatted_address); + + return result; +} + +void _app_freearray (std::vector * ptr) +{ + if (!ptr) + return; + + for (size_t i = 0; i < ptr->size (); i++) + { + PITEM_ADD ptr_item = ptr->at (i); + + SAFE_DELETE (ptr_item); + } + + ptr->clear (); +} + +void _app_freecache (MCACHE_MAP * ptr_map) +{ + if (ptr_map->size () <= UMAP_CACHE_LIMIT) + return; + + for (auto &p : *ptr_map) + { + LPWSTR ptr_buffer = p.second; + + SAFE_DELETE_ARRAY (ptr_buffer); + } + + ptr_map->clear (); +} + +void _app_freethreadpool (MTHREADPOOL * ptr_pool) +{ + if (!ptr_pool || ptr_pool->empty ()) + return; + + const size_t count = ptr_pool->size (); + + for (size_t i = (count - 1); i != LAST_VALUE; i--) + { + const HANDLE hthread = ptr_pool->at (i); + + if (WaitForSingleObjectEx (hthread, 0, FALSE) == WAIT_OBJECT_0) + { + CloseHandle (hthread); + ptr_pool->erase (ptr_pool->begin () + i); + } + } +} + +void _app_freelogstack () +{ + while (true) + { + const PSLIST_ENTRY listEntry = RtlInterlockedPopEntrySList (&log_stack.ListHead); + + if (!listEntry) + break; + + InterlockedDecrement (&log_stack.item_count); + + PITEM_LIST_ENTRY ptr_entry = CONTAINING_RECORD (listEntry, ITEM_LIST_ENTRY, ListEntry); + + SAFE_DELETE (ptr_entry->Body); + + _aligned_free (ptr_entry); + } +} + +void _app_getappicon (ITEM_APP const *ptr_app, bool is_small, size_t * picon_id, HICON * picon) +{ + const bool is_iconshidden = app.ConfigGet (L"IsIconsHidden", false).AsBool (); + + if (ptr_app->type == AppRegular || ptr_app->type == AppService) + { + if (is_iconshidden || !_app_getfileicon (ptr_app->real_path, is_small, picon_id, picon)) + { + if (picon_id) + *picon_id = config.icon_id; + + if (picon) + *picon = CopyIcon (is_small ? config.hicon_small : config.hicon_large); + } + } + else if (ptr_app->type == AppPackage) + { + if (picon_id) + *picon_id = config.icon_package_id; + + if (picon) + *picon = CopyIcon (is_small ? config.hicon_package : config.hicon_large); // small-only! + } + else + { + if (picon_id) + *picon_id = config.icon_id; + + if (picon) + *picon = CopyIcon (is_small ? config.hicon_small : config.hicon_large); + } +} + +void _app_getdisplayname (size_t hash, ITEM_APP const *ptr_app, LPWSTR * extracted_name) +{ + if (!extracted_name) + return; + + if (ptr_app->type == AppService) + { + _r_str_alloc (extracted_name, _r_str_length (ptr_app->original_path), ptr_app->original_path); + } + else if (ptr_app->type == AppPackage) + { + rstring name; + + if (!_app_item_get (ptr_app->type, hash, &name, nullptr, nullptr, nullptr, nullptr)) + name = ptr_app->original_path; + + _r_str_alloc (extracted_name, name.GetLength (), name); + } + else + { + LPCWSTR ptr_path = ((hash == config.ntoskrnl_hash) ? ptr_app->original_path : ptr_app->real_path); + + if (app.ConfigGet (L"ShowFilenames", true).AsBool ()) + { + const rstring path = _r_path_extractfile (ptr_path); + + _r_str_alloc (extracted_name, path.GetLength (), path); + } + else + { + _r_str_alloc (extracted_name, _r_str_length (ptr_path), ptr_path); + } + } +} + +bool _app_getfileicon (LPCWSTR path, bool is_small, size_t * picon_id, HICON * picon) +{ + if (!path || !_r_fs_exists (path) || (!picon_id && !picon)) + return false; + + bool result = false; + + SHFILEINFO shfi = {0}; + DWORD flags = SHGFI_USEFILEATTRIBUTES; + + if (picon_id) + flags |= SHGFI_SYSICONINDEX; + + if (picon) + flags |= SHGFI_ICON; + + if (is_small) + flags |= SHGFI_SMALLICON; + + const HRESULT hrComInit = CoInitialize (nullptr); + + if ((hrComInit == RPC_E_CHANGED_MODE) || SUCCEEDED (hrComInit)) + { + if (SHGetFileInfo (path, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof (shfi), flags)) + { + if (picon_id) + *picon_id = (size_t)shfi.iIcon; + + if (picon && shfi.hIcon) + { + *picon = CopyIcon (shfi.hIcon); + DestroyIcon (shfi.hIcon); + } + + result = true; + } + + if (SUCCEEDED (hrComInit)) + CoUninitialize (); + } + + return result; +} + +rstring _app_getshortcutpath (HWND hwnd, LPCWSTR path) +{ + rstring result; + + IShellLink *psl = nullptr; + + const HRESULT hrComInit = CoInitializeEx (nullptr, COINIT_MULTITHREADED); + + if ((hrComInit == RPC_E_CHANGED_MODE) || SUCCEEDED (hrComInit)) + { + if (SUCCEEDED (CoInitializeSecurity (nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0, nullptr))) + { + if (SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)& psl))) + { + IPersistFile *ppf = nullptr; + + if (SUCCEEDED (psl->QueryInterface (IID_IPersistFile, (void **)& ppf))) + { + if (SUCCEEDED (ppf->Load (path, STGM_READ))) + { + if (SUCCEEDED (psl->Resolve (hwnd, 0))) + { + WIN32_FIND_DATA wfd = {0}; + WCHAR buffer[MAX_PATH] = {0}; + + if (SUCCEEDED (psl->GetPath (buffer, _countof (buffer), (LPWIN32_FIND_DATA)& wfd, SLGP_RAWPATH))) + result = buffer; + } + } + + ppf->Release (); + } + + psl->Release (); + } + } + + if (SUCCEEDED (hrComInit)) + CoUninitialize (); + } + + return result; +} + +bool _app_getsignatureinfo (size_t hash, LPCWSTR path, LPWSTR * psigner) +{ + if (!app.ConfigGet (L"IsCerificatesEnabled", false).AsBool ()) + return false; + + if (!psigner || !path || !path[0]) + return false; + + _r_fastlock_acquireshared (&lock_cache); + const bool is_exists = cache_signatures.find (hash) != cache_signatures.end (); + _r_fastlock_releaseshared (&lock_cache); + + if (is_exists) + { + _r_fastlock_acquireshared (&lock_cache); + + LPCWSTR ptr_text = cache_signatures[hash]; + + _r_str_alloc (psigner, _r_str_length (ptr_text), ptr_text); + + _r_fastlock_releaseshared (&lock_cache); + + return (*psigner != nullptr); + } + + bool result = false; + + cache_signatures[hash] = nullptr; + + const HANDLE hfile = CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); + + if (hfile != INVALID_HANDLE_VALUE) + { + static GUID WinTrustActionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; + + WINTRUST_FILE_INFO fileInfo = {0}; + + fileInfo.cbStruct = sizeof (fileInfo); + fileInfo.pcwszFilePath = path; + fileInfo.hFile = hfile; + + WINTRUST_DATA trustData = {0}; + + trustData.cbStruct = sizeof (trustData); + trustData.dwUIChoice = WTD_UI_NONE; + trustData.fdwRevocationChecks = WTD_REVOKE_NONE; + trustData.dwProvFlags = WTD_SAFER_FLAG | WTD_CACHE_ONLY_URL_RETRIEVAL; + trustData.dwUnionChoice = WTD_CHOICE_FILE; + trustData.pFile = &fileInfo; + + trustData.dwStateAction = WTD_STATEACTION_VERIFY; + const LONG status = WinVerifyTrust ((HWND)INVALID_HANDLE_VALUE, &WinTrustActionGenericVerifyV2, &trustData); + + if (status == S_OK) + { + PCRYPT_PROVIDER_DATA provData = WTHelperProvDataFromStateData (trustData.hWVTStateData); + + if (provData) + { + PCRYPT_PROVIDER_SGNR psProvSigner = WTHelperGetProvSignerFromChain (provData, 0, FALSE, 0); + + if (psProvSigner) + { + CRYPT_PROVIDER_CERT *psProvCert = WTHelperGetProvCertFromChain (psProvSigner, 0); + + if (psProvCert) + { + const DWORD num_chars = CertGetNameString (psProvCert->pCert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, nullptr, 0) + 1; + + if (num_chars > 1) + { + LPWSTR ptr_text = new WCHAR[num_chars]; + + if (ptr_text) + { + if (!CertGetNameString (psProvCert->pCert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, ptr_text, num_chars)) + SAFE_DELETE_ARRAY (ptr_text); + } + + _r_fastlock_acquireexclusive (&lock_cache); + + _app_freecache (&cache_signatures); + + _r_str_alloc (psigner, _r_str_length (ptr_text), ptr_text); + cache_signatures[hash] = ptr_text; + + _r_fastlock_releaseexclusive (&lock_cache); + } + } + } + } + + result = true; + } + + trustData.dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrust ((HWND)INVALID_HANDLE_VALUE, &WinTrustActionGenericVerifyV2, &trustData); + + CloseHandle (hfile); + } + + return result; +} + +bool _app_getversioninfo (size_t hash, LPCWSTR path, LPWSTR * pinfo) +{ + if (!path || !pinfo) + return false; + + _r_fastlock_acquireshared (&lock_cache); + const bool is_exists = cache_versions.find (hash) != cache_versions.end (); + _r_fastlock_releaseshared (&lock_cache); + + if (is_exists) + { + _r_fastlock_acquireshared (&lock_cache); + + LPCWSTR ptr_text = cache_versions[hash]; + + _r_str_alloc (pinfo, _r_str_length (ptr_text), ptr_text); + + _r_fastlock_releaseshared (&lock_cache); + + return (*pinfo != nullptr); + } + + bool result = false; + rstring buffer; + + cache_versions[hash] = nullptr; + + const HINSTANCE hlib = LoadLibraryEx (path, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE); + + if (hlib) + { + const HRSRC hres = FindResource (hlib, MAKEINTRESOURCE (VS_VERSION_INFO), RT_VERSION); + + if (hres) + { + const HGLOBAL hglob = LoadResource (hlib, hres); + + if (hglob) + { + const LPVOID versionInfo = LockResource (hglob); + + if (versionInfo) + { + UINT vLen = 0, langD = 0; + LPVOID retbuf = nullptr; + + WCHAR author_entry[128] = {0}; + WCHAR description_entry[128] = {0}; + + if (VerQueryValue (versionInfo, L"\\VarFileInfo\\Translation", &retbuf, &vLen) && vLen == 4) + { + CopyMemory (&langD, retbuf, vLen); + StringCchPrintf (author_entry, _countof (author_entry), L"\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName", (langD & 0xff00) >> 8, langD & 0xff, (langD & 0xff000000) >> 24, (langD & 0xff0000) >> 16); + StringCchPrintf (description_entry, _countof (description_entry), L"\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription", (langD & 0xff00) >> 8, langD & 0xff, (langD & 0xff000000) >> 24, (langD & 0xff0000) >> 16); + } + else + { + StringCchPrintf (author_entry, _countof (author_entry), L"\\StringFileInfo\\%04X04B0\\CompanyName", GetUserDefaultLangID ()); + StringCchPrintf (description_entry, _countof (description_entry), L"\\StringFileInfo\\%04X04B0\\FileDescription", GetUserDefaultLangID ()); + } + + if (VerQueryValue (versionInfo, description_entry, &retbuf, &vLen)) + { + buffer.Append (SZ_TAB); + buffer.Append (LPCWSTR (retbuf)); + + UINT length = 0; + VS_FIXEDFILEINFO *verInfo = nullptr; + + if (VerQueryValue (versionInfo, L"\\", (void **)(&verInfo), &length)) + { + buffer.Append (_r_fmt (L" %d.%d", HIWORD (verInfo->dwFileVersionMS), LOWORD (verInfo->dwFileVersionMS))); + + if (HIWORD (verInfo->dwFileVersionLS) || LOWORD (verInfo->dwFileVersionLS)) + { + buffer.Append (_r_fmt (L".%d", HIWORD (verInfo->dwFileVersionLS))); + + if (LOWORD (verInfo->dwFileVersionLS)) + buffer.Append (_r_fmt (L".%d", LOWORD (verInfo->dwFileVersionLS))); + } + } + + buffer.Append (L"\r\n"); + } + + if (VerQueryValue (versionInfo, author_entry, &retbuf, &vLen)) + { + buffer.Append (SZ_TAB); + buffer.Append (static_cast(retbuf)); + buffer.Append (L"\r\n"); + } + + buffer.Trim (L"\r\n "); + + // get signature information + { + LPWSTR ptr_text = nullptr; + + _r_str_alloc (&ptr_text, buffer.GetLength (), buffer); + _r_str_alloc (pinfo, buffer.GetLength (), buffer); + + _r_fastlock_acquireexclusive (&lock_cache); + + _app_freecache (&cache_versions); + + cache_versions[hash] = ptr_text; + + _r_fastlock_releaseexclusive (&lock_cache); + } + + result = true; + } + + FreeResource (hglob); + } + } + + FreeLibrary (hlib); + } + + return result; +} + +size_t _app_getposition (HWND hwnd, size_t hash) +{ + const UINT listview_id = _app_gettab_id (hwnd); + + for (size_t i = 0; i < _r_listview_getitemcount (hwnd, listview_id); i++) + { + if ((size_t)_r_listview_getitemlparam (hwnd, listview_id, i) == hash) + return i; + } + + return LAST_VALUE; +} + +rstring _app_getprotoname (UINT8 proto) +{ + for (size_t i = 0; i < protocols.size (); i++) + { + PITEM_PROTOCOL const ptr_proto = protocols.at (i); + + if (ptr_proto && proto == ptr_proto->id) + return ptr_proto->pname; + } + + return SZ_EMPTY; +} + +void _app_generate_packages () +{ + HKEY hkey = nullptr; + HKEY hsubkey = nullptr; + + LONG result = RegOpenKeyEx (HKEY_CLASSES_ROOT, L"Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\Repository\\Packages", 0, KEY_READ, &hkey); + + if (result != ERROR_SUCCESS) + { + if (result != ERROR_FILE_NOT_FOUND) + _app_logerror (L"RegOpenKeyEx", result, nullptr, true); + } + else + { + DWORD index = 0; + + while (true) + { + rstring package_sid_string; + + WCHAR key_name[MAX_PATH] = {0}; + + PSID package_sid[SECURITY_MAX_SID_SIZE] = {0}; + + DWORD size = _countof (key_name) * sizeof (key_name[0]); + + if (RegEnumKeyEx (hkey, index++, key_name, &size, 0, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + result = RegOpenKeyEx (hkey, key_name, 0, KEY_READ, &hsubkey); + + if (result != ERROR_SUCCESS) + { + if (result != ERROR_FILE_NOT_FOUND) + _app_logerror (L"RegOpenKeyEx", result, key_name, true); + } + else + { + size = _countof (package_sid) * sizeof (package_sid[0]); + result = RegQueryValueEx (hsubkey, L"PackageSid", nullptr, nullptr, (LPBYTE)package_sid, &size); + + if (result != ERROR_SUCCESS) + { + if (result != ERROR_FILE_NOT_FOUND) + _app_logerror (L"RegQueryValueEx", result, key_name, true); + + continue; + } + + package_sid_string = _r_str_fromsid (package_sid); + + const size_t hash = _r_str_hash (package_sid_string); + + WCHAR display_name[MAX_PATH] = {0}; + WCHAR path[MAX_PATH] = {0}; + + PITEM_ADD ptr_item = new ITEM_ADD; + + ptr_item->hash = hash; + + size = _countof (display_name) * sizeof (display_name[0]); + result = RegQueryValueEx (hsubkey, L"DisplayName", nullptr, nullptr, (LPBYTE)display_name, &size); + + if (result == ERROR_SUCCESS) + { + if (display_name[0] == L'@') + { + if (!SUCCEEDED (SHLoadIndirectString (rstring (display_name), display_name, _countof (display_name), nullptr)) || !display_name[0]) + StringCchCopy (display_name, _countof (display_name), key_name[0] ? key_name : package_sid_string); + } + } + + size = _countof (path) * sizeof (path[0]); + RegQueryValueEx (hsubkey, L"PackageRootFolder", nullptr, nullptr, (LPBYTE)path, &size); + + // query timestamp + FILETIME ft = {0}; + + if (RegQueryInfoKey (hsubkey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &ft) == ERROR_SUCCESS) + ptr_item->timestamp = _r_unixtime_from_filetime (&ft); + + ptr_item->type = AppPackage; + + _r_str_alloc (&ptr_item->sid, package_sid_string.GetLength (), package_sid_string); + _r_str_alloc (&ptr_item->display_name, _r_str_length (display_name), display_name); + _r_str_alloc (&ptr_item->real_path, _r_str_length (path), path); + + ConvertStringSidToSid (package_sid_string, &ptr_item->psid); + + _app_load_appxmanifest (ptr_item); + + items.push_back (ptr_item); + + RegCloseKey (hsubkey); + } + } + + RegCloseKey (hkey); + } +} + +//void _app_generate_processes () +//{ +// _app_freearray (&processes); +// +// NTSTATUS status = 0; +// +// ULONG length = 0x4000; +// PBYTE buffer = new BYTE[length]; +// +// while (true) +// { +// status = NtQuerySystemInformation (SystemProcessInformation, buffer, length, &length); +// +// if (status == 0xC0000023L /*STATUS_BUFFER_TOO_SMALL*/ || status == 0xc0000004 /*STATUS_INFO_LENGTH_MISMATCH*/) +// { +// SAFE_DELETE_ARRAY (buffer); +// +// buffer = new BYTE[length]; +// } +// else +// { +// break; +// } +// } +// +// if (NT_SUCCESS (status)) +// { +// PSYSTEM_PROCESS_INFORMATION spi = (PSYSTEM_PROCESS_INFORMATION)buffer; +// +// std::unordered_map checker; +// +// do +// { +// const DWORD pid = (DWORD)(DWORD_PTR)spi->UniqueProcessId; +// +// if (!pid) // skip "system idle process" +// continue; +// +// const HANDLE hprocess = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); +// +// if (hprocess) +// { +// WCHAR display_name[MAX_PATH] = {0}; +// WCHAR real_path[MAX_PATH] = {0}; +// +// size_t hash = 0; +// +// StringCchPrintf (display_name, _countof (display_name), L"%s (%lu)", spi->ImageName.Buffer, pid); +// +// if (pid == PROC_SYSTEM_PID) +// { +// StringCchCopy (real_path, _countof (real_path), _r_path_expand (PATH_NTOSKRNL)); +// hash = _r_str_hash (spi->ImageName.Buffer); +// } +// else +// { +// DWORD size = _countof (real_path) - 1; +// +// if (QueryFullProcessImageName (hprocess, 0, real_path, &size)) +// { +// _app_applycasestyle (real_path, _r_str_length (real_path)); // apply case-style +// hash = _r_str_hash (real_path); +// } +// else +// { +// // cannot get file path because it's not filesystem process (Pico maybe?) +// if (GetLastError () == ERROR_GEN_FAILURE) +// { +// StringCchCopy (real_path, _countof (real_path), spi->ImageName.Buffer); +// hash = _r_str_hash (spi->ImageName.Buffer); +// } +// else +// { +// CloseHandle (hprocess); +// continue; +// } +// } +// } +// +// if (hash && apps.find (hash) == apps.end () && checker.find (hash) == checker.end ()) +// { +// checker[hash] = true; +// +// PITEM_ADD ptr_item = new ITEM_ADD; +// +// ptr_item->hash = hash; +// +// _r_str_alloc (&ptr_item->display_name, _r_str_length (display_name), display_name); +// _r_str_alloc (&ptr_item->real_path, _r_str_length (((pid == PROC_SYSTEM_PID) ? PROC_SYSTEM_NAME : real_path)), ((pid == PROC_SYSTEM_PID) ? PROC_SYSTEM_NAME : real_path)); +// +// // get file icon +// { +// HICON hicon = nullptr; +// +// if (!app.ConfigGet (L"IsIconsHidden", false).AsBool () && _app_getfileicon (real_path, true, nullptr, &hicon)) +// { +// ptr_item->hbmp = _app_ico2bmp (hicon); +// DestroyIcon (hicon); +// } +// } +// +// processes.push_back (ptr_item); +// } +// +// CloseHandle (hprocess); +// } +// } +// while ((spi = ((spi->NextEntryOffset ? (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(spi)+(spi)->NextEntryOffset) : nullptr))) != nullptr); +// +// std::sort (processes.begin (), processes.end (), +// [](const PITEM_ADD& a, const PITEM_ADD& b)->bool { +// return StrCmpLogicalW (a->display_name, b->display_name) == -1; +// }); +// } +// +// SAFE_DELETE_ARRAY (buffer); // free the allocated buffer +//} + +void _app_generate_services () +{ + const SC_HANDLE hsvcmgr = OpenSCManager (nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + + if (hsvcmgr) + { + ENUM_SERVICE_STATUS service; + + DWORD dwBytesNeeded = 0; + DWORD dwServicesReturned = 0; + DWORD dwResumedHandle = 0; + DWORD dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS; + const DWORD dwServiceState = SERVICE_STATE_ALL; + + // win10+ + if (_r_sys_validversion (10, 0)) + dwServiceType |= SERVICE_INTERACTIVE_PROCESS | SERVICE_USER_SERVICE | SERVICE_USERSERVICE_INSTANCE; + + if (!EnumServicesStatus (hsvcmgr, dwServiceType, dwServiceState, &service, sizeof (ENUM_SERVICE_STATUS), &dwBytesNeeded, &dwServicesReturned, &dwResumedHandle)) + { + if (GetLastError () == ERROR_MORE_DATA) + { + // Set the buffer + const DWORD dwBytes = sizeof (ENUM_SERVICE_STATUS) + dwBytesNeeded; + LPENUM_SERVICE_STATUS pServices = new ENUM_SERVICE_STATUS[dwBytes]; + + // Now query again for services + if (EnumServicesStatus (hsvcmgr, dwServiceType, dwServiceState, (LPENUM_SERVICE_STATUS)pServices, dwBytes, &dwBytesNeeded, &dwServicesReturned, &dwResumedHandle)) + { + // now traverse each service to get information + for (DWORD i = 0; i < dwServicesReturned; i++) + { + LPENUM_SERVICE_STATUS psvc = (pServices + i); + + LPCWSTR display_name = psvc->lpDisplayName; + LPCWSTR service_name = psvc->lpServiceName; + + WCHAR buffer[MAX_PATH] = {0}; + WCHAR real_path[MAX_PATH] = {0}; + + time_t timestamp = 0; + + // get binary path + const SC_HANDLE hsvc = OpenService (hsvcmgr, service_name, SERVICE_QUERY_CONFIG); + + if (hsvc) + { + LPQUERY_SERVICE_CONFIG lpqsc = {0}; + DWORD bytes_needed = 0; + + if (!QueryServiceConfig (hsvc, nullptr, 0, &bytes_needed)) + { + lpqsc = new QUERY_SERVICE_CONFIG[bytes_needed]; + + if (QueryServiceConfig (hsvc, lpqsc, bytes_needed, &bytes_needed)) + { + FILETIME ft = {0}; + + // query "ServiceDll" path + { + HKEY hkey = nullptr; + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, _r_fmt (L"System\\CurrentControlSet\\Services\\%s\\Parameters", service_name), 0, KEY_READ, &hkey) == ERROR_SUCCESS) + { + DWORD size = _countof (real_path) * sizeof (real_path[0]); + + if (RegQueryInfoKey (hkey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &ft) == ERROR_SUCCESS) + timestamp = _r_unixtime_from_filetime (&ft); + + if (RegQueryValueEx (hkey, L"ServiceDll", nullptr, nullptr, (LPBYTE)real_path, &size) == ERROR_SUCCESS) + StringCchCopy (real_path, _countof (real_path), _r_path_expand (real_path)); + + RegCloseKey (hkey); + } + } + + // query "lpftLastWriteTime" + if (!timestamp) + { + HKEY hkey = nullptr; + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, _r_fmt (L"System\\CurrentControlSet\\Services\\%s", service_name), 0, KEY_READ, &hkey) == ERROR_SUCCESS) + { + if (RegQueryInfoKey (hkey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &ft) == ERROR_SUCCESS) + timestamp = _r_unixtime_from_filetime (&ft); + + RegCloseKey (hkey); + } + } + + // set path + if (!real_path[0]) + { + StringCchCopy (real_path, _countof (real_path), lpqsc->lpBinaryPathName); + + PathRemoveArgs (real_path); + PathUnquoteSpaces (real_path); + } + + _app_applycasestyle (real_path, _r_str_length (real_path)); // apply case-style + } + else + { + SAFE_DELETE_ARRAY (lpqsc); + continue; + } + + SAFE_DELETE_ARRAY (lpqsc); + } + + CloseServiceHandle (hsvc); + } + + UNICODE_STRING serviceNameUs = {0}; + + serviceNameUs.Buffer = buffer; + serviceNameUs.Length = (USHORT)(_r_str_length (service_name) * sizeof (WCHAR)); + serviceNameUs.MaximumLength = serviceNameUs.Length; + + StringCchCopy (buffer, _countof (buffer), service_name); + + SID *serviceSid = nullptr; + ULONG serviceSidLength = 0; + + rstring sidstring; + + // get service security identifier + if (RtlCreateServiceSid (&serviceNameUs, serviceSid, &serviceSidLength) == 0xC0000023 /*STATUS_BUFFER_TOO_SMALL*/) + { + serviceSid = new SID[serviceSidLength]; + + if (serviceSid) + { + if (NT_SUCCESS (RtlCreateServiceSid (&serviceNameUs, serviceSid, &serviceSidLength))) + { + sidstring = _r_str_fromsid (serviceSid); + } + else + { + SAFE_DELETE_ARRAY (serviceSid); + } + } + } + + if (serviceSid && !sidstring.IsEmpty ()) + { + PITEM_ADD ptr_item = new ITEM_ADD; + + ptr_item->hash = _r_str_hash (service_name); + ptr_item->timestamp = timestamp; + ptr_item->type = AppService; + + _r_str_alloc (&ptr_item->service_name, _r_str_length (service_name), service_name); + _r_str_alloc (&ptr_item->display_name, _r_str_length (display_name), display_name); + _r_str_alloc (&ptr_item->real_path, _r_str_length (real_path), real_path); + _r_str_alloc (&ptr_item->sid, sidstring.GetLength (), sidstring); + + if (!ConvertStringSecurityDescriptorToSecurityDescriptor (_r_fmt (SERVICE_SECURITY_DESCRIPTOR, sidstring.GetString ()).ToUpper (), SDDL_REVISION_1, &ptr_item->psd, nullptr)) + { + _app_logerror (L"ConvertStringSecurityDescriptorToSecurityDescriptor", GetLastError (), service_name, false); + + SAFE_DELETE (ptr_item); + } + else + { + items.push_back (ptr_item); + } + } + + SAFE_DELETE_ARRAY (serviceSid); + } + + SAFE_DELETE_ARRAY (pServices); + } + } + } + + CloseServiceHandle (hsvcmgr); + } +} + +void _app_generate_rulesmenu (HMENU hsubmenu, size_t app_hash) +{ + static HBITMAP hbmp_allow = nullptr; + static HBITMAP hbmp_block = nullptr; + + static HBITMAP hbmp_checked = nullptr; + static HBITMAP hbmp_unchecked = nullptr; + + if (!hbmp_allow) + hbmp_allow = _app_ico2bmp (app.GetSharedIcon (app.GetHINSTANCE (), IDI_ALLOW, GetSystemMetrics (SM_CXSMICON))); + + if (!hbmp_block) + hbmp_block = _app_ico2bmp (app.GetSharedIcon (app.GetHINSTANCE (), IDI_BLOCK, GetSystemMetrics (SM_CXSMICON))); + + if (!hbmp_checked) + hbmp_checked = _app_ico2bmp (app.GetSharedIcon (app.GetHINSTANCE (), IDI_CHECKED, GetSystemMetrics (SM_CXSMICON))); + + if (!hbmp_unchecked) + hbmp_unchecked = _app_ico2bmp (app.GetSharedIcon (app.GetHINSTANCE (), IDI_UNCHECKED, GetSystemMetrics (SM_CXSMICON))); + + if (!_app_isrulesexists (TypeCustom, -1, -1)) + { + AppendMenu (hsubmenu, MF_SEPARATOR, 0, nullptr); + AppendMenu (hsubmenu, MF_STRING, IDX_RULES_SPECIAL, app.LocaleString (IDS_STATUS_EMPTY, nullptr)); + + EnableMenuItem (hsubmenu, IDX_RULES_SPECIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + } + else + { + for (UINT8 type = 0; type < 3; type++) + { + if (type == 0) + { + if (!_app_isrulesexists (TypeCustom, true, false)) + continue; + } + else if (type == 1) + { + if (!_app_isrulesexists (TypeCustom, false, false)) + continue; + } + else if (type == 2) + { + if (!_app_isrulesexists (TypeCustom, false, true)) + continue; + } + + AppendMenu (hsubmenu, MF_SEPARATOR, 0, nullptr); + + for (UINT8 loop = 0; loop < 2; loop++) + { + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE const ptr_rule = rules_arr.at (i); + + if (ptr_rule) + { + const bool is_global = (ptr_rule->is_enabled && ptr_rule->apps.empty ()); + const bool is_checked = is_global || (ptr_rule->is_enabled && (ptr_rule->apps.find (app_hash) != ptr_rule->apps.end ())); + + if (ptr_rule->type != TypeCustom || (type == 0 && (!ptr_rule->is_readonly || is_global)) || (type == 1 && (ptr_rule->is_readonly || is_global)) || (type == 2 && !is_global)) + continue; + + if ((loop == 0 && !is_checked) || (loop == 1 && is_checked)) + continue; + + WCHAR buffer[128] = {0}; + StringCchPrintf (buffer, _countof (buffer), app.LocaleString (IDS_RULE_APPLY_2, ptr_rule->is_readonly ? L" [*]" : nullptr), ptr_rule->pname); + + MENUITEMINFO mii = {0}; + + mii.cbSize = sizeof (mii); + mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STATE | MIIM_STRING | MIIM_BITMAP | MIIM_CHECKMARKS; + mii.fType = MFT_STRING; + mii.dwTypeData = buffer; + mii.hbmpItem = ptr_rule->is_block ? hbmp_block : hbmp_allow; + mii.hbmpChecked = hbmp_checked; + mii.hbmpUnchecked = hbmp_unchecked; + mii.fState = (is_checked ? MF_CHECKED : MF_UNCHECKED); + mii.wID = IDX_RULES_SPECIAL + UINT (i); + + if (is_global) + mii.fState |= MF_DISABLED | MF_GRAYED; + + InsertMenuItem (hsubmenu, mii.wID, FALSE, &mii); + } + } + } + } + } + + AppendMenu (hsubmenu, MF_SEPARATOR, 0, nullptr); + AppendMenu (hsubmenu, MF_STRING, IDM_EDITRULES, app.LocaleString (IDS_EDITRULES, L"...")); + AppendMenu (hsubmenu, MF_STRING, IDM_OPENRULESEDITOR, app.LocaleString (IDS_OPENRULESEDITOR, L"...")); +} + +bool _app_item_get (EnumAppType type, size_t hash, rstring * display_name, rstring * real_path, PSID * lpsid, PSECURITY_DESCRIPTOR * lpsd, rstring * /*description*/) +{ + for (size_t i = 0; i < items.size (); i++) + { + PITEM_ADD ptr_item = items.at (i); + + if ( + !ptr_item || + ptr_item->type != type || + ptr_item->hash != hash + ) + { + continue; + } + + if (display_name) + { + if (ptr_item->display_name && ptr_item->display_name[0]) + *display_name = ptr_item->display_name; + + else if (ptr_item->real_path && ptr_item->real_path[0]) + *display_name = ptr_item->real_path; + + else if (ptr_item->sid && ptr_item->sid[0]) + *display_name = ptr_item->sid; + } + + if (real_path) + { + if (ptr_item->real_path && ptr_item->real_path[0]) + *real_path = ptr_item->real_path; + } + + if (lpsid) + { + SAFE_DELETE_ARRAY (*lpsid); + + if (ptr_item->psid) + { + static const DWORD length = SECURITY_MAX_SID_SIZE; + + *lpsid = new BYTE[length]; + + CopyMemory (*lpsid, ptr_item->psid, length); + } + } + + if (lpsd) + { + SAFE_DELETE_ARRAY (*lpsd); + + if (ptr_item->psd) + { + const DWORD length = GetSecurityDescriptorLength (ptr_item->psd); + + if (length) + { + *lpsd = new BYTE[length]; + + CopyMemory (*lpsd, ptr_item->psd, length); + } + } + } + + //if (description) + // *description = pvec->at (i).pdesc; + + return true; + } + + return false; +} + +INT CALLBACK _app_listviewcompare_abc (LPARAM item1, LPARAM item2, LPARAM lparam) +{ + HWND hwnd = GetParent ((HWND)lparam); + const UINT ctrl_id = GetDlgCtrlID ((HWND)lparam); + + const bool is_checked1 = _r_listview_isitemchecked (hwnd, ctrl_id, (size_t)item1); + const bool is_checked2 = _r_listview_isitemchecked (hwnd, ctrl_id, (size_t)item2); + + if ((is_checked1 || is_checked2) && (is_checked1 != is_checked2)) + { + if (is_checked1 && !is_checked2) + return -1; + + if (!is_checked1 && is_checked2) + return 1; + } + + const rstring str1 = _r_listview_getitemtext (hwnd, ctrl_id, (size_t)item1, 0); + const rstring str2 = _r_listview_getitemtext (hwnd, ctrl_id, (size_t)item2, 0); + + return StrCmpLogicalW (str1, str2); +} + +INT CALLBACK _app_listviewcompare_apps (LPARAM lp1, LPARAM lp2, LPARAM lparam) +{ + const UINT column_id = LOWORD (lparam); + const BOOL is_descend = HIWORD (lparam); + + const size_t hash1 = static_cast(lp1); + const size_t hash2 = static_cast(lp2); + + INT result = 0; + + PITEM_APP const ptr_app1 = _app_getapplication (hash1); + PITEM_APP const ptr_app2 = _app_getapplication (hash2); + + if (!ptr_app1 || !ptr_app2) + return 0; + + if (column_id == 0) + { + // file + result = StrCmpLogicalW (ptr_app1->display_name, ptr_app2->display_name); + } + else if (column_id == 1) + { + // timestamp + if (ptr_app1->timestamp == ptr_app2->timestamp) + result = 0; + + else if (ptr_app1->timestamp < ptr_app2->timestamp) + result = -1; + + else if (ptr_app1->timestamp > ptr_app2->timestamp) + result = 1; + } + + return is_descend ? -result : result; +} + +INT CALLBACK _app_listviewcompare_rules (LPARAM item1, LPARAM item2, LPARAM lparam) +{ + const UINT listview_id = (UINT)lparam; + + const UINT column_id = app.ConfigGet (L"SortColumnRules", 0).AsUint (); + const bool is_descend = app.ConfigGet (L"IsSortDescendingRules", false).AsBool (); + + const rstring str1 = _r_listview_getitemtext (app.GetHWND (), listview_id, (size_t)item1, column_id); + const rstring str2 = _r_listview_getitemtext (app.GetHWND (), listview_id, (size_t)item2, column_id); + + const INT result = StrCmpLogicalW (str1, str2); + + return is_descend ? -result : result; +} + +void _app_listviewsort (HWND hwnd, UINT ctrl_id, INT subitem, bool is_notifycode) +{ + _r_fastlock_acquireshared (&lock_access); + + for (INT i = 0; i < _r_listview_getcolumncount (hwnd, ctrl_id); i++) + _r_listview_setcolumnsortindex (hwnd, ctrl_id, i, 0); + + if ( + ctrl_id == IDC_APPS_PROFILE || + ctrl_id == IDC_APPS_SERVICE || + ctrl_id == IDC_APPS_PACKAGE + ) + { + bool is_descend = app.ConfigGet (L"IsSortDescending", true).AsBool (); + + if (is_notifycode) + is_descend = !is_descend; + + if (subitem == -1) + subitem = app.ConfigGet (L"SortColumn", 1).AsBool (); + + const WPARAM wparam = MAKEWPARAM (subitem, is_descend); + + if (is_notifycode) + { + app.ConfigSet (L"IsSortDescending", is_descend); + app.ConfigSet (L"SortColumn", (DWORD)subitem); + } + + _r_listview_setcolumnsortindex (hwnd, ctrl_id, subitem, is_descend ? -1 : 1); + + _r_fastlock_acquireshared (&lock_checkbox); + + for (size_t i = 0; i < _r_listview_getitemcount (hwnd, ctrl_id); i++) + { + const size_t hash = (size_t)_r_listview_getitemlparam (hwnd, ctrl_id, i); + PITEM_APP ptr_app = _app_getapplication (hash); + + if (ptr_app) + { + _r_listview_setitem (hwnd, ctrl_id, i, 0, nullptr, LAST_VALUE, _app_getappgroup (hash, ptr_app)); + _r_listview_setitemcheck (hwnd, ctrl_id, i, ptr_app->is_enabled); + } + } + + _r_fastlock_releaseshared (&lock_checkbox); + + SendDlgItemMessage (hwnd, ctrl_id, LVM_SORTITEMS, wparam, (LPARAM)& _app_listviewcompare_apps); + + _app_refreshstatus (hwnd); + } + else if ( + ctrl_id == IDC_RULES_BLOCKLIST || + ctrl_id == IDC_RULES_SYSTEM || + ctrl_id == IDC_RULES_CUSTOM + ) + { + bool is_descend = app.ConfigGet (L"IsSortDescendingRules", false).AsBool (); + + if (is_notifycode) + is_descend = !is_descend; + + if (subitem == -1) + subitem = app.ConfigGet (L"SortColumnRules", 0).AsUint (); + + if (is_notifycode) + { + app.ConfigSet (L"IsSortDescendingRules", is_descend); + app.ConfigSet (L"SortColumnRules", (DWORD)subitem); + } + + _r_listview_setcolumnsortindex (hwnd, ctrl_id, subitem, is_descend ? -1 : 1); + + SendDlgItemMessage (hwnd, ctrl_id, LVM_SORTITEMSEX, (WPARAM)ctrl_id, (LPARAM)& _app_listviewcompare_rules); + } + else + { + SendDlgItemMessage (hwnd, ctrl_id, LVM_SORTITEMSEX, (WPARAM)GetDlgItem (hwnd, ctrl_id), (LPARAM)& _app_listviewcompare_abc); + } + + _r_fastlock_releaseshared (&lock_access); +} + +void _app_refreshstatus (HWND hwnd) +{ + const HWND hstatus = GetDlgItem (hwnd, IDC_STATUSBAR); + const HDC hdc = GetDC (hstatus); + + const UINT listview_id = _app_gettab_id (hwnd); + + // item count + if (hdc) + { + SelectObject (hdc, (HFONT)SendMessage (hstatus, WM_GETFONT, 0, 0)); // fix + + static const UINT parts_count = 3; + + rstring text[parts_count]; + INT parts[parts_count] = {0}; + LONG size[parts_count] = {0}; + LONG lay = 0; + + _r_fastlock_acquireshared (&lock_access); + + ITEM_STATUS itemStat = {0}; + _app_getcount (&itemStat); + + _r_fastlock_releaseshared (&lock_access); + + for (UINT i = 0; i < parts_count; i++) + { + switch (i) + { + case 0: + { + text[i].Format (app.LocaleString (IDS_STATUS_TOTAL, nullptr), itemStat.total_count); + break; + } + + case 1: + { + text[i].Format (L"%s: %d", app.LocaleString (IDS_STATUS_UNUSED_APPS, nullptr).GetString (), itemStat.unused_count); + break; + } + + case 2: + { + text[i].Format (L"%s: %d", app.LocaleString (IDS_STATUS_TIMER_APPS, nullptr).GetString (), itemStat.timers_count); + break; + } + } + + size[i] = _r_dc_fontwidth (hdc, text[i], text[i].GetLength ()) + 10; + + if (i) + lay += size[i]; + } + + RECT rc = {0}; + GetClientRect (hstatus, &rc); + + parts[0] = _R_RECT_WIDTH (&rc) - lay - GetSystemMetrics (SM_CXSMICON); + parts[1] = parts[0] + size[1]; + parts[2] = parts[1] + size[2]; + + SendMessage (hstatus, SB_SETPARTS, parts_count, (LPARAM)parts); + + for (UINT i = 0; i < parts_count; i++) + SendMessage (hstatus, SB_SETTEXT, MAKEWPARAM (i, 0), (LPARAM)text[i].GetString ()); + + ReleaseDC (hstatus, hdc); + } + + // group information + { + size_t group1_count = 0; + size_t group2_count = 0; + size_t group3_count = 0; + + if ( + listview_id == IDC_APPS_PROFILE || + listview_id == IDC_APPS_SERVICE || + listview_id == IDC_APPS_PACKAGE + ) + { + const size_t total_count = _r_listview_getitemcount (hwnd, listview_id); + + for (size_t i = 0; i < total_count; i++) + { + const size_t hash = _r_listview_getitemlparam (hwnd, listview_id, i); + const PITEM_APP ptr_app = _app_getapplication (hash); + + if (ptr_app) + { + const size_t group_id = _app_getappgroup (hash, ptr_app); + + if (group_id == 0) + group1_count += 1; + + else if (group_id == 1) + group2_count += 1; + + else + group3_count += 1; + } + } + + const bool is_whitelist = (app.ConfigGet (L"Mode", ModeWhitelist).AsUint () == ModeWhitelist); + + _r_listview_setgroup (hwnd, listview_id, 0, app.LocaleString (is_whitelist ? IDS_GROUP_ALLOWED : IDS_GROUP_BLOCKED, _r_fmt (L" (%d/%d)", group1_count, total_count)), 0, 0); + _r_listview_setgroup (hwnd, listview_id, 1, app.LocaleString (IDS_GROUP_SPECIAL_APPS, _r_fmt (L" (%d/%d)", group2_count, total_count)), 0, 0); + _r_listview_setgroup (hwnd, listview_id, 2, app.LocaleString (is_whitelist ? IDS_GROUP_BLOCKED : IDS_GROUP_ALLOWED, _r_fmt (L" (%d/%d)", group3_count, total_count)), 0, 0); + } + else if ( + listview_id == IDC_RULES_BLOCKLIST || + listview_id == IDC_RULES_SYSTEM || + listview_id == IDC_RULES_CUSTOM + ) + { + const size_t total_count = _r_listview_getitemcount (hwnd, listview_id); + + for (size_t i = 0; i < total_count; i++) + { + const size_t idx = _r_listview_getitemlparam (hwnd, listview_id, i); + const PITEM_RULE ptr_rule = rules_arr.at (idx); + + if (ptr_rule) + { + const size_t group_id = _app_getrulegroup (ptr_rule); + + if (group_id == 0) + group1_count += 1; + + else if (group_id == 1) + group2_count += 1; + + else + group3_count += 1; + } + } + + _r_listview_setgroup (hwnd, listview_id, 0, app.LocaleString (IDS_GROUP_ENABLED, _r_fmt (L" (%d/%d)", group1_count, total_count)), 0, 0); + _r_listview_setgroup (hwnd, listview_id, 1, app.LocaleString (IDS_GROUP_SPECIAL, _r_fmt (L" (%d/%d)", group2_count, total_count)), 0, 0); + _r_listview_setgroup (hwnd, listview_id, 2, app.LocaleString (IDS_GROUP_DISABLED, _r_fmt (L" (%d/%d)", group3_count, total_count)), 0, 0); + } + } +} + +rstring _app_parsehostaddress_dns (LPCWSTR host, USHORT port) +{ + rstring result; + + PDNS_RECORD ppQueryResultsSet = nullptr; + PIP4_ARRAY pSrvList = nullptr; + + // use custom dns-server (if present) + WCHAR dnsServer[INET_ADDRSTRLEN] = {0}; + StringCchCopy (dnsServer, _countof (dnsServer), app.ConfigGet (L"DnsServerV4", nullptr)); // ipv4 dns-server address + + if (dnsServer[0]) + { + pSrvList = new IP4_ARRAY; + + if (pSrvList) + { + if (InetPton (AF_INET, dnsServer, &(pSrvList->AddrArray[0]))) + { + pSrvList->AddrCount = 1; + } + else + { + _app_logerror (L"InetPton", WSAGetLastError (), dnsServer, true); + SAFE_DELETE (pSrvList); + } + } + } + + const DNS_STATUS dnsStatus = DnsQuery (host, DNS_TYPE_ALL, DNS_QUERY_NO_HOSTS_FILE | DNS_QUERY_DNSSEC_CHECKING_DISABLED, pSrvList, &ppQueryResultsSet, nullptr); + + if (dnsStatus != ERROR_SUCCESS) + { + _app_logerror (L"DnsQuery", dnsStatus, host, true); + } + else + { + for (auto current = ppQueryResultsSet; current != nullptr; current = current->pNext) + { + if (current->wType == DNS_TYPE_A) + { + // ipv4 address + WCHAR str[INET_ADDRSTRLEN] = {0}; + InetNtop (AF_INET, &(current->Data.A.IpAddress), str, _countof (str)); + + result.Append (str); + + if (port) + result.AppendFormat (L":%d", port); + + result.Append (RULE_DELIMETER); + } + else if (current->wType == DNS_TYPE_AAAA) + { + // ipv6 address + WCHAR str[INET6_ADDRSTRLEN] = {0}; + InetNtop (AF_INET6, &(current->Data.AAAA.Ip6Address), str, _countof (str)); + + result.Append (str); + + if (port) + result.AppendFormat (L":%d", port); + + result.Append (RULE_DELIMETER); + } + else if (current->wType == DNS_TYPE_CNAME) + { + // canonical name + if (current->Data.CNAME.pNameHost) + { + result = _app_parsehostaddress_dns (current->Data.CNAME.pNameHost, port); + break; + } + } + } + + result.Trim (RULE_DELIMETER); + + DnsRecordListFree (ppQueryResultsSet, DnsFreeRecordList); + } + + SAFE_DELETE (pSrvList); + + return result; +} + +rstring _app_parsehostaddress_wsa (LPCWSTR hostname, USHORT port) +{ + rstring result; + + if (!config.is_wsainit || !hostname || !hostname[0] || !app.ConfigGet (L"IsEnableWsaResolver", true).AsBool ()) + return L""; + + ADDRINFOEXW hints = {0}; + ADDRINFOEXW * ppQueryResultsSet = nullptr; + + hints.ai_family = AF_UNSPEC; + //hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + LPGUID lpNspid = nullptr; + const INT code = GetAddrInfoEx (hostname, L"domain", NS_DNS, lpNspid, &hints, &ppQueryResultsSet, nullptr, nullptr, nullptr, nullptr); + + if (code != ERROR_SUCCESS || !ppQueryResultsSet) + { + _app_logerror (L"GetAddrInfoEx", code, hostname, false); + } + else + { + for (auto current = ppQueryResultsSet; current != nullptr; current = current->ai_next) + { + WCHAR printableIP[INET6_ADDRSTRLEN] = {0}; + + if (current->ai_family == AF_INET) + { + struct sockaddr_in *sock_in4 = (struct sockaddr_in *)current->ai_addr; + PIN_ADDR addr4 = &(sock_in4->sin_addr); + + if (IN4_IS_ADDR_UNSPECIFIED (addr4) || IN4_IS_ADDR_LOOPBACK (addr4)) + continue; + + InetNtop (current->ai_family, addr4, printableIP, _countof (printableIP)); + } + else if (current->ai_family == AF_INET6) + { + struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)current->ai_addr; + PIN6_ADDR addr6 = &(sock_in6->sin6_addr); + + if (IN6_IS_ADDR_UNSPECIFIED (addr6) || IN6_IS_ADDR_LOOPBACK (addr6)) + continue; + + InetNtop (current->ai_family, addr6, printableIP, _countof (printableIP)); + } + + if (!printableIP[0]) + continue; + + result.Append (printableIP); + + if (port) + result.AppendFormat (L":%d", port); + + result.Append (RULE_DELIMETER); + } + + result.Trim (RULE_DELIMETER); + } + + if (ppQueryResultsSet) + FreeAddrInfoExW (ppQueryResultsSet); + + return result; +} + +bool _app_parsenetworkstring (LPCWSTR network_string, NET_ADDRESS_FORMAT * format_ptr, USHORT * port_ptr, FWP_V4_ADDR_AND_MASK * paddr4, FWP_V6_ADDR_AND_MASK * paddr6, LPWSTR paddr_dns, size_t dns_length) +{ + NET_ADDRESS_INFO ni; + SecureZeroMemory (&ni, sizeof (ni)); + + USHORT port = 0; + BYTE prefix_length = 0; + + static const DWORD types = NET_STRING_ANY_ADDRESS | NET_STRING_ANY_SERVICE | NET_STRING_IP_NETWORK | NET_STRING_ANY_ADDRESS_NO_SCOPE | NET_STRING_ANY_SERVICE_NO_SCOPE; + const DWORD errcode = ParseNetworkString (network_string, types, &ni, &port, &prefix_length); + + if (errcode != ERROR_SUCCESS) + { + _app_logerror (L"ParseNetworkString", errcode, network_string, true); + return false; + } + else + { + if (format_ptr) + *format_ptr = ni.Format; + + if (port_ptr) + *port_ptr = port; + + if (ni.Format == NET_ADDRESS_IPV4) + { + if (paddr4) + { + ULONG mask = 0; + ConvertLengthToIpv4Mask (prefix_length, &mask); + + paddr4->addr = ntohl (ni.Ipv4Address.sin_addr.S_un.S_addr); + paddr4->mask = ntohl (mask); + } + + return true; + } + else if (ni.Format == NET_ADDRESS_IPV6) + { + if (paddr6) + { + CopyMemory (paddr6->addr, ni.Ipv6Address.sin6_addr.u.Byte, FWP_V6_ADDR_SIZE); + paddr6->prefixLength = min (prefix_length, 128); + } + + return true; + } + else if (ni.Format == NET_ADDRESS_DNS_NAME) + { + if (paddr_dns) + { + const size_t hash = _r_str_hash (ni.NamedAddress.Address); + + _r_fastlock_acquireshared (&lock_cache); + const bool is_exists = cache_dns.find (hash) != cache_dns.end (); + _r_fastlock_releaseshared (&lock_cache); + + if (is_exists) + { + _r_fastlock_acquireshared (&lock_cache); + StringCchCopy (paddr_dns, dns_length, cache_dns[hash]); + _r_fastlock_releaseshared (&lock_cache); + + return *paddr_dns; + } + + rstring host = _app_parsehostaddress_dns (ni.NamedAddress.Address, port); + + if (host.IsEmpty ()) + host = _app_parsehostaddress_wsa (ni.NamedAddress.Address, port); + + if (host.IsEmpty ()) + { + return false; + } + else + { + _r_fastlock_acquireexclusive (&lock_cache); + + _app_freecache (&cache_dns); + _r_str_alloc (&cache_dns[hash], host.GetLength (), host); + + StringCchCopy (paddr_dns, dns_length, host); + + _r_fastlock_releaseexclusive (&lock_cache); + + return true; + } + } + + return true; + } + } + + return false; +} + +bool _app_parserulestring (rstring rule, PITEM_ADDRESS ptr_addr) +{ + rule.Trim (L"\r\n "); // trim whitespace + + if (rule.IsEmpty ()) + return true; + + if (rule.At (0) == L'*') + return true; + + EnumRuleItemType type = TypeRuleItemUnknown; + const size_t range_pos = rule.Find (RULE_RANGE_CHAR); + bool is_range = (range_pos != rstring::npos); + + WCHAR range_start[LEN_IP_MAX] = {0}; + WCHAR range_end[LEN_IP_MAX] = {0}; + + if (is_range) + { + StringCchCopy (range_start, _countof (range_start), rule.Midded (0, range_pos)); + StringCchCopy (range_end, _countof (range_end), rule.Midded (range_pos + 1)); + } + + // auto-parse rule type + { + const size_t hash = rule.Hash (); + + _r_fastlock_acquireshared (&lock_cache); + const bool is_exists = cache_types.find (hash) != cache_types.end (); + _r_fastlock_releaseshared (&lock_cache); + + if (is_exists) + { + _r_fastlock_acquireshared (&lock_cache); + + type = cache_types[hash]; + + _r_fastlock_releaseshared (&lock_cache); + } + else + { + if (_app_isruleport (rule)) + { + type = TypePort; + } + else if (is_range ? (_app_isruleip (range_start) && _app_isruleip (range_end)) : _app_isruleip (rule)) + { + type = TypeIp; + } + else if (_app_isrulehost (rule)) + { + type = TypeHost; + } + + if (type != TypeRuleItemUnknown) + { + _r_fastlock_acquireexclusive (&lock_cache); + + if (cache_types.size () >= UMAP_CACHE_LIMIT) + cache_types.clear (); + + cache_types[hash] = type; + + _r_fastlock_releaseexclusive (&lock_cache); + } + } + } + + if (type == TypeRuleItemUnknown) + return false; + + if (!ptr_addr) + return true; + + if (type == TypeHost) + is_range = false; + + ptr_addr->is_range = is_range; + + if (type == TypePort) + { + if (!is_range) + { + // ...port + ptr_addr->type = TypePort; + ptr_addr->port = (UINT16)rule.AsUint (); + + return true; + } + else + { + // ...port range + ptr_addr->type = TypePort; + + if (ptr_addr->prange) + { + ptr_addr->prange->valueLow.type = FWP_UINT16; + ptr_addr->prange->valueLow.uint16 = (UINT16)wcstoul (range_start, nullptr, 10); + + ptr_addr->prange->valueHigh.type = FWP_UINT16; + ptr_addr->prange->valueHigh.uint16 = (UINT16)wcstoul (range_end, nullptr, 10); + } + + return true; + } + } + else + { + NET_ADDRESS_FORMAT format; + + FWP_V4_ADDR_AND_MASK addr4 = {0}; + FWP_V6_ADDR_AND_MASK addr6 = {0}; + + USHORT port2 = 0; + + if (type == TypeIp && is_range) + { + // ...ip range (start) + if (_app_parsenetworkstring (range_start, &format, &port2, &addr4, &addr6, nullptr, 0)) + { + if (format == NET_ADDRESS_IPV4) + { + if (ptr_addr->prange) + { + ptr_addr->prange->valueLow.type = FWP_UINT32; + ptr_addr->prange->valueLow.uint32 = addr4.addr; + } + } + else if (format == NET_ADDRESS_IPV6) + { + if (ptr_addr->prange) + { + ptr_addr->prange->valueLow.type = FWP_BYTE_ARRAY16_TYPE; + CopyMemory (ptr_addr->prange->valueLow.byteArray16->byteArray16, addr6.addr, FWP_V6_ADDR_SIZE); + } + } + else + { + return false; + } + + if (port2 && !ptr_addr->port) + ptr_addr->port = port2; + } + else + { + return false; + } + + // ...ip range (end) + if (_app_parsenetworkstring (range_end, &format, &port2, &addr4, &addr6, nullptr, 0)) + { + if (format == NET_ADDRESS_IPV4) + { + if (ptr_addr->prange) + { + ptr_addr->prange->valueHigh.type = FWP_UINT32; + ptr_addr->prange->valueHigh.uint32 = addr4.addr; + } + } + else if (format == NET_ADDRESS_IPV6) + { + if (ptr_addr->prange) + { + ptr_addr->prange->valueHigh.type = FWP_BYTE_ARRAY16_TYPE; + CopyMemory (ptr_addr->prange->valueHigh.byteArray16->byteArray16, addr6.addr, FWP_V6_ADDR_SIZE); + } + } + else + { + return false; + } + } + else + { + return false; + } + + ptr_addr->format = format; + ptr_addr->type = TypeIp; + } + else + { + // ...ip/host + if (_app_parsenetworkstring (rule, &format, &port2, &addr4, &addr6, ptr_addr->host, _countof (ptr_addr->host))) + { + if (format == NET_ADDRESS_IPV4) + { + if (ptr_addr->paddr4) + { + ptr_addr->paddr4->mask = addr4.mask; + ptr_addr->paddr4->addr = addr4.addr; + } + } + else if (format == NET_ADDRESS_IPV6) + { + if (ptr_addr->paddr6) + { + ptr_addr->paddr6->prefixLength = addr6.prefixLength; + CopyMemory (ptr_addr->paddr6->addr, addr6.addr, FWP_V6_ADDR_SIZE); + } + } + else if (format == NET_ADDRESS_DNS_NAME) + { + // ptr_addr->host = ; + } + else + { + return false; + } + + ptr_addr->type = TypeIp; + ptr_addr->format = format; + + if (port2) + ptr_addr->port = port2; + + return true; + } + else + { + return false; + } + } + } + + return true; +} + +bool _app_resolveaddress (ADDRESS_FAMILY af, LPVOID paddr, LPWSTR buffer, DWORD length) +{ + SOCKADDR_IN ipv4Address = {0}; + SOCKADDR_IN6 ipv6Address = {0}; + PSOCKADDR psock = {0}; + socklen_t size = 0; + + if (af == AF_INET) + { + ipv4Address.sin_family = af; + ipv4Address.sin_addr = *PIN_ADDR (paddr); + + psock = (PSOCKADDR)& ipv4Address; + size = sizeof (ipv4Address); + } + else if (af == AF_INET6) + { + ipv6Address.sin6_family = af; + ipv6Address.sin6_addr = *PIN6_ADDR (paddr); + + psock = (PSOCKADDR)& ipv6Address; + size = sizeof (ipv6Address); + } + else + { + return false; + } + + if (GetNameInfo (psock, size, buffer, length, nullptr, 0, NI_NAMEREQD) != 0) + return false; + + return true; +} + +void _app_resolvefilename (rstring & path) +{ + // "\??\" refers to \GLOBAL??\. Just remove it. + if (_wcsnicmp (path, L"\\??\\", 4) == 0) + { + path.Mid (4); + } + // "\SystemRoot" means "C:\Windows". + else if (_wcsnicmp (path, L"\\SystemRoot", 11) == 0) + { + WCHAR systemRoot[MAX_PATH] = {0}; + GetSystemDirectory (systemRoot, _countof (systemRoot)); + + path.Mid (11 + 9); + path.Insert (0, systemRoot); + } + // "system32\" means "C:\Windows\system32\". + else if (_wcsnicmp (path, L"system32\\", 9) == 0) + { + WCHAR systemRoot[MAX_PATH] = {0}; + GetSystemDirectory (systemRoot, _countof (systemRoot)); + + path.Mid (8); + path.Insert (0, systemRoot); + } +} + +void _app_showitem (HWND hwnd, UINT ctrl_id, size_t item, INT scroll_pos) +{ + if (item != LAST_VALUE) + { + if (scroll_pos == -1) + SendDlgItemMessage (hwnd, ctrl_id, LVM_ENSUREVISIBLE, item, TRUE); // ensure item visible + + ListView_SetItemState (GetDlgItem (hwnd, ctrl_id), -1, 0, LVIS_SELECTED | LVIS_FOCUSED); // deselect all + ListView_SetItemState (GetDlgItem (hwnd, ctrl_id), item, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); // select item + } + + if (scroll_pos != -1) + SendDlgItemMessage (hwnd, ctrl_id, LVM_SCROLL, 0, scroll_pos); // restore vscroll position +} + +HBITMAP _app_ico2bmp (HICON hicon) +{ + if (!hicon) + return nullptr; + + const INT icon_size = GetSystemMetrics (SM_CXSMICON); + + RECT iconRectangle = {0}; + + iconRectangle.right = icon_size; + iconRectangle.bottom = icon_size; + + HBITMAP hbitmap = nullptr; + const HDC screenHdc = GetDC (nullptr); + + if (screenHdc) + { + const HDC hdc = CreateCompatibleDC (screenHdc); + + if (hdc) + { + BITMAPINFO bitmapInfo = {0}; + bitmapInfo.bmiHeader.biSize = sizeof (bitmapInfo); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + bitmapInfo.bmiHeader.biWidth = icon_size; + bitmapInfo.bmiHeader.biHeight = icon_size; + bitmapInfo.bmiHeader.biBitCount = 32; + + hbitmap = CreateDIBSection (hdc, &bitmapInfo, DIB_RGB_COLORS, nullptr, nullptr, 0); + + if (hbitmap) + { + const HBITMAP oldBitmap = (HBITMAP)SelectObject (hdc, hbitmap); + + BLENDFUNCTION blendFunction = {0}; + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.AlphaFormat = AC_SRC_ALPHA; + blendFunction.SourceConstantAlpha = 255; + + BP_PAINTPARAMS paintParams = {0}; + paintParams.cbSize = sizeof (paintParams); + paintParams.dwFlags = BPPF_ERASE; + paintParams.pBlendFunction = &blendFunction; + + HDC bufferHdc = nullptr; + + const HPAINTBUFFER paintBuffer = BeginBufferedPaint (hdc, &iconRectangle, BPBF_DIB, &paintParams, &bufferHdc); + + if (paintBuffer) + { + DrawIconEx (bufferHdc, 0, 0, hicon, icon_size, icon_size, 0, nullptr, DI_NORMAL); + EndBufferedPaint (paintBuffer, TRUE); + } + else + { + _r_dc_fillrect (hdc, &iconRectangle, GetSysColor (COLOR_MENU)); + DrawIconEx (hdc, 0, 0, hicon, icon_size, icon_size, 0, nullptr, DI_NORMAL); + } + + SelectObject (hdc, oldBitmap); + } + + DeleteDC (hdc); + } + + ReleaseDC (nullptr, screenHdc); + } + + return hbitmap; +} + +void _app_load_appxmanifest (PITEM_ADD ptr_item) +{ + if (!ptr_item || !ptr_item->real_path) + return; + + rstring result; + rstring path; + + static LPCWSTR appx_names[] = { + L"AppxManifest.xml", + L"VSAppxManifest.xml", + }; + + for (size_t i = 0; i < _countof (appx_names); i++) + { + path.Format (L"%s\\%s", ptr_item->real_path, appx_names[i]); + + if (_r_fs_exists (path)) + goto doopen; + } + + return; + +doopen: + + pugi::xml_document doc; + pugi::xml_parse_result xml_result = doc.load_file (path, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + if (xml_result) + { + pugi::xml_node root = doc.child (L"Package"); + + if (root) + { + pugi::xml_node xml_applications = root.child (L"Applications"); + + for (pugi::xml_node item = xml_applications.child (L"Application"); item; item = item.next_sibling (L"Application")) + { + if (!item.attribute (L"Executable").empty ()) + { + result.Format (L"%s\\%s", ptr_item->real_path, item.attribute (L"Executable").as_string ()); + + if (_r_fs_exists (result)) + break; + } + } + } + } + + _r_str_alloc (&ptr_item->real_path, result.GetLength (), result.GetString ()); +} + +LPVOID _app_loadresource (LPCWSTR res, PDWORD size) +{ + const HINSTANCE hinst = app.GetHINSTANCE (); + + HRSRC hres = FindResource (hinst, res, RT_RCDATA); + + if (hres) + { + HGLOBAL hloaded = LoadResource (hinst, hres); + + if (hloaded) + { + LPVOID pLockedResource = LockResource (hloaded); + + if (pLockedResource) + { + DWORD dwResourceSize = SizeofResource (hinst, hres); + + if (dwResourceSize != 0) + { + if (size) + *size = dwResourceSize; + + return pLockedResource; + } + } + } + } + + return nullptr; +} diff --git a/src/helper.hpp b/src/helper.hpp new file mode 100644 index 00000000..9f2405dd --- /dev/null +++ b/src/helper.hpp @@ -0,0 +1,54 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +UINT _app_gettab_id (HWND hwnd, size_t page_id = LAST_VALUE); +void _app_settab_id (HWND hwnd, size_t page_id); + +void _app_applycasestyle (LPWSTR buffer, size_t length); + +bool _app_formataddress (PITEM_LOG const ptr_log, FWP_DIRECTION dir, UINT16 port, LPWSTR* ptr_dest, bool is_appenddns); + +void _app_freearray (std::vector* ptr); +void _app_freecache (MCACHE_MAP* ptr_map); +void _app_freethreadpool (MTHREADPOOL* ptr_pool); +void _app_freelogstack (); +void _app_getappicon (ITEM_APP const *ptr_app, bool is_small, size_t* picon_id, HICON* picon); +void _app_getdisplayname (size_t hash, ITEM_APP const *ptr_app, LPWSTR* extracted_name); +bool _app_getfileicon (LPCWSTR path, bool is_small, size_t* picon_id, HICON* picon); +rstring _app_getshortcutpath (HWND hwnd, LPCWSTR path); +bool _app_getsignatureinfo (size_t hash, LPCWSTR path, LPWSTR* psigner); +bool _app_getversioninfo (size_t hash, LPCWSTR path, LPWSTR* pinfo); +size_t _app_getposition (HWND hwnd, size_t hash); +rstring _app_getprotoname (UINT8 proto); + +void _app_generate_packages (); +void _app_generate_services (); + +void _app_generate_rulesmenu (HMENU hsubmenu, size_t app_hash); + +bool _app_item_get (EnumAppType type, size_t hash, rstring* display_name, rstring* real_path, PSID* lpsid, PSECURITY_DESCRIPTOR* lpsd, rstring* /*description*/); + +INT CALLBACK _app_listviewcompare_abc (LPARAM item1, LPARAM item2, LPARAM lparam); +INT CALLBACK _app_listviewcompare_apps (LPARAM lp1, LPARAM lp2, LPARAM lparam); +INT CALLBACK _app_listviewcompare_rules (LPARAM item1, LPARAM item2, LPARAM lparam); +void _app_listviewsort (HWND hwnd, UINT ctrl_id, INT subitem, bool is_notifycode); + +void _app_refreshstatus (HWND hwnd); + +rstring _app_parsehostaddress_dns (LPCWSTR host, USHORT port); +rstring _app_parsehostaddress_wsa (LPCWSTR hostname, USHORT port); + +bool _app_parsenetworkstring (LPCWSTR network_string, NET_ADDRESS_FORMAT* format_ptr, USHORT* port_ptr, FWP_V4_ADDR_AND_MASK* paddr4, FWP_V6_ADDR_AND_MASK* paddr6, LPWSTR paddr_dns, size_t dns_length); +bool _app_parserulestring (rstring rule, PITEM_ADDRESS ptr_addr); + +bool _app_resolveaddress (ADDRESS_FAMILY af, LPVOID paddr, LPWSTR buffer, DWORD length); +void _app_resolvefilename (rstring& path); + +void _app_showitem (HWND hwnd, UINT ctrl_id, size_t item, INT scroll_pos); + +HBITMAP _app_ico2bmp (HICON hicon); + +void _app_load_appxmanifest (PITEM_ADD ptr_item); +LPVOID _app_loadresource (LPCWSTR res, PDWORD size); diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 00000000..37f1ebd9 --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,898 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +void _app_logerror (LPCWSTR fn, DWORD errcode, LPCWSTR desc, bool is_nopopups) +{ + const time_t current_time = _r_unixtime_now (); + + _r_dbg (fn, errcode, desc); + + if ((current_time - app.ConfigGet (L"ErrorNotificationsTimestamp", 0).AsLonglong ()) >= app.ConfigGet (L"ErrorNotificationsPeriod", 4).AsLonglong () && !is_nopopups && app.ConfigGet (L"IsErrorNotificationsEnabled", true).AsBool ()) // check for timeout (sec.) + { + app.TrayPopup (app.GetHWND (), UID, nullptr, NIIF_ERROR | (app.ConfigGet (L"IsNotificationsSound", true).AsBool () ? 0 : NIIF_NOSOUND), APP_NAME, app.LocaleString (IDS_STATUS_ERROR, nullptr)); + app.ConfigSet (L"ErrorNotificationsTimestamp", current_time); + } +} + +rstring _app_getlogviewer () +{ + rstring result; + + static LPCWSTR csvviewer[] = { + L"CSVFileView.exe", + L"CSVFileView\\CSVFileView.exe", + L"..\\CSVFileView\\CSVFileView.exe" + }; + + for (size_t i = 0; i < _countof (csvviewer); i++) + { + result = _r_path_expand (csvviewer[i]); + + if (_r_fs_exists (result)) + return result; + } + + result = app.ConfigGet (L"LogViewer", L"notepad.exe"); + + return _r_path_expand (result); +} + +bool _app_logchecklimit () +{ + const DWORD limit = app.ConfigGet (L"LogSizeLimitKb", LOG_SIZE_LIMIT).AsUlong (); + + if (!limit || !config.hlogfile || config.hlogfile == INVALID_HANDLE_VALUE) + return false; + + if (_r_fs_size (config.hlogfile) >= (limit * _R_BYTESIZE_KB)) + { + _app_logclear (); + + return true; + } + + return false; +} + +bool _app_loginit (bool is_install) +{ + // dropped packets logging (win7+) + if (!config.hnetevent || !_r_sys_validversion (6, 1)) + return false; + + // reset all handles + _r_fastlock_acquireexclusive (&lock_writelog); + + if (config.hlogfile != nullptr && config.hlogfile != INVALID_HANDLE_VALUE) + { + CloseHandle (config.hlogfile); + config.hlogfile = nullptr; + } + + _r_fastlock_releaseexclusive (&lock_writelog); + + if (!is_install) + return true; // already closed + + // check if log enabled + if (!app.ConfigGet (L"IsLogEnabled", false).AsBool ()) + return false; + + bool result = false; + + const rstring path = _r_path_expand (app.ConfigGet (L"LogPath", LOG_PATH_DEFAULT)); + + _r_fastlock_acquireexclusive (&lock_writelog); + + config.hlogfile = CreateFile (path, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (config.hlogfile == INVALID_HANDLE_VALUE) + { + _app_logerror (L"CreateFile", GetLastError (), path, false); + } + else + { + if (GetLastError () != ERROR_ALREADY_EXISTS) + { + DWORD written = 0; + static const BYTE bom[] = {0xFF, 0xFE}; + + WriteFile (config.hlogfile, bom, sizeof (bom), &written, nullptr); // write utf-16 le byte order mask + } + else + { + _app_logchecklimit (); + + _r_fs_setpos (config.hlogfile, 0, FILE_END); + } + + result = true; + } + + _r_fastlock_releaseexclusive (&lock_writelog); + + return result; +} + +void _app_logwrite (PITEM_LOG const ptr_log) +{ + if (!ptr_log || !config.hlogfile || config.hlogfile == INVALID_HANDLE_VALUE) + return; + + // parse path + rstring path; + { + _r_fastlock_acquireshared (&lock_access); + + PITEM_APP const ptr_app = _app_getapplication (ptr_log->hash); + + if (ptr_app) + { + if (ptr_app->type == AppPackage || ptr_app->type == AppService) + { + if (ptr_app->real_path && ptr_app->real_path[0]) + path = ptr_app->real_path; + + else if (ptr_app->display_name && ptr_app->display_name[0]) + path = ptr_app->display_name; + } + else if (ptr_app->original_path && ptr_app->original_path[0]) + { + path = ptr_app->original_path; + } + } + + _r_fastlock_releaseshared (&lock_access); + } + + // parse filter name + rstring filter_name; + { + if ((ptr_log->provider_name && ptr_log->provider_name[0]) && (ptr_log->filter_name && ptr_log->filter_name[0])) + filter_name.Format (L"%s\\%s", ptr_log->provider_name, ptr_log->filter_name); + + else if (ptr_log->filter_name && ptr_log->filter_name[0]) + filter_name = ptr_log->filter_name; + } + + // parse direction + rstring direction; + { + if (ptr_log->direction == FWP_DIRECTION_INBOUND) + direction = SZ_LOG_DIRECTION_IN; + + else if (ptr_log->direction == FWP_DIRECTION_OUTBOUND) + direction = SZ_LOG_DIRECTION_OUT; + + if (ptr_log->is_loopback) + direction.Append (SZ_LOG_DIRECTION_LOOPBACK); + } + + rstring buffer; + buffer.Format (L"\"%s\"%c\"%s\"%c\"%s\"%c%s (" SZ_LOG_REMOTE_ADDRESS L")%c%s (" SZ_LOG_LOCAL_ADDRESS L")%c%s%c\"%s\"%c#%" PRIu64 L"%c%s%c%s\r\n", + _r_fmt_date (ptr_log->date, FDTF_SHORTDATE | FDTF_LONGTIME).GetString (), + LOG_DIV, + ptr_log->username ? ptr_log->username : SZ_EMPTY, + LOG_DIV, + path.IsEmpty () ? SZ_EMPTY : path.GetString (), + LOG_DIV, + ptr_log->remote_fmt ? ptr_log->remote_fmt : SZ_EMPTY, + LOG_DIV, + ptr_log->local_fmt ? ptr_log->local_fmt : SZ_EMPTY, + LOG_DIV, + _app_getprotoname (ptr_log->protocol).GetString (), + LOG_DIV, + filter_name.IsEmpty () ? SZ_EMPTY : filter_name.GetString (), + LOG_DIV, + ptr_log->filter_id, + LOG_DIV, + direction.IsEmpty () ? SZ_EMPTY : direction.GetString (), + LOG_DIV, + (ptr_log->is_allow ? SZ_LOG_ALLOW : SZ_LOG_BLOCK) + ); + + _r_fastlock_acquireexclusive (&lock_writelog); + + _app_logchecklimit (); + + DWORD written = 0; + WriteFile (config.hlogfile, buffer.GetString (), DWORD (buffer.GetLength () * sizeof (WCHAR)), &written, nullptr); + + _r_fastlock_releaseexclusive (&lock_writelog); +} + +void _app_logclear () +{ + const rstring path = _r_path_expand (app.ConfigGet (L"LogPath", LOG_PATH_DEFAULT)); + + _r_fastlock_acquireexclusive (&lock_writelog); + + if (config.hlogfile != nullptr && config.hlogfile != INVALID_HANDLE_VALUE) + { + _r_fs_setpos (config.hlogfile, 2, FILE_BEGIN); + + SetEndOfFile (config.hlogfile); + } + else + { + _r_fs_delete (path, false); + } + + _r_fs_delete (_r_fmt (L"%s.bak", path.GetString ()), false); + + _r_fastlock_releaseexclusive (&lock_writelog); +} + +bool _wfp_logsubscribe () +{ + if (!config.hengine) + return false; + + bool result = false; + + if (config.hnetevent) + { + result = true; + } + else + { + const HMODULE hlib = LoadLibrary (L"fwpuclnt.dll"); + + if (!hlib) + { + _app_logerror (L"LoadLibrary", GetLastError (), L"fwpuclnt.dll", false); + } + else + { + typedef DWORD (WINAPI *FWPMNES5) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK4, LPVOID, LPHANDLE); // win10new+ + typedef DWORD (WINAPI *FWPMNES4) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK4, LPVOID, LPHANDLE); // win10rs5+ + typedef DWORD (WINAPI *FWPMNES3) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK3, LPVOID, LPHANDLE); // win10rs4+ + typedef DWORD (WINAPI *FWPMNES2) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK2, LPVOID, LPHANDLE); // win10+ + typedef DWORD (WINAPI *FWPMNES1) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK1, LPVOID, LPHANDLE); // win8+ + typedef DWORD (WINAPI *FWPMNES0) (HANDLE, const FWPM_NET_EVENT_SUBSCRIPTION0*, FWPM_NET_EVENT_CALLBACK0, LPVOID, LPHANDLE); // win7+ + + const FWPMNES5 _FwpmNetEventSubscribe5 = (FWPMNES5)GetProcAddress (hlib, "FwpmNetEventSubscribe5"); // win10new+ + const FWPMNES4 _FwpmNetEventSubscribe4 = (FWPMNES4)GetProcAddress (hlib, "FwpmNetEventSubscribe4"); // win10rs5+ + const FWPMNES3 _FwpmNetEventSubscribe3 = (FWPMNES3)GetProcAddress (hlib, "FwpmNetEventSubscribe3"); // win10rs4+ + const FWPMNES2 _FwpmNetEventSubscribe2 = (FWPMNES2)GetProcAddress (hlib, "FwpmNetEventSubscribe2"); // win10+ + const FWPMNES1 _FwpmNetEventSubscribe1 = (FWPMNES1)GetProcAddress (hlib, "FwpmNetEventSubscribe1"); // win8+ + const FWPMNES0 _FwpmNetEventSubscribe0 = (FWPMNES0)GetProcAddress (hlib, "FwpmNetEventSubscribe0"); // win7+ + + if (!_FwpmNetEventSubscribe5 && !_FwpmNetEventSubscribe4 && !_FwpmNetEventSubscribe3 && !_FwpmNetEventSubscribe2 && !_FwpmNetEventSubscribe1 && !_FwpmNetEventSubscribe0) + { + _app_logerror (L"GetProcAddress", GetLastError (), L"FwpmNetEventSubscribe", false); + } + else + { + FWPM_NET_EVENT_SUBSCRIPTION subscription; + FWPM_NET_EVENT_ENUM_TEMPLATE enum_template; + + SecureZeroMemory (&subscription, sizeof (subscription)); + SecureZeroMemory (&enum_template, sizeof (enum_template)); + + if (config.psession) + CopyMemory (&subscription.sessionKey, config.psession, sizeof (GUID)); + + subscription.enumTemplate = &enum_template; + + DWORD rc = 0; + + if (_FwpmNetEventSubscribe5) + rc = _FwpmNetEventSubscribe5 (config.hengine, &subscription, &_wfp_logcallback4, nullptr, &config.hnetevent); // win10new+ + + else if (_FwpmNetEventSubscribe4) + rc = _FwpmNetEventSubscribe4 (config.hengine, &subscription, &_wfp_logcallback4, nullptr, &config.hnetevent); // win10rs5+ + + else if (_FwpmNetEventSubscribe3) + rc = _FwpmNetEventSubscribe3 (config.hengine, &subscription, &_wfp_logcallback3, nullptr, &config.hnetevent); // win10rs4+ + + else if (_FwpmNetEventSubscribe2) + rc = _FwpmNetEventSubscribe2 (config.hengine, &subscription, &_wfp_logcallback2, nullptr, &config.hnetevent); // win10+ + + else if (_FwpmNetEventSubscribe1) + rc = _FwpmNetEventSubscribe1 (config.hengine, &subscription, &_wfp_logcallback1, nullptr, &config.hnetevent); // win8+ + + else if (_FwpmNetEventSubscribe0) + rc = _FwpmNetEventSubscribe0 (config.hengine, &subscription, &_wfp_logcallback0, nullptr, &config.hnetevent); // win7+ + + if (rc != ERROR_SUCCESS) + { + _app_logerror (L"FwpmNetEventSubscribe", rc, nullptr, false); + } + else + { + _app_loginit (true); // create log file + result = true; + } + } + + FreeLibrary (hlib); + } + } + + return result; +} + +bool _wfp_logunsubscribe () +{ + bool result = false; + + _app_loginit (false); // destroy log file handle if present + + if (config.hnetevent) + { + const HMODULE hlib = LoadLibrary (L"fwpuclnt.dll"); + + if (hlib) + { + typedef DWORD (WINAPI *FWPMNEU) (HANDLE, HANDLE); // FwpmNetEventUnsubscribe0 + + const FWPMNEU _FwpmNetEventUnsubscribe = (FWPMNEU)GetProcAddress (hlib, "FwpmNetEventUnsubscribe0"); + + if (_FwpmNetEventUnsubscribe) + { + const DWORD rc = _FwpmNetEventUnsubscribe (config.hengine, config.hnetevent); + + if (rc == ERROR_SUCCESS) + { + config.hnetevent = nullptr; + result = true; + } + } + + FreeLibrary (hlib); + } + } + + return result; +} + +void CALLBACK _wfp_logcallback (UINT32 flags, FILETIME const* pft, UINT8 const* app_id, SID* package_id, SID* user_id, UINT8 proto, FWP_IP_VERSION ipver, UINT32 remote_addr, FWP_BYTE_ARRAY16 const* remote_addr6, UINT16 remoteport, UINT32 local_addr, FWP_BYTE_ARRAY16 const* local_addr6, UINT16 localport, UINT16 layer_id, UINT64 filter_id, UINT32 direction, bool is_allow, bool is_loopback) +{ + if (is_allow && app.ConfigGet (L"IsExcludeClassifyAllow", true).AsBool ()) + return; + + // do not parse when tcp connection has been established, or when non-tcp traffic has been authorized + if (layer_id) + { + FWPM_LAYER* layer = nullptr; + + if (FwpmLayerGetById (config.hengine, layer_id, &layer) == ERROR_SUCCESS && layer) + { + if (memcmp (&layer->layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, sizeof (GUID)) == 0 || memcmp (&layer->layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, sizeof (GUID)) == 0) + { + FwpmFreeMemory ((void**)&layer); + return; + } + + FwpmFreeMemory ((void**)&layer); + } + } + + const bool is_logenabled = app.ConfigGet (L"IsLogEnabled", false).AsBool (); + const bool is_notificationenabled = (app.ConfigGet (L"Mode", ModeWhitelist).AsUint () == ModeWhitelist) && app.ConfigGet (L"IsNotificationsEnabled", true).AsBool (); // only for whitelist mode + + PITEM_LIST_ENTRY ptr_entry = (PITEM_LIST_ENTRY)_aligned_malloc (sizeof (ITEM_LIST_ENTRY), MEMORY_ALLOCATION_ALIGNMENT); + + if (ptr_entry) + { + PITEM_LOG ptr_log = new ITEM_LOG; + + if (!ptr_log) + { + _aligned_free (ptr_entry); + return; + } + + // get package id (win8+) + rstring sidstring; + + if ((flags & FWPM_NET_EVENT_FLAG_PACKAGE_ID_SET) != 0 && package_id) + { + sidstring = _r_str_fromsid (package_id); + + if (sidstring.IsEmpty () || !_app_item_get (AppPackage, sidstring.Hash (), nullptr, nullptr, nullptr, nullptr, nullptr)) + sidstring.Clear (); + } + + // copy converted nt device path into win32 + if ((flags & FWPM_NET_EVENT_FLAG_PACKAGE_ID_SET) != 0 && !sidstring.IsEmpty ()) + { + _r_str_alloc (&ptr_log->path, sidstring.GetLength (), sidstring); + + ptr_log->hash = sidstring.Hash (); + } + else if ((flags & FWPM_NET_EVENT_FLAG_APP_ID_SET) != 0 && app_id) + { + const rstring path = _r_path_dospathfromnt (LPCWSTR (app_id)); + + _r_str_alloc (&ptr_log->path, path.GetLength (), path); + + ptr_log->hash = path.Hash (); + + _app_applycasestyle (ptr_log->path, _r_str_length (ptr_log->path)); // apply case-style + } + else + { + ptr_log->hash = 0; + } + + if (is_logenabled || is_notificationenabled) + { + // copy date and time + if (pft) + ptr_log->date = _r_unixtime_from_filetime (pft); + + // get username (only if log enabled) + if (is_logenabled) + { + if ((flags & FWPM_NET_EVENT_FLAG_USER_ID_SET) != 0 && user_id) + { + SID_NAME_USE sid_type = SidTypeInvalid; + + WCHAR username[MAX_PATH] = {0}; + WCHAR domain[MAX_PATH] = {0}; + + DWORD length1 = _countof (username); + DWORD length2 = _countof (domain); + + if (LookupAccountSid (nullptr, user_id, username, &length1, domain, &length2, &sid_type)) + { + rstring userstring; + userstring.Format (L"%s\\%s", domain, username); + + _r_str_alloc (&ptr_log->username, userstring.GetLength (), userstring); + } + } + } + + // read filter information + if (filter_id) + { + FWPM_FILTER* ptr_filter = nullptr; + FWPM_PROVIDER* ptr_provider = nullptr; + + ptr_log->filter_id = filter_id; + + if (FwpmFilterGetById (config.hengine, filter_id, &ptr_filter) == ERROR_SUCCESS && ptr_filter) + { + LPCWSTR filter_name = ptr_filter->displayData.name ? ptr_filter->displayData.name : ptr_filter->displayData.description; + + _r_str_alloc (&ptr_log->filter_name, _r_str_length (filter_name), filter_name); + + if (ptr_filter->providerKey) + { + if (memcmp (ptr_filter->providerKey, &GUID_WfpProvider, sizeof (GUID)) == 0) + ptr_log->is_myprovider = true; + + if (FwpmProviderGetByKey (config.hengine, ptr_filter->providerKey, &ptr_provider) == ERROR_SUCCESS && ptr_provider) + { + LPCWSTR provider_name = ptr_provider->displayData.name ? ptr_provider->displayData.name : ptr_provider->displayData.description; + + _r_str_alloc (&ptr_log->provider_name, _r_str_length (provider_name), provider_name); + } + } + + if (ptr_filter->weight.type == FWP_UINT8) + { + ptr_log->is_blocklist = (ptr_filter->weight.uint8 == FILTER_WEIGHT_BLOCKLIST); + ptr_log->is_system = (ptr_filter->weight.uint8 == FILTER_WEIGHT_HIGHEST) || (ptr_filter->weight.uint8 == FILTER_WEIGHT_HIGHEST_IMPORTANT); + ptr_log->is_custom = (ptr_filter->weight.uint8 == FILTER_WEIGHT_CUSTOM) || (ptr_filter->weight.uint8 == FILTER_WEIGHT_CUSTOM_BLOCK); + } + } + + if (ptr_filter) + FwpmFreeMemory ((void**)&ptr_filter); + + if (ptr_provider) + FwpmFreeMemory ((void**)&ptr_provider); + } + + // destination + if ((flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET) != 0) + { + if (ipver == FWP_IP_VERSION_V4) + { + ptr_log->af = AF_INET; + + // remote address + if ((flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET) != 0 && remote_addr) + ptr_log->remote_addr.S_un.S_addr = ntohl (remote_addr); + + // local address + if ((flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) != 0 && local_addr) + ptr_log->local_addr.S_un.S_addr = ntohl (local_addr); + } + else if (ipver == FWP_IP_VERSION_V6) + { + ptr_log->af = AF_INET6; + + // remote address + if ((flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET) != 0 && remote_addr6) + CopyMemory (ptr_log->remote_addr6.u.Byte, remote_addr6->byteArray16, FWP_V6_ADDR_SIZE); + + // local address + if ((flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) != 0 && local_addr6) + CopyMemory (&ptr_log->local_addr6.u.Byte, local_addr6->byteArray16, FWP_V6_ADDR_SIZE); + } + + if ((flags & FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET) != 0 && remoteport) + ptr_log->remote_port = remoteport; + + if ((flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET) != 0 && localport) + ptr_log->local_port = localport; + } + + // protocol + if ((flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET) != 0 && proto) + ptr_log->protocol = proto; + + // indicates FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW state + ptr_log->is_allow = is_allow; + + // indicates whether the packet originated from (or was heading to) the loopback adapter + ptr_log->is_loopback = is_loopback; + + // indicates the direction of the packet transmission + if (direction == FWP_DIRECTION_OUTBOUND || direction == FWP_DIRECTION_OUT) + ptr_log->direction = FWP_DIRECTION_OUTBOUND; + + else if (direction == FWP_DIRECTION_INBOUND || direction == FWP_DIRECTION_IN) + ptr_log->direction = FWP_DIRECTION_INBOUND; + } + + // push into a slist + { + ptr_entry->Body = ptr_log; + + RtlInterlockedPushEntrySList (&log_stack.ListHead, &ptr_entry->ListEntry); + InterlockedIncrement (&log_stack.item_count); + + // check if thread has been terminated + const LONG thread_count = InterlockedCompareExchange (&log_stack.thread_count, 0, 0); + + if (!_r_fastlock_islocked (&lock_logthread) || ((_r_fastlock_islocked (&lock_logbusy) || InterlockedCompareExchange (&log_stack.item_count, 0, 0) >= NOTIFY_LIMIT_POOL_SIZE) && (thread_count >= 1 && thread_count < NOTIFY_LIMIT_THREAD_COUNT))) + { + _r_fastlock_acquireexclusive (&lock_threadpool); + + _app_freethreadpool (&threads_pool); + + const HANDLE hthread = _r_createthread (&LogThread, app.GetHWND (), true, THREAD_PRIORITY_BELOW_NORMAL); + + if (hthread) + { + InterlockedIncrement (&log_stack.thread_count); + + threads_pool.push_back (hthread); + ResumeThread (hthread); + } + + _r_fastlock_releaseexclusive (&lock_threadpool); + } + } + } +} + +// win7+ callback +void CALLBACK _wfp_logcallback0 (LPVOID, const FWPM_NET_EVENT1 *pEvent) +{ + if (pEvent) + { + UINT16 layer_id = 0; + UINT64 filter_id = 0; + UINT32 direction = 0; + bool is_loopback = false; + + if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP && pEvent->classifyDrop) + { + layer_id = pEvent->classifyDrop->layerId; + filter_id = pEvent->classifyDrop->filterId; + direction = pEvent->classifyDrop->msFwpDirection; + is_loopback = pEvent->classifyDrop->isLoopback; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP && pEvent->ipsecDrop) + { + layer_id = pEvent->ipsecDrop->layerId; + filter_id = pEvent->ipsecDrop->filterId; + direction = pEvent->ipsecDrop->direction; + } + else + { + return; + } + + _wfp_logcallback (pEvent->header.flags, &pEvent->header.timeStamp, pEvent->header.appId.data, nullptr, pEvent->header.userId, pEvent->header.ipProtocol, pEvent->header.ipVersion, pEvent->header.remoteAddrV4, &pEvent->header.remoteAddrV6, pEvent->header.remotePort, pEvent->header.localAddrV4, &pEvent->header.localAddrV6, pEvent->header.localPort, layer_id, filter_id, direction, false, is_loopback); + } +} + +// win8+ callback +void CALLBACK _wfp_logcallback1 (LPVOID, const FWPM_NET_EVENT2 *pEvent) +{ + if (pEvent) + { + UINT16 layer_id = 0; + UINT64 filter_id = 0; + UINT32 direction = 0; + bool is_loopback = false; + bool is_allow = false; + + if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP && pEvent->classifyDrop) + { + layer_id = pEvent->classifyDrop->layerId; + filter_id = pEvent->classifyDrop->filterId; + direction = pEvent->classifyDrop->msFwpDirection; + is_loopback = pEvent->classifyDrop->isLoopback; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP && pEvent->ipsecDrop) + { + layer_id = pEvent->ipsecDrop->layerId; + filter_id = pEvent->ipsecDrop->filterId; + direction = pEvent->ipsecDrop->direction; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW && pEvent->classifyAllow) + { + layer_id = pEvent->classifyAllow->layerId; + filter_id = pEvent->classifyAllow->filterId; + direction = pEvent->classifyAllow->msFwpDirection; + is_loopback = pEvent->classifyAllow->isLoopback; + + is_allow = true; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC && pEvent->classifyDropMac) + { + layer_id = pEvent->classifyDropMac->layerId; + filter_id = pEvent->classifyDropMac->filterId; + direction = pEvent->classifyDropMac->msFwpDirection; + is_loopback = pEvent->classifyDropMac->isLoopback; + } + else + { + return; + } + + _wfp_logcallback (pEvent->header.flags, &pEvent->header.timeStamp, pEvent->header.appId.data, pEvent->header.packageSid, pEvent->header.userId, pEvent->header.ipProtocol, pEvent->header.ipVersion, pEvent->header.remoteAddrV4, &pEvent->header.remoteAddrV6, pEvent->header.remotePort, pEvent->header.localAddrV4, &pEvent->header.localAddrV6, pEvent->header.localPort, layer_id, filter_id, direction, is_allow, is_loopback); + } +} + +// win10+ callback +void CALLBACK _wfp_logcallback2 (LPVOID, const FWPM_NET_EVENT3 *pEvent) +{ + if (pEvent) + { + UINT16 layer_id = 0; + UINT64 filter_id = 0; + UINT32 direction = 0; + bool is_loopback = false; + bool is_allow = false; + + if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP && pEvent->classifyDrop) + { + layer_id = pEvent->classifyDrop->layerId; + filter_id = pEvent->classifyDrop->filterId; + direction = pEvent->classifyDrop->msFwpDirection; + is_loopback = pEvent->classifyDrop->isLoopback; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP && pEvent->ipsecDrop) + { + layer_id = pEvent->ipsecDrop->layerId; + filter_id = pEvent->ipsecDrop->filterId; + direction = pEvent->ipsecDrop->direction; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW && pEvent->classifyAllow) + { + layer_id = pEvent->classifyAllow->layerId; + filter_id = pEvent->classifyAllow->filterId; + direction = pEvent->classifyAllow->msFwpDirection; + is_loopback = pEvent->classifyAllow->isLoopback; + + is_allow = true; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC && pEvent->classifyDropMac) + { + layer_id = pEvent->classifyDropMac->layerId; + filter_id = pEvent->classifyDropMac->filterId; + direction = pEvent->classifyDropMac->msFwpDirection; + is_loopback = pEvent->classifyDropMac->isLoopback; + } + else + { + return; + } + + _wfp_logcallback (pEvent->header.flags, &pEvent->header.timeStamp, pEvent->header.appId.data, pEvent->header.packageSid, pEvent->header.userId, pEvent->header.ipProtocol, pEvent->header.ipVersion, pEvent->header.remoteAddrV4, &pEvent->header.remoteAddrV6, pEvent->header.remotePort, pEvent->header.localAddrV4, &pEvent->header.localAddrV6, pEvent->header.localPort, layer_id, filter_id, direction, is_allow, is_loopback); + } +} + +// win10rs4+ callback +void CALLBACK _wfp_logcallback3 (LPVOID, const FWPM_NET_EVENT4 *pEvent) +{ + if (pEvent) + { + UINT16 layer_id = 0; + UINT64 filter_id = 0; + UINT32 direction = 0; + bool is_loopback = false; + bool is_allow = false; + + if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP && pEvent->classifyDrop) + { + layer_id = pEvent->classifyDrop->layerId; + filter_id = pEvent->classifyDrop->filterId; + direction = pEvent->classifyDrop->msFwpDirection; + is_loopback = pEvent->classifyDrop->isLoopback; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP && pEvent->ipsecDrop) + { + layer_id = pEvent->ipsecDrop->layerId; + filter_id = pEvent->ipsecDrop->filterId; + direction = pEvent->ipsecDrop->direction; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW && pEvent->classifyAllow) + { + layer_id = pEvent->classifyAllow->layerId; + filter_id = pEvent->classifyAllow->filterId; + direction = pEvent->classifyAllow->msFwpDirection; + is_loopback = pEvent->classifyAllow->isLoopback; + + is_allow = true; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC && pEvent->classifyDropMac) + { + layer_id = pEvent->classifyDropMac->layerId; + filter_id = pEvent->classifyDropMac->filterId; + direction = pEvent->classifyDropMac->msFwpDirection; + is_loopback = pEvent->classifyDropMac->isLoopback; + } + else + { + return; + } + + _wfp_logcallback (pEvent->header.flags, &pEvent->header.timeStamp, pEvent->header.appId.data, pEvent->header.packageSid, pEvent->header.userId, pEvent->header.ipProtocol, pEvent->header.ipVersion, pEvent->header.remoteAddrV4, &pEvent->header.remoteAddrV6, pEvent->header.remotePort, pEvent->header.localAddrV4, &pEvent->header.localAddrV6, pEvent->header.localPort, layer_id, filter_id, direction, is_allow, is_loopback); + } +} + +// win10rs5+ callback +void CALLBACK _wfp_logcallback4 (LPVOID, const FWPM_NET_EVENT5 *pEvent) +{ + if (pEvent) + { + UINT16 layer_id = 0; + UINT64 filter_id = 0; + UINT32 direction = 0; + bool is_loopback = false; + bool is_allow = false; + + if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP && pEvent->classifyDrop) + { + layer_id = pEvent->classifyDrop->layerId; + filter_id = pEvent->classifyDrop->filterId; + direction = pEvent->classifyDrop->msFwpDirection; + is_loopback = pEvent->classifyDrop->isLoopback; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP && pEvent->ipsecDrop) + { + layer_id = pEvent->ipsecDrop->layerId; + filter_id = pEvent->ipsecDrop->filterId; + direction = pEvent->ipsecDrop->direction; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW && pEvent->classifyAllow) + { + layer_id = pEvent->classifyAllow->layerId; + filter_id = pEvent->classifyAllow->filterId; + direction = pEvent->classifyAllow->msFwpDirection; + is_loopback = pEvent->classifyAllow->isLoopback; + + is_allow = true; + } + else if (pEvent->type == FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC && pEvent->classifyDropMac) + { + layer_id = pEvent->classifyDropMac->layerId; + filter_id = pEvent->classifyDropMac->filterId; + direction = pEvent->classifyDropMac->msFwpDirection; + is_loopback = pEvent->classifyDropMac->isLoopback; + } + else + { + return; + } + + _wfp_logcallback (pEvent->header.flags, &pEvent->header.timeStamp, pEvent->header.appId.data, pEvent->header.packageSid, pEvent->header.userId, pEvent->header.ipProtocol, pEvent->header.ipVersion, pEvent->header.remoteAddrV4, &pEvent->header.remoteAddrV6, pEvent->header.remotePort, pEvent->header.localAddrV4, &pEvent->header.localAddrV6, pEvent->header.localPort, layer_id, filter_id, direction, is_allow, is_loopback); + } +} + +UINT WINAPI LogThread (LPVOID lparam) +{ + const HWND hwnd = (HWND)lparam; + + _r_fastlock_acquireshared (&lock_logthread); + + while (true) + { + const PSLIST_ENTRY listEntry = RtlInterlockedPopEntrySList (&log_stack.ListHead); + + if (!listEntry) + break; + + InterlockedDecrement (&log_stack.item_count); + + PITEM_LIST_ENTRY ptr_entry = CONTAINING_RECORD (listEntry, ITEM_LIST_ENTRY, ListEntry); + PITEM_LOG ptr_log = ptr_entry->Body; + + _aligned_free (ptr_entry); + + if (ptr_log) + { + const bool is_logenabled = app.ConfigGet (L"IsLogEnabled", false).AsBool (); + const bool is_notificationenabled = (app.ConfigGet (L"Mode", ModeWhitelist).AsUint () == ModeWhitelist) && app.ConfigGet (L"IsNotificationsEnabled", true).AsBool (); // only for whitelist mode + + // apps collector + _r_fastlock_acquireshared (&lock_access); + const bool is_notexist = ptr_log->hash && ptr_log->path && !ptr_log->is_allow && apps.find (ptr_log->hash) == apps.end (); + _r_fastlock_releaseshared (&lock_access); + + if (is_notexist) + { + _r_fastlock_acquireshared (&lock_logbusy); + + _r_fastlock_acquireexclusive (&lock_access); + _app_addapplication (hwnd, ptr_log->path, 0, 0, 0, false, false, true); + _r_fastlock_releaseexclusive (&lock_access); + + _r_fastlock_releaseshared (&lock_logbusy); + + _app_listviewsort (hwnd, IDC_APPS_PROFILE, -1, false); + _app_profile_save (hwnd); + } + + bool is_added = false; + + if ((is_logenabled || is_notificationenabled) && (!(ptr_log->is_system && app.ConfigGet (L"IsExcludeStealth", true).AsBool ()))) + { + _r_fastlock_acquireshared (&lock_logbusy); + + _app_formataddress (ptr_log, FWP_DIRECTION_OUTBOUND, ptr_log->remote_port, &ptr_log->remote_fmt, true); + _app_formataddress (ptr_log, FWP_DIRECTION_INBOUND, ptr_log->local_port, &ptr_log->local_fmt, true); + + _r_fastlock_releaseshared (&lock_logbusy); + + // write log to a file + if (is_logenabled) + _app_logwrite (ptr_log); + + // show notification (only for my own provider and file is present) + if (is_notificationenabled && ptr_log->hash && !ptr_log->is_allow && ptr_log->is_myprovider) + { + if (!(ptr_log->is_blocklist && app.ConfigGet (L"IsExcludeBlocklist", true).AsBool ()) && !(ptr_log->is_custom && app.ConfigGet (L"IsExcludeCustomRules", true).AsBool ())) + { + // read app config + _r_fastlock_acquireexclusive (&lock_access); + + PITEM_APP const ptr_app = _app_getapplication (ptr_log->hash); + + if (ptr_app) + { + if (!ptr_app->is_silent) + is_added = _app_notifyadd (config.hnotification, ptr_log, ptr_app); + } + + _r_fastlock_releaseexclusive (&lock_access); + } + } + } + + if (!is_added) + SAFE_DELETE (ptr_log); + } + } + + _r_fastlock_releaseshared (&lock_logthread); + + InterlockedDecrement (&log_stack.thread_count); + + _endthreadex (0); + + return ERROR_SUCCESS; +} diff --git a/src/log.hpp b/src/log.hpp new file mode 100644 index 00000000..2e1771a8 --- /dev/null +++ b/src/log.hpp @@ -0,0 +1,27 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +void _app_logerror (LPCWSTR fn, DWORD errcode, LPCWSTR desc, bool is_nopopups); + +rstring _app_getlogviewer (); + +bool _app_logchecklimit (); + +bool _app_loginit (bool is_install); +void _app_logwrite (PITEM_LOG const ptr_log); +void _app_logclear (); + +bool _wfp_logsubscribe (); +bool _wfp_logunsubscribe (); + +void CALLBACK _wfp_logcallback (UINT32 flags, FILETIME const* pft, UINT8 const* app_id, SID* package_id, SID* user_id, UINT8 proto, FWP_IP_VERSION ipver, UINT32 remote_addr, FWP_BYTE_ARRAY16 const* remote_addr6, UINT16 remoteport, UINT32 local_addr, FWP_BYTE_ARRAY16 const* local_addr6, UINT16 localport, UINT16 layer_id, UINT64 filter_id, UINT32 direction, bool is_allow, bool is_loopback); + +void CALLBACK _wfp_logcallback0 (LPVOID, const FWPM_NET_EVENT1 *pEvent); +void CALLBACK _wfp_logcallback1 (LPVOID, const FWPM_NET_EVENT2 *pEvent); +void CALLBACK _wfp_logcallback2 (LPVOID, const FWPM_NET_EVENT3 *pEvent); +void CALLBACK _wfp_logcallback3 (LPVOID, const FWPM_NET_EVENT4 *pEvent); +void CALLBACK _wfp_logcallback4 (LPVOID, const FWPM_NET_EVENT5 *pEvent); + +UINT WINAPI LogThread (LPVOID lparam); diff --git a/src/notifications.cpp b/src/notifications.cpp new file mode 100644 index 00000000..67003d9b --- /dev/null +++ b/src/notifications.cpp @@ -0,0 +1,1198 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +void _app_notifycreatewindow () +{ + WNDCLASSEX wcex = {0}; + + wcex.cbSize = sizeof (wcex); + wcex.style = CS_VREDRAW | CS_HREDRAW; + wcex.hInstance = app.GetHINSTANCE (); + wcex.hCursor = LoadCursor (nullptr, IDC_ARROW); + wcex.hbrBackground = GetSysColorBrush (COLOR_WINDOW); + wcex.lpszClassName = NOTIFY_CLASS_DLG; + wcex.lpfnWndProc = &NotificationProc; + + if (!RegisterClassEx (&wcex)) + return; + + static const UINT wnd_width = app.GetDPI (NOTIFY_WIDTH); + static const UINT wnd_height = app.GetDPI (NOTIFY_HEIGHT); + + static const INT title_font_height = 10; + static const INT text_font_height = 9; + + static const INT cxsmIcon = GetSystemMetrics (SM_CXSMICON); + static const INT IconSize = app.GetDPI (20); + + config.hnotification = CreateWindowEx (WS_EX_TOPMOST | WS_EX_TOOLWINDOW, NOTIFY_CLASS_DLG, nullptr, WS_POPUP, 0, 0, wnd_width, wnd_height, nullptr, nullptr, wcex.hInstance, nullptr); + + if (!config.hnotification) + return; + + HFONT hfont_title = nullptr; + HFONT hfont_main = nullptr; + HFONT hfont_text = nullptr; + + // load system font + { + NONCLIENTMETRICS ncm = {0}; + ncm.cbSize = sizeof (ncm); + + if (SystemParametersInfo (SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) + { + hfont_title = _app_notifyinitfont (&ncm.lfCaptionFont, title_font_height, FW_NORMAL, UI_FONT_NOTIFICATION, false); + hfont_main = _app_notifyinitfont (&ncm.lfMessageFont, text_font_height, FW_NORMAL, UI_FONT_NOTIFICATION, true); + hfont_text = _app_notifyinitfont (&ncm.lfMessageFont, text_font_height, FW_NORMAL, UI_FONT_NOTIFICATION, false); + } + } + + HWND hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_CENTER | SS_ICON, app.GetDPI (6), app.GetDPI (4), IconSize, IconSize, config.hnotification, (HMENU)IDC_ICON_ID, nullptr, nullptr); + SendMessage (hctrl, STM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_MAIN, cxsmIcon)); + + hctrl = CreateWindow (WC_STATIC, APP_NAME, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_WORDELLIPSIS, IconSize + app.GetDPI (10), app.GetDPI (4), wnd_width - app.GetDPI (150), IconSize, config.hnotification, (HMENU)IDC_TITLE_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_title, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_CENTER | SS_ICON | SS_NOTIFY, wnd_width - IconSize * 4 - app.GetDPI (18), app.GetDPI (4), IconSize, IconSize, config.hnotification, (HMENU)IDC_MENU_BTN, nullptr, nullptr); + SendMessage (hctrl, STM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_MENU, cxsmIcon)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_CENTER | SS_ICON | SS_NOTIFY, wnd_width - IconSize * 3 - app.GetDPI (14), app.GetDPI (4), IconSize, IconSize, config.hnotification, (HMENU)IDC_TIMER_BTN, nullptr, nullptr); + SendMessage (hctrl, STM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_TIMER, cxsmIcon)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_CENTER | SS_ICON | SS_NOTIFY, wnd_width - IconSize * 2 - app.GetDPI (10), app.GetDPI (4), IconSize, IconSize, config.hnotification, (HMENU)IDC_RULES_BTN, nullptr, nullptr); + SendMessage (hctrl, STM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_RULES, cxsmIcon)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_CENTER | SS_ICON | SS_NOTIFY, wnd_width - IconSize - app.GetDPI (6), app.GetDPI (4), IconSize, IconSize, config.hnotification, (HMENU)IDC_CLOSE_BTN, nullptr, nullptr); + SendMessage (hctrl, STM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_CLOSE, cxsmIcon)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (44), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_FILE_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_RIGHT | SS_NOTIFY, app.GetDPI (12), app.GetDPI (44), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_FILE_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_main, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (64), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_SIGNATURE_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (64), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_SIGNATURE_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (84), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_ADDRESS_REMOTE_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (84), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_ADDRESS_REMOTE_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (104), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_ADDRESS_LOCAL_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (104), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_ADDRESS_LOCAL_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (124), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_PROTOCOL_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (124), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_PROTOCOL_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (144), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_FILTER_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (144), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_FILTER_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + hctrl = CreateWindow (WC_STATIC, nullptr, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, app.GetDPI (12), app.GetDPI (164), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_DATE_ID, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + + hctrl = CreateWindow (WC_EDIT, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_AUTOHSCROLL | ES_RIGHT, app.GetDPI (12), app.GetDPI (164), wnd_width - app.GetDPI (24), app.GetDPI (16), config.hnotification, (HMENU)IDC_DATE_TEXT, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, EM_SETMARGINS, EC_LEFTMARGIN, 0); + SendMessage (hctrl, EM_SETMARGINS, EC_RIGHTMARGIN, 0); + + static const UINT btn_height = 54; + + hctrl = CreateWindow (WC_BUTTON, nullptr, WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_COMMANDLINK, app.GetDPI (8), wnd_height - app.GetDPI (btn_height * 3 + 19), wnd_width - app.GetDPI (8 * 2), app.GetDPI (btn_height), config.hnotification, (HMENU)IDC_ALLOW_BTN, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, BM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_ALLOW, IconSize)); + + hctrl = CreateWindow (WC_BUTTON, nullptr, WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_COMMANDLINK, app.GetDPI (8), wnd_height - app.GetDPI (btn_height * 2 + 16), wnd_width - app.GetDPI (8 * 2), app.GetDPI (btn_height), config.hnotification, (HMENU)IDC_BLOCK_BTN, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, BM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_BLOCK, IconSize)); + + hctrl = CreateWindow (WC_BUTTON, nullptr, WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_COMMANDLINK, app.GetDPI (8), wnd_height - app.GetDPI (btn_height + 12), wnd_width - app.GetDPI (8 * 2), app.GetDPI (btn_height), config.hnotification, (HMENU)IDC_LATER_BTN, nullptr, nullptr); + SendMessage (hctrl, WM_SETFONT, (WPARAM)hfont_text, MAKELPARAM (TRUE, 0)); + SendMessage (hctrl, BM_SETIMAGE, IMAGE_ICON, (WPARAM)app.GetSharedIcon (app.GetHINSTANCE (), IDI_BLOCK, IconSize)); + + _r_ctrl_settip (config.hnotification, IDC_MENU_BTN, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_RULES_BTN, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_TIMER_BTN, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_CLOSE_BTN, LPSTR_TEXTCALLBACK); + + _r_ctrl_settip (config.hnotification, IDC_FILE_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_SIGNATURE_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_ADDRESS_LOCAL_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_ADDRESS_REMOTE_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_PROTOCOL_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_FILTER_TEXT, LPSTR_TEXTCALLBACK); + _r_ctrl_settip (config.hnotification, IDC_DATE_TEXT, LPSTR_TEXTCALLBACK); + + _app_notifyhide (config.hnotification); +} + +bool _app_notifycommand (HWND hwnd, UINT ctrl_id, size_t timer_idx) +{ + _r_fastlock_acquireexclusive (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + + if (idx != LAST_VALUE) + { + PITEM_LOG ptr_log = notifications.at (idx); + + if (ptr_log) + { + _r_fastlock_acquireexclusive (&lock_access); + + const size_t hash = ptr_log->hash; + const size_t item = _app_getposition (app.GetHWND (), hash); + + PITEM_APP ptr_app = _app_getapplication (hash); + + if (ptr_app) + { + if (ctrl_id == IDC_ALLOW_BTN || ctrl_id == IDC_BLOCK_BTN) + { + ptr_app->is_enabled = (ctrl_id == IDC_ALLOW_BTN); + + if (ctrl_id == IDC_BLOCK_BTN) + ptr_app->is_silent = true; + + _r_fastlock_acquireshared (&lock_checkbox); + + _r_listview_setitem (app.GetHWND (), IDC_APPS_PROFILE, item, 0, nullptr, LAST_VALUE, _app_getappgroup (hash, ptr_app)); + _r_listview_setitemcheck (app.GetHWND (), IDC_APPS_PROFILE, item, ptr_app->is_enabled); + + _r_fastlock_releaseshared (&lock_checkbox); + + MFILTER_APPS rules; + rules.push_back (ptr_app); + + // create rule timer + if (timer_idx != LAST_VALUE) + { + _app_timer_create (app.GetHWND (), &rules, timers.at (timer_idx)); + } + else + { + _wfp_create3filters (&rules, __LINE__); + } + } + else if (ctrl_id == IDM_DISABLENOTIFICATIONS) + { + ptr_app->is_silent = true; + } + else if (ctrl_id == IDC_LATER_BTN) + { + // TODO + } + + ptr_app->last_notify = _r_unixtime_now (); + + _r_fastlock_releaseexclusive (&lock_access); + + _app_freenotify (hash, false); + + _r_fastlock_releaseexclusive (&lock_notification); + + _app_notifyrefresh (hwnd, true); + + _app_listviewsort (app.GetHWND (), IDC_APPS_PROFILE, -1, false); + _app_profile_save (app.GetHWND ()); + + _r_listview_redraw (app.GetHWND (), IDC_APPS_PROFILE); + + return true; + } + + _r_fastlock_releaseexclusive (&lock_access); + } + } + + _r_fastlock_releaseexclusive (&lock_notification); + + return false; +} + +bool _app_notifyadd (HWND hwnd, PITEM_LOG const ptr_log, PITEM_APP const ptr_app) +{ + if (!ptr_app || !ptr_log) + return false; + + const time_t current_time = _r_unixtime_now (); + + // check for last display time + { + const time_t notification_timeout = app.ConfigGet (L"NotificationsTimeout", NOTIFY_TIMEOUT_DEFAULT).AsLonglong (); + + if (notification_timeout && ((current_time - ptr_app->last_notify) < notification_timeout)) + return false; + } + + _r_fastlock_acquireexclusive (&lock_notification); + + ptr_app->last_notify = current_time; + + // remove existing log item (if exists) + for (size_t i = 0; i < notifications.size (); i++) + { + PITEM_LOG ptr_chk = notifications.at (i); + + if (ptr_chk && ptr_chk->hash == ptr_log->hash) + { + _app_freenotify (i, true); + break; + } + } + + // prevent notifications overflow + if ((notifications.size () >= NOTIFY_LIMIT_SIZE)) + _app_freenotify (0, true); + + notifications.push_back (ptr_log); + size_t idx = notifications.size () - 1; + + _r_fastlock_releaseexclusive (&lock_notification); + + if (app.ConfigGet (L"IsNotificationsSound", true).AsBool ()) + _app_notifyplaysound (); + + if (!_r_wnd_undercursor (hwnd) && _app_notifyshow (hwnd, idx, true, true)) + { + const UINT display_timeout = app.ConfigGet (L"NotificationsDisplayTimeout", NOTIFY_TIMER_DEFAULT).AsUint (); + + if (display_timeout) + _app_notifysettimeout (hwnd, NOTIFY_TIMER_TIMEOUT_ID, true, (display_timeout * _R_SECONDSCLOCK_MSEC)); + } + + return true; +} + +void _app_freenotify (size_t idx_orhash, bool is_idx) +{ + const size_t count = notifications.size (); + + if (!count) + return; + + if (is_idx) + { + PITEM_LOG ptr_log = notifications.at (idx_orhash); + + SAFE_DELETE (ptr_log); + + notifications.erase (notifications.begin () + idx_orhash); + } + else + { + for (size_t i = (count - 1); i != LAST_VALUE; i--) + { + PITEM_LOG ptr_log = notifications.at (i); + + if (!ptr_log || ptr_log->hash == idx_orhash) + { + SAFE_DELETE (ptr_log); + + notifications.erase (notifications.begin () + i); + } + } + } +} + +size_t _app_notifygetcurrent (HWND hwnd) +{ + size_t new_idx = LAST_VALUE; + const size_t count = notifications.size (); + + if (count) + { + if (count == 1) + { + new_idx = 0; + } + else + { + const size_t idx = (size_t)GetWindowLongPtr (hwnd, GWLP_USERDATA); + new_idx = max (0, min (idx, count - 1)); + } + } + + SetWindowLongPtr (hwnd, GWLP_USERDATA, new_idx); + + return new_idx; +} + +bool _app_notifyshow (HWND hwnd, size_t idx, bool is_forced, bool is_safety) +{ + _r_fastlock_acquireshared (&lock_notification); + + if (!app.ConfigGet (L"IsNotificationsEnabled", true).AsBool () || notifications.empty () || idx == LAST_VALUE) + { + if (notifications.empty () || idx == LAST_VALUE) + SetWindowLongPtr (hwnd, GWLP_USERDATA, LAST_VALUE); + + _r_fastlock_releaseshared (&lock_notification); + + return false; + } + + // destroy safety timer to prevent race condition + KillTimer (hwnd, NOTIFY_TIMER_SAFETY_ID); + + // prevent fullscreen apps lose focus + if (is_forced && _r_wnd_isfullscreenmode ()) + is_forced = false; + + const size_t total_size = notifications.size (); + + idx = (total_size == 1) ? 0 : max (0, min (idx, total_size - 1)); + + PITEM_LOG const ptr_log = notifications.at (idx); + + if (ptr_log) + { + _r_fastlock_acquireshared (&lock_access); + + PITEM_APP const ptr_app = _app_getapplication (ptr_log->hash); + + if (ptr_app) + { + SetWindowLongPtr (hwnd, GWLP_USERDATA, idx); + + rstring is_signed; + const rstring empty_text = app.LocaleString (IDS_STATUS_EMPTY, nullptr); + + if (app.ConfigGet (L"IsCerificatesEnabled", false).AsBool ()) + { + if (ptr_app->is_signed) + is_signed = (ptr_app->signer && ptr_app->signer[0]) ? ptr_app->signer : app.LocaleString (IDS_SIGN_SIGNED, nullptr); + + else + is_signed = app.LocaleString (IDS_SIGN_UNSIGNED, nullptr); + } + + _r_ctrl_settext (hwnd, IDC_TITLE_ID, APP_NAME); + + { + const HDC hdc = GetDC (hwnd); + + _app_notifysettext (hdc, hwnd, IDC_FILE_ID, app.LocaleString (IDS_FILE, L":"), IDC_FILE_TEXT, (ptr_app->display_name && ptr_app->display_name[0]) ? _r_path_extractfile (ptr_app->display_name) : empty_text); + _app_notifysettext (hdc, hwnd, IDC_SIGNATURE_ID, app.LocaleString (IDS_SIGNATURE, L":"), IDC_SIGNATURE_TEXT, is_signed.IsEmpty () ? empty_text : is_signed); + _app_notifysettext (hdc, hwnd, IDC_ADDRESS_REMOTE_ID, app.LocaleString (IDS_ADDRESS_REMOTE, L":"), IDC_ADDRESS_REMOTE_TEXT, (ptr_log->remote_fmt && ptr_log->remote_fmt[0]) ? ptr_log->remote_fmt : empty_text); + _app_notifysettext (hdc, hwnd, IDC_ADDRESS_LOCAL_ID, app.LocaleString (IDS_ADDRESS_LOCAL, L":"), IDC_ADDRESS_LOCAL_TEXT, (ptr_log->local_fmt && ptr_log->local_fmt[0]) ? ptr_log->local_fmt : empty_text); + _app_notifysettext (hdc, hwnd, IDC_PROTOCOL_ID, app.LocaleString (IDS_PROTOCOL, L":"), IDC_PROTOCOL_TEXT, _app_getprotoname (ptr_log->protocol)); + _app_notifysettext (hdc, hwnd, IDC_FILTER_ID, app.LocaleString (IDS_FILTER, L":"), IDC_FILTER_TEXT, (ptr_log->filter_name && ptr_log->filter_name[0]) ? ptr_log->filter_name : empty_text); + _app_notifysettext (hdc, hwnd, IDC_DATE_ID, app.LocaleString (IDS_DATE, L":"), IDC_DATE_TEXT, _r_fmt_date (ptr_log->date, FDTF_SHORTDATE | FDTF_LONGTIME)); + + ReleaseDC (hwnd, hdc); + } + + _r_ctrl_settext (hwnd, IDC_ALLOW_BTN, app.LocaleString (IDS_ACTION_ALLOW, nullptr)); + _r_ctrl_settext (hwnd, IDC_BLOCK_BTN, app.LocaleString (IDS_ACTION_BLOCK, nullptr)); + _r_ctrl_settext (hwnd, IDC_LATER_BTN, app.LocaleString (IDS_ACTION_LATER, nullptr)); + + _app_notifysetnote (hwnd, IDC_ALLOW_BTN, app.LocaleString (IDS_ACTION_ALLOW_HINT, nullptr)); + _app_notifysetnote (hwnd, IDC_BLOCK_BTN, app.LocaleString (IDS_ACTION_BLOCK_HINT, nullptr)); + _app_notifysetnote (hwnd, IDC_LATER_BTN, app.LocaleString (IDS_ACTION_LATER_HINT, nullptr)); + + _r_fastlock_releaseshared (&lock_notification); + _r_fastlock_releaseshared (&lock_access); + + _app_notifysetpos (hwnd); + + _r_ctrl_enable (hwnd, IDC_ALLOW_BTN, !is_safety); + _r_ctrl_enable (hwnd, IDC_BLOCK_BTN, !is_safety); + _r_ctrl_enable (hwnd, IDC_LATER_BTN, !is_safety); + + if (is_safety) + SetTimer (hwnd, NOTIFY_TIMER_SAFETY_ID, NOTIFY_TIMER_SAFETY, nullptr); + + ShowWindow (hwnd, is_forced ? SW_SHOW : SW_SHOWNA); + + return true; + } + + _r_fastlock_releaseshared (&lock_access); + } + + _r_fastlock_releaseshared (&lock_notification); + + return false; +} + +void _app_notifyhide (HWND hwnd) +{ + _app_notifysettimeout (hwnd, 0, false, 0); + + ShowWindow (hwnd, SW_HIDE); +} + +// Play notification sound even if system have "nosound" mode +void _app_notifyplaysound () +{ + bool result = false; + static WCHAR notify_snd_path[MAX_PATH] = {0}; + + if (!notify_snd_path[0] || !_r_fs_exists (notify_snd_path)) + { + HKEY hkey = nullptr; + notify_snd_path[0] = 0; + + if (RegOpenKeyEx (HKEY_CURRENT_USER, L"AppEvents\\Schemes\\Apps\\.Default\\" NOTIFY_SOUND_DEFAULT L"\\.Default", 0, KEY_READ, &hkey) == ERROR_SUCCESS) + { + DWORD size = _countof (notify_snd_path) * sizeof (WCHAR); + + if (RegQueryValueEx (hkey, nullptr, nullptr, nullptr, (LPBYTE)notify_snd_path, &size) == ERROR_SUCCESS) + { + const rstring path = _r_path_expand (notify_snd_path); + + if (_r_fs_exists (path)) + { + StringCchCopy (notify_snd_path, _countof (notify_snd_path), path); + result = true; + } + } + + RegCloseKey (hkey); + } + } + else + { + result = true; + } + + if (!result || !_r_fs_exists (notify_snd_path) || !PlaySound (notify_snd_path, nullptr, SND_FILENAME | SND_ASYNC)) + PlaySound (NOTIFY_SOUND_DEFAULT, nullptr, SND_ASYNC); +} + +bool _app_notifyrefresh (HWND hwnd, bool is_safety) +{ + if (!app.ConfigGet (L"IsNotificationsEnabled", true).AsBool ()) + { + _app_notifyhide (config.hnotification); + return true; + } + + _r_fastlock_acquireshared (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + + if (notifications.empty () || idx == LAST_VALUE || !IsWindowVisible (hwnd)) + { + _app_notifyhide (hwnd); + _r_fastlock_releaseshared (&lock_notification); + + return false; + } + + _r_fastlock_releaseshared (&lock_notification); + + return _app_notifyshow (hwnd, idx, false, is_safety); +} + +void _app_notifysetpos (HWND hwnd) +{ + RECT windowRect = {0}; + GetWindowRect (hwnd, &windowRect); + + RECT desktopRect = {0}; + SystemParametersInfo (SPI_GETWORKAREA, 0, &desktopRect, 0); + + APPBARDATA appbar = {0}; + appbar.cbSize = sizeof (appbar); + appbar.hWnd = FindWindow (L"Shell_TrayWnd", nullptr); + + SHAppBarMessage (ABM_GETTASKBARPOS, &appbar); + + const UINT border_x = GetSystemMetrics (SM_CXBORDER); + const UINT border_y = GetSystemMetrics (SM_CYBORDER); + + if (appbar.uEdge == ABE_LEFT) + { + windowRect.left = appbar.rc.right + border_x; + windowRect.top = (desktopRect.bottom - (windowRect.bottom - windowRect.top)) - border_y; + } + else if (appbar.uEdge == ABE_TOP) + { + windowRect.left = (desktopRect.right - (windowRect.right - windowRect.left)) - border_x; + windowRect.top = appbar.rc.bottom + border_y; + } + else if (appbar.uEdge == ABE_RIGHT) + { + windowRect.left = (desktopRect.right - (windowRect.right - windowRect.left)) - border_x; + windowRect.top = (desktopRect.bottom - (windowRect.bottom - windowRect.top)) - border_y; + } + else/* if (appbar.uEdge == ABE_BOTTOM)*/ + { + windowRect.left = (desktopRect.right - (windowRect.right - windowRect.left)) - border_x; + windowRect.top = (desktopRect.bottom - (windowRect.bottom - windowRect.top)) - border_y; + } + + SetWindowPos (hwnd, nullptr, windowRect.left, windowRect.top, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_FRAMECHANGED); +} + +bool _app_notifysettimeout (HWND hwnd, UINT_PTR timer_id, bool is_create, UINT timeout) +{ + if (is_create) + { + if (!hwnd || !timer_id) + return false; + + if (timer_id == NOTIFY_TIMER_TIMEOUT_ID) + config.is_notifytimeout = true; + + SetTimer (hwnd, timer_id, timeout, nullptr); + } + else + { + if (!timer_id || timer_id == NOTIFY_TIMER_TIMEOUT_ID) + config.is_notifytimeout = false; + + if (timer_id) + { + KillTimer (hwnd, timer_id); + } + else + { + KillTimer (hwnd, NOTIFY_TIMER_POPUP_ID); + KillTimer (hwnd, NOTIFY_TIMER_TIMEOUT_ID); + } + } + + return true; +} + +void _app_notifysetnote (HWND hwnd, UINT ctrl_id, rstring str) +{ + static const size_t len = 48; + + if (str.GetLength () > len) + { + str.Insert (len, L"..."); + str.SetLength (len + 3); + } + + SendDlgItemMessage (hwnd, ctrl_id, BCM_SETNOTE, 0, (LPARAM)str.GetString ()); +} + +void _app_notifysettext (HDC hdc, HWND hwnd, UINT ctrl_id1, LPCWSTR text1, UINT ctrl_id2, LPCWSTR text2) +{ + RECT rc_wnd = {0}; + RECT rc_ctrl = {0}; + + const HWND hctrl1 = GetDlgItem (hwnd, ctrl_id1); + const HWND hctrl2 = GetDlgItem (hwnd, ctrl_id2); + + static const INT padding = app.GetDPI (12); + static const INT border = padding / 2; + + SelectObject (hdc, (HFONT)SendDlgItemMessage (hwnd, ctrl_id1, WM_GETFONT, 0, 0)); // fix + SelectObject (hdc, (HFONT)SendDlgItemMessage (hwnd, ctrl_id2, WM_GETFONT, 0, 0)); // fix + + GetWindowRect (hwnd, &rc_wnd); + GetWindowRect (hctrl1, &rc_ctrl); + + MapWindowPoints (HWND_DESKTOP, hwnd, (LPPOINT)&rc_ctrl, 2); + + const INT wnd_width = _R_RECT_WIDTH (&rc_wnd) - (padding); + const INT ctrl1_width = _r_dc_fontwidth (hdc, text1, _r_str_length (text1)) + GetSystemMetrics (SM_CYSMCAPTION); + //const INT ctrl2_width = _r_dc_fontwidth (hdc, text2, _r_str_length (text2)); + + SetWindowPos (hctrl1, nullptr, padding, rc_ctrl.top, ctrl1_width + border, _R_RECT_HEIGHT (&rc_ctrl), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW); + SetWindowPos (hctrl2, nullptr, padding + ctrl1_width + border, rc_ctrl.top, wnd_width - ctrl1_width - padding - border, _R_RECT_HEIGHT (&rc_ctrl), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW); + + SetWindowText (hctrl1, text1); + SetWindowText (hctrl2, text2); +} + +HFONT _app_notifyinitfont (PLOGFONT plf, LONG height, LONG weight, LPCWSTR name, BOOL is_underline) +{ + plf->lfHeight = _r_dc_fontsizetoheight (height); + + plf->lfWeight = weight; + plf->lfUnderline = (BYTE)is_underline; + + //plf->lfCharSet = DEFAULT_CHARSET; + //plf->lfQuality = DEFAULT_QUALITY; + + StringCchCopy (plf->lfFaceName, LF_FACESIZE, name); + + return CreateFontIndirect (plf); +} + +void DrawFrameBorder (HDC hdc, HWND hwnd, COLORREF clr) +{ + RECT rc = {0}; + GetWindowRect (hwnd, &rc); + + const HPEN hpen = CreatePen (PS_INSIDEFRAME, GetSystemMetrics (SM_CXBORDER), clr); + + const HPEN old_pen = (HPEN)SelectObject (hdc, hpen); + const HBRUSH old_brush = (HBRUSH)SelectObject (hdc, GetStockObject (NULL_BRUSH)); + + Rectangle (hdc, 0, 0, _R_RECT_WIDTH (&rc), _R_RECT_HEIGHT (&rc)); + + SelectObject (hdc, old_pen); + SelectObject (hdc, old_brush); + + DeleteObject (hpen); +} + +LRESULT CALLBACK NotificationProc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_CREATE: + { +#ifndef _APP_NO_DARKTHEME + _r_wnd_setdarktheme (config.hnotification); +#endif // _APP_NO_DARKTHEME + + break; + } + + case WM_CLOSE: + { + _app_notifyhide (hwnd); + + SetWindowLongPtr (hwnd, DWLP_MSGRESULT, TRUE); + return TRUE; + } + + case WM_MOUSEMOVE: + { + if (!config.is_notifytimeout) + _app_notifysettimeout (hwnd, 0, false, 0); + + break; + } + + case WM_ACTIVATE: + { + switch (LOWORD (wparam)) + { + case WA_INACTIVE: + { + if (!config.is_notifytimeout && !_r_wnd_undercursor (hwnd)) + _app_notifyhide (hwnd); + + break; + } + } + + break; + } + + case WM_TIMER: + { + if (wparam == NOTIFY_TIMER_SAFETY_ID) + { + _r_ctrl_enable (hwnd, IDC_ALLOW_BTN, true); + _r_ctrl_enable (hwnd, IDC_BLOCK_BTN, true); + _r_ctrl_enable (hwnd, IDC_LATER_BTN, true); + + KillTimer (hwnd, wparam); + } + + if (config.is_notifytimeout && wparam != NOTIFY_TIMER_TIMEOUT_ID) + return FALSE; + + if (wparam == NOTIFY_TIMER_TIMEOUT_ID) + { + if (_r_wnd_undercursor (hwnd)) + { + _app_notifysettimeout (hwnd, wparam, false, 0); + return FALSE; + } + } + + if (wparam == NOTIFY_TIMER_POPUP_ID || wparam == NOTIFY_TIMER_TIMEOUT_ID) + _app_notifyhide (hwnd); + + break; + } + + case WM_KEYDOWN: + { + switch (wparam) + { + case VK_ESCAPE: + { + _app_notifyhide (hwnd); + break; + } + } + + break; + } + + case WM_PAINT: + { + PAINTSTRUCT ps = {0}; + const HDC hdc = BeginPaint (hwnd, &ps); + + RECT rc = {0}; + GetClientRect (hwnd, &rc); + + rc.bottom = app.GetDPI (28); + + _r_dc_fillrect (hdc, &rc, NOTIFY_CLR_TITLE_BG); + + for (INT i = 0; i < _R_RECT_WIDTH (&rc); i++) + SetPixel (hdc, i, rc.bottom, _r_dc_getcolorshade (NOTIFY_CLR_TITLE_BG, 90)); + + DrawFrameBorder (hdc, hwnd, NOTIFY_CLR_BORDER); + + EndPaint (hwnd, &ps); + + break; + } + + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + { + const UINT ctrl_id = GetDlgCtrlID ((HWND)lparam); + + SetBkMode ((HDC)wparam, TRANSPARENT); // HACK!!! + + if ( + ctrl_id == IDC_ICON_ID || + ctrl_id == IDC_TITLE_ID || + ctrl_id == IDC_MENU_BTN || + ctrl_id == IDC_RULES_BTN || + ctrl_id == IDC_TIMER_BTN || + ctrl_id == IDC_CLOSE_BTN + ) + { + SetTextColor ((HDC)wparam, NOTIFY_CLR_TITLE_TEXT); + + static HBRUSH hbrush = nullptr; + + if (!hbrush) + hbrush = CreateSolidBrush (NOTIFY_CLR_TITLE_BG); + + return (INT_PTR)hbrush; + } + else if (ctrl_id == IDC_FILE_TEXT) + SetTextColor ((HDC)wparam, GetSysColor (COLOR_HIGHLIGHT)); + + else + SetTextColor ((HDC)wparam, NOTIFY_CLR_TEXT); + + return (INT_PTR)NOTIFY_CLR_BG_BRUSH; + } + +#ifndef _APP_NO_DARKTHEME + case WM_SETTINGCHANGE: + case WM_SYSCOLORCHANGE: + { + _r_wnd_setdarktheme (hwnd); + break; + } +#endif // _APP_NO_DARKTHEME + + case WM_SETCURSOR: + { + const UINT ctrl_id = GetDlgCtrlID ((HWND)wparam); + + if ( + ctrl_id == IDC_MENU_BTN || + ctrl_id == IDC_RULES_BTN || + ctrl_id == IDC_TIMER_BTN || + ctrl_id == IDC_CLOSE_BTN || + ctrl_id == IDC_FILE_TEXT + ) + { + SetCursor (LoadCursor (nullptr, IDC_HAND)); + + SetWindowLongPtr (hwnd, DWLP_MSGRESULT, TRUE); + return TRUE; + } + + break; + } + + case WM_NOTIFY: + { + LPNMHDR nmlp = (LPNMHDR)lparam; + + switch (nmlp->code) + { + case TTN_GETDISPINFO: + { + LPNMTTDISPINFO lpnmdi = (LPNMTTDISPINFO)lparam; + + if ((lpnmdi->uFlags & TTF_IDISHWND) != 0) + { + WCHAR buffer[1024] = {0}; + const UINT ctrl_id = GetDlgCtrlID ((HWND)lpnmdi->hdr.idFrom); + + if ( + ctrl_id == IDC_MENU_BTN || + ctrl_id == IDC_RULES_BTN || + ctrl_id == IDC_TIMER_BTN || + ctrl_id == IDC_CLOSE_BTN || + ctrl_id == IDC_FILE_TEXT || + ctrl_id == IDC_SIGNATURE_TEXT || + ctrl_id == IDC_ADDRESS_LOCAL_TEXT || + ctrl_id == IDC_ADDRESS_REMOTE_TEXT || + ctrl_id == IDC_PROTOCOL_TEXT || + ctrl_id == IDC_FILTER_TEXT || + ctrl_id == IDC_DATE_TEXT + ) + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + + if (idx != LAST_VALUE) + { + PITEM_LOG const ptr_log = notifications.at (idx); + + if (ptr_log) + { + if (ctrl_id == IDC_RULES_BTN) + StringCchCopy (buffer, _countof (buffer), app.LocaleString (IDS_TRAY_RULES, nullptr)); + + else if (ctrl_id == IDC_TIMER_BTN) + StringCchCopy (buffer, _countof (buffer), app.LocaleString (IDS_TIMER, nullptr)); + + else if (ctrl_id == IDC_CLOSE_BTN) + StringCchCopy (buffer, _countof (buffer), app.LocaleString (IDS_CLOSE, nullptr)); + + else if (ctrl_id == IDC_FILE_TEXT) + StringCchCopy (buffer, _countof (buffer), _app_gettooltip (IDC_APPS_PROFILE, ptr_log->hash)); + + else + StringCchCopy (buffer, _countof (buffer), _r_ctrl_gettext (hwnd, ctrl_id)); + } + + if (buffer[0]) + lpnmdi->lpszText = buffer; + } + + _r_fastlock_releaseshared (&lock_notification); + } + } + + break; + } + } + + break; + } + + case WM_COMMAND: + { + if ((LOWORD (wparam) >= IDX_RULES_SPECIAL && LOWORD (wparam) <= IDX_RULES_SPECIAL + rules_arr.size ())) + { + size_t hash = 0; + + _r_fastlock_acquireexclusive (&lock_access); + + const size_t rule_idx = (LOWORD (wparam) - IDX_RULES_SPECIAL); + PITEM_RULE ptr_rule = rules_arr.at (rule_idx); + + if (ptr_rule) + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t notify_idx = _app_notifygetcurrent (hwnd); + + if (notify_idx != LAST_VALUE) + { + hash = notifications.at (notify_idx)->hash; + + //if (ptr_rule->is_forservices && (hash == config.ntoskrnl_hash || hash == config.svchost_hash)) + // continue; + + PITEM_APP ptr_app = _app_getapplication (hash); + + if (ptr_app) + { + const bool is_remove = ptr_rule->is_enabled && (ptr_rule->apps.find (hash) != ptr_rule->apps.end ()); + + if (is_remove) + { + ptr_rule->apps.erase (hash); + + if (ptr_rule->apps.empty ()) + _app_ruleenable (ptr_rule, false); + } + else + { + ptr_rule->apps[hash] = true; + + _app_ruleenable (ptr_rule, true); + } + } + } + + _r_fastlock_releaseshared (&lock_notification); + + MFILTER_RULES rules; + rules.push_back (ptr_rule); + + _wfp_create4filters (&rules, __LINE__); + } + + _r_fastlock_releaseexclusive (&lock_access); + + _r_fastlock_acquireexclusive (&lock_notification); + + _app_freenotify (hash, false); + + _r_fastlock_releaseexclusive (&lock_notification); + + _app_notifyrefresh (hwnd, true); + + _app_listviewsort (app.GetHWND (), IDC_APPS_PROFILE, -1, false); + _app_profile_save (app.GetHWND ()); + + _r_listview_redraw (app.GetHWND (), IDC_APPS_PROFILE); + + return FALSE; + } + else if ((LOWORD (wparam) >= IDX_TIMER && LOWORD (wparam) <= IDX_TIMER + timers.size ())) + { + const size_t idx = (LOWORD (wparam) - IDX_TIMER); + + _app_notifycommand (hwnd, IDC_ALLOW_BTN, idx); + + return FALSE; + } + else if ((LOWORD (wparam) >= IDX_NOTIFICATIONS && LOWORD (wparam) <= IDX_NOTIFICATIONS + timers.size ())) + { + const size_t idx = (LOWORD (wparam) - IDX_NOTIFICATIONS); + + _app_notifyshow (hwnd, idx, true, false); + + return FALSE; + } + + switch (LOWORD (wparam)) + { + case IDC_FILE_TEXT: + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + + if (idx != LAST_VALUE) + { + _app_showitem (app.GetHWND (), IDC_APPS_PROFILE, _app_getposition (app.GetHWND (), notifications.at (idx)->hash), -1); + + _r_wnd_toggle (app.GetHWND (), true); + } + + _r_fastlock_releaseshared (&lock_notification); + + break; + } + + case IDC_MENU_BTN: + case IDC_RULES_BTN: + case IDC_TIMER_BTN: + { + const HMENU hmenu = CreateMenu (); + const HMENU hsubmenu = CreateMenu (); + + const HWND hctrl = (HWND)lparam; + + AppendMenu (hmenu, MF_POPUP, (UINT_PTR)hsubmenu, L" "); + + if (LOWORD (wparam) == IDC_MENU_BTN) + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t current_idx = _app_notifygetcurrent (hwnd); + + for (size_t i = 0; i < notifications.size (); i++) + { + PITEM_LOG ptr_log = notifications.at (i); + + if (ptr_log) + { + AppendMenu (hsubmenu, MF_BYPOSITION, IDX_NOTIFICATIONS + UINT (i), _r_fmt (L"%s - %s", ptr_log->path ? _r_path_extractfile (ptr_log->path).GetString () : SZ_EMPTY, ptr_log->remote_fmt)); + + if (i == current_idx) + CheckMenuRadioItem (hsubmenu, IDX_NOTIFICATIONS, IDX_NOTIFICATIONS + UINT (i), IDX_NOTIFICATIONS + UINT (i), MF_BYCOMMAND); + } + } + + if (notifications.size () == 1) + EnableMenuItem (hsubmenu, IDX_NOTIFICATIONS, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + + _r_fastlock_releaseshared (&lock_notification); + } + else if (LOWORD (wparam) == IDC_RULES_BTN) + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + size_t hash = 0; + + if (idx != LAST_VALUE) + { + PITEM_LOG const ptr_log = notifications.at (idx); + + if (ptr_log) + hash = ptr_log->hash; + } + + _r_fastlock_releaseshared (&lock_notification); + + _r_fastlock_acquireshared (&lock_access); + + AppendMenu (hsubmenu, MF_STRING, IDM_DISABLENOTIFICATIONS, app.LocaleString (IDS_DISABLENOTIFICATIONS, nullptr)); + + _app_generate_rulesmenu (hsubmenu, hash); + + _r_fastlock_releaseshared (&lock_access); + } + else if (LOWORD (wparam) == IDC_TIMER_BTN) + { + for (size_t i = 0; i < timers.size (); i++) + AppendMenu (hsubmenu, MF_BYPOSITION, IDX_TIMER + UINT (i), _r_fmt_interval (timers.at (i) + 1, 1)); + } + RECT buttonRect = {0}; + + GetClientRect (hctrl, &buttonRect); + ClientToScreen (hctrl, (LPPOINT)&buttonRect); + + buttonRect.left -= app.GetDPI (2); + buttonRect.top += app.GetDPI (24); + + _r_wnd_adjustwindowrect (hctrl, &buttonRect); + + TrackPopupMenuEx (hsubmenu, TPM_RIGHTBUTTON | TPM_LEFTBUTTON, buttonRect.left, buttonRect.top, hwnd, nullptr); + + DestroyMenu (hsubmenu); + DestroyMenu (hmenu); + + break; + } + + case IDC_ALLOW_BTN: + case IDC_BLOCK_BTN: + case IDC_LATER_BTN: + case IDM_DISABLENOTIFICATIONS: + { + _app_notifycommand (hwnd, LOWORD (wparam), LAST_VALUE); + break; + } + + case IDM_EDITRULES: + { + _app_settab_id (hwnd, IDC_RULES_CUSTOM); + break; + } + + case IDM_OPENRULESEDITOR: + { + PITEM_RULE ptr_rule = new ITEM_RULE; + + if (ptr_rule) + { + _r_fastlock_acquireshared (&lock_notification); + + const size_t idx = _app_notifygetcurrent (hwnd); + size_t hash = 0; + + if (idx != LAST_VALUE) + { + PITEM_LOG const ptr_log = notifications.at (idx); + + if (ptr_log) + { + ptr_rule->apps[ptr_log->hash] = true; + ptr_rule->protocol = ptr_log->protocol; + hash = ptr_log->hash; + + LPWSTR rule = nullptr; + _app_formataddress (ptr_log, FWP_DIRECTION_OUTBOUND, ptr_log->remote_port, &rule, false); + + _r_str_alloc (&ptr_rule->pname, _r_str_length (rule), rule); + _r_str_alloc (&ptr_rule->prule_remote, _r_str_length (rule), rule); + + SAFE_DELETE_ARRAY (rule); + } + } + + _r_fastlock_releaseshared (&lock_notification); + + _app_ruleenable (ptr_rule, true); + + ptr_rule->type = TypeCustom; + ptr_rule->is_block = ((app.ConfigGet (L"Mode", ModeWhitelist).AsUint () == ModeWhitelist) ? false : true); + + if (DialogBoxParam (nullptr, MAKEINTRESOURCE (IDD_EDITOR), app.GetHWND (), &EditorProc, (LPARAM)ptr_rule)) + { + _r_fastlock_acquireexclusive (&lock_access); + + rules_arr.push_back (ptr_rule); + + MFILTER_RULES rules; + rules.push_back (ptr_rule); + + _wfp_create4filters (&rules, __LINE__); + + _r_fastlock_releaseexclusive (&lock_access); + + _app_listviewsort (hwnd, IDC_APPS_PROFILE, -1, false); + _app_profile_save (hwnd); + + _r_listview_redraw (hwnd, IDC_APPS_PROFILE); + + _r_fastlock_acquireexclusive (&lock_notification); + + _app_freenotify (hash, false); + + _r_fastlock_releaseexclusive (&lock_notification); + + _app_notifyrefresh (hwnd, true); + } + else + { + _app_freerule (&ptr_rule); + } + } + + break; + } + + case IDC_CLOSE_BTN: + { + _app_notifyhide (hwnd); + break; + } + } + + break; + } + } + + return DefWindowProc (hwnd, msg, wparam, lparam); +} diff --git a/src/notifications.hpp b/src/notifications.hpp new file mode 100644 index 00000000..636bc7c9 --- /dev/null +++ b/src/notifications.hpp @@ -0,0 +1,29 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +void _app_notifycreatewindow (); +bool _app_notifycommand (HWND hwnd, UINT ctrl_id, size_t timer_idx); + +bool _app_notifyadd (HWND hwnd, PITEM_LOG const ptr_log, PITEM_APP const ptr_app); +void _app_freenotify (size_t idx_orhash, bool is_idx); +size_t _app_notifygetcurrent (HWND hwnd); + +bool _app_notifyshow (HWND hwnd, size_t idx, bool is_forced, bool is_safety); +void _app_notifyhide (HWND hwnd); + +void _app_notifyplaysound (); + +bool _app_notifyrefresh (HWND hwnd, bool is_safety); + +void _app_notifysetpos (HWND hwnd); +bool _app_notifysettimeout (HWND hwnd, UINT_PTR timer_id, bool is_create, UINT timeout); + +void _app_notifysetnote (HWND hwnd, UINT ctrl_id, rstring str); +void _app_notifysettext (HDC hdc, HWND hwnd, UINT ctrl_id1, LPCWSTR text1, UINT ctrl_id2, LPCWSTR text2); + +HFONT _app_notifyinitfont (PLOGFONT plf, LONG height, LONG weight, LPCWSTR name, BOOL is_underline); + +void DrawFrameBorder (HDC hdc, HWND hwnd, COLORREF clr); +LRESULT CALLBACK NotificationProc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); diff --git a/src/profile.cpp b/src/profile.cpp new file mode 100644 index 00000000..3b5b3333 --- /dev/null +++ b/src/profile.cpp @@ -0,0 +1,1228 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +size_t _app_addapplication (HWND hwnd, rstring path, time_t timestamp, time_t timer, time_t last_notify, bool is_silent, bool is_enabled, bool is_fromdb) +{ + if (path.IsEmpty () || path.At (0) == 0 || PathIsDirectory (path)) + return 0; + + // if file is shortcut - get location + if (!is_fromdb) + { + if (_wcsnicmp (PathFindExtension (path), L".lnk", 4) == 0) + { + path = _app_getshortcutpath (hwnd, path); + + if (path.IsEmpty ()) + return 0; + } + } + + _app_resolvefilename (path); + + const size_t hash = path.Hash (); + + if (apps.find (hash) != apps.end ()) + return hash; // already exists + + ITEM_APP * ptr_app = &apps[hash]; // application pointer + + const bool is_ntoskrnl = (hash == config.ntoskrnl_hash); + const time_t current_time = _r_unixtime_now (); + + rstring real_path; + + if (_wcsnicmp (path, L"\\device\\", 8) == 0) // device path + { + real_path = path; + + ptr_app->type = AppDevice; + } + else if (_wcsnicmp (path, L"S-1-", 4) == 0) // windows store (win8+) + { + ptr_app->type = AppPackage; + + _app_item_get (AppPackage, hash, nullptr, &real_path, &ptr_app->psid, nullptr, nullptr); + } + else if (PathIsNetworkPath (path)) // network path + { + real_path = path; + + ptr_app->type = AppNetwork; + } + else + { + real_path = path; + + if (!is_ntoskrnl && real_path.Find (OBJ_NAME_PATH_SEPARATOR) == rstring::npos) + { + if (_app_item_get (AppService, hash, nullptr, &real_path, nullptr, &ptr_app->psd, nullptr)) + { + ptr_app->type = AppService; + } + else + { + ptr_app->type = AppPico; + } + } + else + { + ptr_app->type = AppRegular; + + if (is_ntoskrnl) // "system" process + { + path.At (0) = _r_str_upper (path.At (0)); // fix "System" lowercase + + real_path = _r_path_expand (PATH_NTOSKRNL); + } + } + } + + if (!real_path.IsEmpty ()) + { + const DWORD dwAttr = GetFileAttributes (real_path); + + ptr_app->is_temp = ((dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_TEMPORARY) != 0)) || (_wcsnicmp (real_path, config.tmp1_dir, config.tmp1_length) == 0); + ptr_app->is_system = !ptr_app->is_temp && (is_ntoskrnl || ((dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_SYSTEM) != 0)) || (_wcsnicmp (real_path, config.windows_dir, config.wd_length) == 0)); + + ptr_app->is_signed = _app_getsignatureinfo (hash, real_path, &ptr_app->signer); + } + + _app_applycasestyle (real_path.GetBuffer (), real_path.GetLength ()); // apply case-style + _app_applycasestyle (path.GetBuffer (), path.GetLength ()); // apply case-style + + _r_str_alloc (&ptr_app->original_path, path.GetLength (), path); + _r_str_alloc (&ptr_app->real_path, real_path.GetLength (), real_path); + + _app_getdisplayname (hash, ptr_app, &ptr_app->display_name); + _app_getappicon (ptr_app, true, &ptr_app->icon_id, nullptr); + + ptr_app->is_enabled = is_enabled; + ptr_app->is_silent = is_silent; + + ptr_app->timestamp = timestamp ? timestamp : current_time; + ptr_app->last_notify = last_notify; + + // install timer + if (timer) + { + if (timer > current_time) + { + if (!ptr_app->htimer && CreateTimerQueueTimer (&ptr_app->htimer, config.htimer, &_app_timer_callback, (PVOID)hash, DWORD ((timer - current_time) * _R_SECONDSCLOCK_MSEC), 0, WT_EXECUTEONLYONCE | WT_EXECUTEINTIMERTHREAD)) + { + ptr_app->is_enabled = true; + ptr_app->timer = timer; + } + } + else + { + ptr_app->is_enabled = false; + } + } + + if (hwnd) + { + UINT listview_id; + + if (ptr_app->type == AppService) + listview_id = IDC_APPS_SERVICE; + + else if (ptr_app->type == AppPackage) + listview_id = IDC_APPS_PACKAGE; + + else + listview_id = IDC_APPS_PROFILE; + + const size_t item = _r_listview_getitemcount (hwnd, listview_id); + + _r_fastlock_acquireshared (&lock_checkbox); + + _r_listview_additem (hwnd, listview_id, item, 0, ptr_app->display_name, ptr_app->icon_id, _app_getappgroup (hash, ptr_app), hash); + _r_listview_setitem (hwnd, listview_id, item, 1, _r_fmt_date (ptr_app->timestamp, FDTF_SHORTDATE | FDTF_SHORTTIME)); + + _r_listview_setitemcheck (hwnd, listview_id, item, is_enabled); + + _r_fastlock_releaseshared (&lock_checkbox); + } + + return hash; +} + +ITEM_APP * _app_getapplication (size_t hash) +{ + if (hash && apps.find (hash) != apps.end ()) + return &apps.at (hash); + + return nullptr; +} + +PITEM_RULE _app_getrule (size_t hash, EnumRuleType type, BOOL is_readonly) +{ + if (!hash) + return nullptr; + + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE ptr_rule = rules_arr.at (i); + + if (ptr_rule && ptr_rule->type == type) + { + if (is_readonly != -1 && ((BOOL)ptr_rule->is_readonly != (is_readonly))) + continue; + + if (ptr_rule->pname && _r_str_hash (ptr_rule->pname) == hash) + return ptr_rule; + } + } + + return nullptr; +} + +bool _app_freeapplication (size_t hash) +{ + bool is_enabled = false; + + PITEM_APP ptr_app = _app_getapplication (hash); + + if (ptr_app) + { + is_enabled = ptr_app->is_enabled; + + SAFE_DELETE_ARRAY (ptr_app->display_name); + SAFE_DELETE_ARRAY (ptr_app->real_path); + SAFE_DELETE_ARRAY (ptr_app->original_path); + + SAFE_DELETE_ARRAY (ptr_app->description); + SAFE_DELETE_ARRAY (ptr_app->signer); + + SAFE_DELETE_ARRAY (ptr_app->psid); + SAFE_DELETE_ARRAY (ptr_app->psd); + } + + if (hash) + { + _r_fastlock_acquireexclusive (&lock_cache); + + if (cache_signatures.find (hash) != cache_signatures.end ()) + { + SAFE_DELETE_ARRAY (cache_signatures[hash]); + + cache_signatures.erase (hash); + } + + if (cache_versions.find (hash) != cache_versions.end ()) + { + SAFE_DELETE_ARRAY (cache_versions[hash]); + + cache_versions.erase (hash); + } + + _r_fastlock_releaseexclusive (&lock_cache); + + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE ptr_rule = rules_arr.at (i); + + if (ptr_rule) + { + if (ptr_rule->type != TypeCustom) + continue; + + if (ptr_rule->apps.find (hash) != ptr_rule->apps.end ()) + { + ptr_rule->apps.erase (hash); + + if (!is_enabled && ptr_rule->is_enabled) + is_enabled = true; + + if (ptr_rule->is_enabled && ptr_rule->apps.empty ()) + { + ptr_rule->is_enabled = false; + ptr_rule->is_haveerrors = false; + } + } + } + } + + _r_fastlock_acquireexclusive (&lock_notification); + _app_freenotify (hash, false); + _r_fastlock_releaseexclusive (&lock_notification); + + if (ptr_app->htimer) + DeleteTimerQueueTimer (config.htimer, ptr_app->htimer, nullptr); + + _app_notifyrefresh (config.hnotification, false); + + apps.erase (hash); + } + + return is_enabled; +} + +void _app_freerule (PITEM_RULE * ptr) +{ + if (ptr) + { + PITEM_RULE ptr_rule = *ptr; + + SAFE_DELETE (ptr_rule); + + *ptr = nullptr; + } +} + +void _app_getcount (PITEM_STATUS ptr_status) +{ + if (!ptr_status) + return; + + for (auto const &p : apps) + { + const ITEM_APP *ptr_app = &p.second; + const bool is_used = _app_isappused (ptr_app, p.first); + + if (_app_istimeractive (ptr_app)) + ptr_status->timers_count += 1; + + if (!ptr_app->is_undeletable && (!_app_isappexists (ptr_app) || !is_used) && !(ptr_app->type == AppService || ptr_app->type == AppPackage)) + ptr_status->unused_count += 1; + + if (is_used) + ptr_status->total_count += 1; + } +} + +size_t _app_getappgroup (size_t hash, PITEM_APP const ptr_app) +{ + // if(!app.ConfigGet (L"IsEnableGroups", false).AsBool ()) + // return LAST_VALUE; + + if (!ptr_app) + return 2; + + // apps with special rule + if (app.ConfigGet (L"IsEnableSpecialGroup", true).AsBool () && _app_isapphaverule (hash)) + return 1; + + return ptr_app->is_enabled ? 0 : 2; +} + +size_t _app_getrulegroup (PITEM_RULE const ptr_rule) +{ + // if(!app.ConfigGet (L"IsEnableGroups", false).AsBool ()) + // return LAST_VALUE; + + if (!ptr_rule || !ptr_rule->is_enabled) + return 2; + + if (app.ConfigGet (L"IsEnableSpecialGroup", true).AsBool () && (ptr_rule->is_forservices || !ptr_rule->apps.empty ())) + return 1; + + return 0; +} + +size_t _app_getruleicon (PITEM_RULE const ptr_rule) +{ + if (!ptr_rule) + return 0; + + return ptr_rule->is_block ? 1 : 0; +} + +rstring _app_gettooltip (UINT listview_id, size_t idx) +{ + rstring result; + + _r_fastlock_acquireshared (&lock_access); + + if ( + listview_id == IDC_APPS_LV || + listview_id == IDC_APPS_PROFILE || + listview_id == IDC_APPS_SERVICE || + listview_id == IDC_APPS_PACKAGE + ) + { + PITEM_APP ptr_app = _app_getapplication (idx); + + if (ptr_app) + { + result = (ptr_app->real_path && ptr_app->real_path[0]) ? ptr_app->real_path : ((ptr_app->display_name && ptr_app->display_name[0]) ? ptr_app->display_name : ptr_app->original_path); + + // file information + if (ptr_app->type == AppRegular) + { + rstring display_name; + + if (_app_getversioninfo (idx, ptr_app->real_path, &ptr_app->description)) + { + display_name = ptr_app->description; + + if (!display_name.IsEmpty ()) + result.AppendFormat (L"\r\n%s:\r\n" SZ_TAB L"%s" SZ_TAB, app.LocaleString (IDS_FILE, nullptr).GetString (), display_name.GetString ()); + } + } + else if (ptr_app->type == AppService) + { + rstring display_name; + + if (_app_item_get (ptr_app->type, idx, &display_name, nullptr, nullptr, nullptr, nullptr)) + result.AppendFormat (L"\r\n%s:\r\n" SZ_TAB L"%s\r\n" SZ_TAB L"%s" SZ_TAB, app.LocaleString (IDS_FILE, nullptr).GetString (), ptr_app->original_path, display_name.GetString ()); + } + else if (ptr_app->type == AppPackage) + { + rstring display_name; + + if (_app_item_get (ptr_app->type, idx, &display_name, nullptr, nullptr, nullptr, nullptr)) + result.AppendFormat (L"\r\n%s:\r\n" SZ_TAB L"%s" SZ_TAB, app.LocaleString (IDS_FILE, nullptr).GetString (), display_name.GetString ()); + } + + // signature information + if (app.ConfigGet (L"IsCerificatesEnabled", false).AsBool () && ptr_app->is_signed && ptr_app->signer) + result.AppendFormat (L"\r\n%s:\r\n" SZ_TAB L"%s", app.LocaleString (IDS_SIGNATURE, nullptr).GetString (), ptr_app->signer); + + // timer information + if (_app_istimeractive (ptr_app)) + result.AppendFormat (L"\r\n%s:\r\n" SZ_TAB L"%s", app.LocaleString (IDS_TIMELEFT, nullptr).GetString (), _r_fmt_interval (ptr_app->timer - _r_unixtime_now (), 3).GetString ()); + + // notes + { + rstring buffer; + + if (!_app_isappexists (ptr_app)) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_INVALID, nullptr).GetString ()); + + if (ptr_app->is_silent) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_SILENT, nullptr).GetString ()); + + if (ptr_app->is_system) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_SYSTEM, nullptr).GetString ()); + + // app type + { + if (ptr_app->type == AppNetwork) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_NETWORK, nullptr).GetString ()); + + else if (ptr_app->type == AppPico) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_PICO, nullptr).GetString ()); + + else if (ptr_app->type == AppPackage) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_PACKAGE, nullptr).GetString ()); + + else if (ptr_app->type == AppService) + buffer.AppendFormat (SZ_TAB L"%s\r\n", app.LocaleString (IDS_HIGHLIGHT_SERVICE, nullptr).GetString ()); + } + + if (!buffer.IsEmpty ()) + { + buffer.InsertFormat (0, L"\r\n%s:\r\n", app.LocaleString (IDS_NOTES, nullptr).GetString ()); + result.Append (buffer); + } + } + } + } + else if ( + listview_id == IDC_RULES_BLOCKLIST || + listview_id == IDC_RULES_SYSTEM || + listview_id == IDC_RULES_CUSTOM + ) + { + const PITEM_RULE ptr_rule = rules_arr.at (idx); + + if (ptr_rule) + { + rstring rule_remote = ptr_rule->prule_remote; + rstring rule_local = ptr_rule->prule_local; + + rule_remote = rule_remote.IsEmpty () ? app.LocaleString (IDS_STATUS_EMPTY, nullptr) : rule_remote.Replace (RULE_DELIMETER, L"\r\n" SZ_TAB); + rule_local = rule_local.IsEmpty () ? app.LocaleString (IDS_STATUS_EMPTY, nullptr) : rule_local.Replace (RULE_DELIMETER, L"\r\n" SZ_TAB); + + result.Format (L"%s (#%d)\r\n%s:\r\n%s%s\r\n%s:\r\n%s%s", ptr_rule->pname, idx, app.LocaleString (IDS_RULE, L" (" SZ_LOG_REMOTE_ADDRESS L")").GetString (), SZ_TAB, rule_remote.GetString (), app.LocaleString (IDS_RULE, L" (" SZ_LOG_LOCAL_ADDRESS L")").GetString (), SZ_TAB, rule_local.GetString ()); + + if (ptr_rule->is_forservices || !ptr_rule->apps.empty ()) + result.AppendFormat (L"\r\n%s:\r\n%s%s", app.LocaleString (IDS_FILEPATH, nullptr).GetString (), SZ_TAB, _app_rulesexpand (ptr_rule, true, L"\r\n" SZ_TAB).GetString ()); + } + } + + _r_fastlock_releaseshared (&lock_access); + + return result; +} + +void _app_setruleitem (HWND hwnd, UINT listview_id, size_t item, PITEM_RULE ptr_rule) +{ + _r_listview_setitem (hwnd, listview_id, item, 0, ptr_rule->type == TypeCustom && ptr_rule->is_readonly ? _r_fmt (L"%s*", ptr_rule->pname) : ptr_rule->pname, _app_getruleicon (ptr_rule), _app_getrulegroup (ptr_rule)); + _r_listview_setitem (hwnd, listview_id, item, 1, app.LocaleString (IDS_DIRECTION_1 + ptr_rule->dir, nullptr)); + _r_listview_setitem (hwnd, listview_id, item, 2, ptr_rule->protocol ? _app_getprotoname (ptr_rule->protocol) : app.LocaleString (IDS_ALL, nullptr)); + + _r_listview_setitemcheck (hwnd, listview_id, item, ptr_rule->is_enabled); +} + +bool _app_ruleenable (PITEM_RULE ptr_rule, bool is_enable) +{ + if (!ptr_rule) + return false; + + ptr_rule->is_enabled = is_enable; + + if (ptr_rule->is_readonly && ptr_rule->pname) + { + const size_t hash = _r_str_hash (ptr_rule->pname); + + if (rules_config.find (hash) != rules_config.end ()) + rules_config[hash]->is_enabled = is_enable; + } + + return true; +} + +rstring _app_rulesexpand (PITEM_RULE const ptr_rule, bool is_forservices, LPCWSTR delimeter) +{ + rstring result; + + if (ptr_rule) + { + if (is_forservices && ptr_rule->is_forservices) + { + rstring svchost_path = _r_path_expand (PATH_SVCHOST); + + _app_applycasestyle (svchost_path.GetBuffer (), svchost_path.GetLength ()); + svchost_path.ReleaseBuffer (); + + result.AppendFormat (L"%s%s", PROC_SYSTEM_NAME, delimeter); + result.AppendFormat (L"%s%s", svchost_path.GetString (), delimeter); + } + + for (auto const &p : ptr_rule->apps) + { + ITEM_APP const *ptr_app = _app_getapplication (p.first); + + if (ptr_app) + { + if (ptr_app->type == AppPackage || ptr_app->type == AppService) + { + if (ptr_app->display_name && ptr_app->display_name[0]) + result.Append (ptr_app->display_name); + } + else + { + if (ptr_app->original_path && ptr_app->original_path[0]) + result.Append (ptr_app->original_path); + } + + result.Append (delimeter); + } + } + + result.Trim (delimeter); + } + + return result; +} + +bool _app_isapphaverule (size_t hash) +{ + if (!hash) + return false; + + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE const ptr_rule = rules_arr.at (i); + + if (ptr_rule && ptr_rule->is_enabled && ptr_rule->apps.find (hash) != ptr_rule->apps.end ()) + return true; + } + + return false; +} + +bool _app_isappused (ITEM_APP const *ptr_app, size_t hash) +{ + if (ptr_app && (ptr_app->is_enabled || ptr_app->is_silent || _app_isapphaverule (hash))) + return true; + + return false; +} + +bool _app_isappexists (ITEM_APP const *ptr_app) +{ + if (!ptr_app) + return false; + + if (ptr_app->is_undeletable) + return true; + + if (ptr_app->is_enabled && ptr_app->is_haveerrors) + return false; + + if (ptr_app->type == AppRegular) + return ptr_app->real_path && ptr_app->real_path[0] && _r_fs_exists (ptr_app->real_path); + + else if (ptr_app->type == AppService || ptr_app->type == AppPackage) + return _app_item_get (ptr_app->type, _r_str_hash (ptr_app->original_path), nullptr, nullptr, nullptr, nullptr, nullptr); + + return true; +} + +bool _app_isrulehost (LPCWSTR rule) +{ + if (!rule || !rule[0]) + return false; + + NET_ADDRESS_INFO ni; + SecureZeroMemory (&ni, sizeof (ni)); + + USHORT port = 0; + BYTE prefix_length = 0; + + static const DWORD types = NET_STRING_NAMED_ADDRESS | NET_STRING_NAMED_SERVICE; + const DWORD errcode = ParseNetworkString (rule, types, &ni, &port, &prefix_length); + + return (errcode == ERROR_SUCCESS); +} + +bool _app_isruleip (LPCWSTR rule) +{ + if (!rule || !rule[0]) + return false; + + NET_ADDRESS_INFO ni; + SecureZeroMemory (&ni, sizeof (ni)); + + USHORT port = 0; + BYTE prefix_length = 0; + + static const DWORD types = NET_STRING_IP_ADDRESS | NET_STRING_IP_SERVICE | NET_STRING_IP_NETWORK | NET_STRING_IP_ADDRESS_NO_SCOPE | NET_STRING_IP_SERVICE_NO_SCOPE; + const DWORD errcode = ParseNetworkString (rule, types, &ni, &port, &prefix_length); + + return (errcode == ERROR_SUCCESS); +} + +bool _app_isruleport (LPCWSTR rule) +{ + if (!rule || !rule[0]) + return false; + + const size_t length = _r_str_length (rule); + + for (size_t i = 0; i < length; i++) + { + if (iswdigit (rule[i]) == 0 && rule[i] != RULE_RANGE_CHAR) + return false; + } + + return true; +} + +bool _app_isrulesexists (EnumRuleType type, BOOL is_readonly, BOOL is_global) +{ + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE ptr_rule = rules_arr.at (i); + + if (ptr_rule && ptr_rule->type == type) + { + if (is_readonly != -1 && ((BOOL)ptr_rule->is_readonly != is_readonly)) + continue; + + if (is_global != -1 && ((ptr_rule->is_enabled && !ptr_rule->apps.empty ()) != is_global)) + continue; + + return true; + } + } + + return false; +} + +bool _app_isrulepresent (size_t hash) +{ + if (!hash) + return false; + + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE ptr_rule = rules_arr.at (i); + + if (ptr_rule && ptr_rule->is_readonly && ptr_rule->pname && _r_str_hash (ptr_rule->pname) == hash) + return true; + } + + return false; +} + +void _app_profile_loadrules (HWND hwnd, LPCWSTR path, LPCWSTR path_backup, bool is_internal, MFILTER_RULES * ptr_rules, EnumRuleType type, UINT8 weight, time_t * ptimestamp) +{ + if (!ptr_rules) + return; + + pugi::xml_document doc_original; + pugi::xml_document doc_backup; + + pugi::xml_node root; + pugi::xml_parse_result result_original = doc_original.load_file (path, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + // if file not found or parsing error, load from backup + if (path_backup) + { + if (is_internal) + { + DWORD size = 0; + const LPVOID buffer = _app_loadresource (path_backup, &size); + + pugi::xml_parse_result result_backup; + + if (buffer) + result_backup = doc_backup.load_buffer (buffer, size, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + if (result_backup && (!result_original || doc_backup.child (L"root").attribute (L"timestamp").as_llong () > doc_original.child (L"root").attribute (L"timestamp").as_llong ())) + { + root = doc_backup.child (L"root"); + result_original = result_backup; + } + } + else + { + if (!result_original && _r_fs_exists (path_backup)) + result_original = doc_original.load_file (path_backup, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + } + } + + // show only syntax, memory and i/o errors... + if (!result_original && result_original.status != pugi::status_file_not_found) + _app_logerror (L"pugi::load_file", 0, _r_fmt (L"status: %d,offset: %d,text: %s,file: %s", result_original.status, result_original.offset, rstring (result_original.description ()).GetString (), path), false); + + if (result_original) + { + if (!root) + root = doc_original.child (L"root"); + + if (root) + { + if (root.attribute (L"type").empty () || root.attribute (L"type").as_uint () == XmlRules) + { + if (ptimestamp) + *ptimestamp = root.attribute (L"timestamp").as_llong (); + + for (pugi::xml_node item = root.child (L"item"); item; item = item.next_sibling (L"item")) + { + PITEM_RULE ptr_rule = new ITEM_RULE; + + if (ptr_rule) + { + const size_t hash = _r_str_hash (item.attribute (L"name").as_string ()); + PITEM_RULE_CONFIG ptr_config = nullptr; + + // allocate required memory + { + const rstring attr_name = item.attribute (L"name").as_string (); + const rstring attr_rule_remote = item.attribute (L"rule").as_string (); + const rstring attr_rule_local = item.attribute (L"rule_local").as_string (); + + const size_t name_length = min (attr_name.GetLength (), RULE_NAME_CCH_MAX); + const size_t rule_remote_length = min (attr_rule_remote.GetLength (), RULE_RULE_CCH_MAX); + const size_t rule_local_length = min (attr_rule_local.GetLength (), RULE_RULE_CCH_MAX); + + _r_str_alloc (&ptr_rule->pname, name_length, attr_name); + _r_str_alloc (&ptr_rule->prule_remote, rule_remote_length, attr_rule_remote); + _r_str_alloc (&ptr_rule->prule_local, rule_local_length, attr_rule_local); + } + + ptr_rule->dir = (FWP_DIRECTION)item.attribute (L"dir").as_uint (); + ptr_rule->protocol = (UINT8)item.attribute (L"protocol").as_uint (); + ptr_rule->af = (ADDRESS_FAMILY)item.attribute (L"version").as_uint (); + + ptr_rule->type = item.attribute (L"is_custom").as_bool () ? TypeCustom : type; + ptr_rule->is_block = item.attribute (L"is_block").as_bool (); + ptr_rule->is_forservices = item.attribute (L"is_services").as_bool (); + ptr_rule->is_readonly = (type != TypeCustom); + + ptr_rule->is_enabled = item.attribute (L"is_enabled").as_bool (); + + // calculate rule weight + { + if (type == TypeCustom && item.attribute (L"is_block").as_bool ()) + ptr_rule->weight = FILTER_WEIGHT_CUSTOM_BLOCK; + + else + ptr_rule->weight = weight; + } + + if (is_internal) + { + // internal rules + if (rules_config.find (hash) != rules_config.end ()) + { + ptr_config = rules_config[hash]; + + if (ptr_config) + ptr_rule->is_enabled = ptr_config->is_enabled; + } + else + { + ptr_config = new ITEM_RULE_CONFIG; + + if (ptr_config) + { + ptr_config->is_enabled = ptr_rule->is_enabled; + + _r_str_alloc (&ptr_config->pname, _r_str_length (ptr_rule->pname), ptr_rule->pname); + + rules_config[hash] = ptr_config; + } + } + } + + // load apps + { + rstring apps_rule = item.attribute (L"apps").as_string (); + + if (is_internal && ptr_config && ptr_config->papps) + { + if (apps_rule.IsEmpty ()) + apps_rule = ptr_config->papps; + + else + apps_rule.AppendFormat (L"%s%s", RULE_DELIMETER, ptr_config->papps); + } + + if (!apps_rule.IsEmpty ()) + { + rstring::rvector arr = apps_rule.AsVector (RULE_DELIMETER); + + for (size_t i = 0; i < arr.size (); i++) + { + const rstring app_path = _r_path_expand (arr.at (i).Trim (L"\r\n ")); + size_t app_hash = app_path.Hash (); + + if (app_hash) + { + if (item.attribute (L"is_services").as_bool ()) + { + if (app_hash == config.ntoskrnl_hash || app_hash == config.svchost_hash) + continue; + } + + if (!_app_getapplication (app_hash)) + app_hash = _app_addapplication (hwnd, app_path, 0, 0, 0, false, false, true); + + if (ptr_rule->type == TypeBlocklist || ptr_rule->type == TypeSystem) + apps[app_hash].is_undeletable = true; + + ptr_rule->apps[app_hash] = true; + } + } + } + } + + ptr_rules->push_back (ptr_rule); + + if (hwnd) + { + UINT listview_id; + + if (ptr_rule->type == TypeBlocklist) + listview_id = IDC_RULES_BLOCKLIST; + + else if (ptr_rule->type == TypeSystem) + listview_id = IDC_RULES_SYSTEM; + + else if (ptr_rule->type == TypeCustom) + listview_id = IDC_RULES_CUSTOM; + + else + continue; + + const size_t lv_item = _r_listview_getitemcount (hwnd, listview_id); + + _r_fastlock_acquireshared (&lock_checkbox); + + _r_listview_additem (hwnd, listview_id, lv_item, 0, ptr_rule->pname, _app_getruleicon (ptr_rule), _app_getrulegroup (ptr_rule), ptr_rules->size () - 1); + _app_setruleitem (hwnd, listview_id, lv_item, ptr_rule); + + _r_fastlock_releaseshared (&lock_checkbox); + } + } + } + } + } + } +} + +void _app_profile_load (HWND hwnd, LPCWSTR path_apps, LPCWSTR path_rules) +{ + // load applications + { + const UINT listview_id = _app_gettab_id (hwnd, LAST_VALUE); + const size_t item_id = (size_t)SendDlgItemMessage (hwnd, listview_id, LVM_GETNEXTITEM, (WPARAM)-1, LVNI_SELECTED); + const INT scroll_pos = GetScrollPos (GetDlgItem (hwnd, listview_id), SB_VERT); + bool is_meadded = false; + + _app_freearray (&items); + + // generate package list (win8+) + if (_r_sys_validversion (6, 2)) + _app_generate_packages (); + + // generate services list + _app_generate_services (); + + _r_fastlock_acquireexclusive (&lock_access); + + if (hwnd) + { + for (size_t i = 0; i < (size_t)SendDlgItemMessage (hwnd, IDC_TAB, TCM_GETITEMCOUNT, 0, 0); i++) + _r_listview_deleteallitems (hwnd, _app_gettab_id (hwnd, i)); + } + + // load apps list + { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file (path_apps ? path_apps : config.apps_path, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + // load backup + if (!result) + { + if (_r_fs_exists (config.apps_path_backup)) + result = doc.load_file (config.apps_path_backup, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + } + + if (!result) + { + // show only syntax, memory and i/o errors... + if (result.status != pugi::status_file_not_found) + _app_logerror (L"pugi::load_file", 0, _r_fmt (L"status: %d,offset: %d,text: %s,file: %s", result.status, result.offset, rstring (result.description ()).GetString (), path_apps ? path_apps : config.apps_path), false); + } + else + { + pugi::xml_node root = doc.child (L"root"); + + if (root) + { + if (root.attribute (L"type").empty () || root.attribute (L"type").as_uint () == XmlApps) + { + apps.clear (); + + for (pugi::xml_node item = root.child (L"item"); item; item = item.next_sibling (L"item")) + { + const size_t hash = _r_str_hash (item.attribute (L"path").as_string ()); + + if (hash == config.myhash) + is_meadded = true; + + _app_addapplication (hwnd, item.attribute (L"path").as_string (), item.attribute (L"timestamp").as_llong (), item.attribute (L"timer").as_llong (), item.attribute (L"last_notify").as_llong (), item.attribute (L"is_silent").as_bool (), item.attribute (L"is_enabled").as_bool (), true); + } + } + } + } + + if (hwnd) + { + _app_listviewsort (hwnd, listview_id, -1, false); + _r_listview_redraw (hwnd, listview_id); + } + } + + if (!is_meadded) + _app_addapplication (hwnd, app.GetBinaryPath (), 0, 0, 0, false, (app.ConfigGet (L"Mode", ModeWhitelist).AsUint () == ModeWhitelist) ? true : false, true); + + // disable deletion for this shit ;) + if (!_app_getapplication (config.ntoskrnl_hash)) + _app_addapplication (hwnd, PROC_SYSTEM_NAME, 0, 0, 0, false, false, true); + + if (!_app_getapplication (config.svchost_hash)) + _app_addapplication (hwnd, _r_path_expand (PATH_SVCHOST), 0, 0, 0, false, false, true); + + apps[config.myhash].is_undeletable = true; + apps[config.ntoskrnl_hash].is_undeletable = true; + apps[config.svchost_hash].is_undeletable = true; + + for (size_t i = 0; i < items.size (); i++) + { + PITEM_ADD ptr_item = items.at (i); + + if (ptr_item) + _app_addapplication (hwnd, ptr_item->type == AppService ? ptr_item->service_name : ptr_item->sid, ptr_item->timestamp, 0, 0, false, false, true); + } + + if (hwnd) + _app_showitem (hwnd, listview_id, item_id, scroll_pos); + } + + // load rules config + { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file (config.rules_config_path, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + if (!result && _r_fs_exists (config.rules_config_path_backup)) + result = doc.load_file (config.rules_config_path_backup, PUGIXML_LOAD_FLAGS, PUGIXML_LOAD_ENCODING); + + if (!result) + { + // show only syntax, memory and i/o errors... + if (result.status != pugi::status_file_not_found) + _app_logerror (L"pugi::load_file", 0, _r_fmt (L"status: %d,offset: %d,text: %s,file: %s", result.status, result.offset, rstring (result.description ()).GetString (), config.rules_config_path), false); + } + else + { + pugi::xml_node root = doc.child (L"root"); + + if (root) + { + if (root.attribute (L"type").empty () || root.attribute (L"type").as_uint () == XmlRulesConfig) + { + rules_config.clear (); + + for (pugi::xml_node item = root.child (L"item"); item; item = item.next_sibling (L"item")) + { + if (!item.attribute (L"name").empty ()) + { + const size_t hash = _r_str_hash (item.attribute (L"name").as_string ()); + + if (rules_config.find (hash) == rules_config.end ()) + { + PITEM_RULE_CONFIG ptr_config = new ITEM_RULE_CONFIG; + + ptr_config->is_enabled = item.attribute (L"is_enabled").as_bool (); + + const rstring attr_name = item.attribute (L"name").as_string (); + const rstring attr_apps = item.attribute (L"apps").as_string (); + + _r_str_alloc (&ptr_config->pname, attr_name.GetLength (), attr_name); + _r_str_alloc (&ptr_config->papps, attr_apps.GetLength (), attr_apps); + + rules_config[hash] = ptr_config; + } + } + } + } + } + } + } + + // clear old entries + { + for (size_t i = 0; i < rules_arr.size (); i++) + _app_freerule (&rules_arr.at (i)); + + rules_arr.clear (); + } + + // load blocklist rules (internal) + _app_profile_loadrules (hwnd, config.rules_blocklist_path, MAKEINTRESOURCE (IDR_RULES_BLOCKLIST), true, &rules_arr, TypeBlocklist, FILTER_WEIGHT_BLOCKLIST, &config.blocklist_timestamp); + + // load system rules (internal) + _app_profile_loadrules (hwnd, config.rules_system_path, MAKEINTRESOURCE (IDR_RULES_SYSTEM), true, &rules_arr, TypeSystem, FILTER_WEIGHT_SYSTEM, &config.rule_system_timestamp); + + // load custom rules + _app_profile_loadrules (hwnd, path_rules ? path_rules : config.rules_custom_path, config.rules_custom_path_backup, false, &rules_arr, TypeCustom, FILTER_WEIGHT_CUSTOM, nullptr); + + _r_fastlock_releaseexclusive (&lock_access); + + if (hwnd) + _app_refreshstatus (hwnd); +} + +void _app_profile_save (HWND /*hwnd*/, LPCWSTR path_apps, LPCWSTR path_rules) +{ + const time_t current_time = _r_unixtime_now (); + const bool is_backuprequired = app.ConfigGet (L"IsBackupProfile", true).AsBool () && (((current_time - app.ConfigGet (L"BackupTimestamp", 0).AsLonglong ()) >= app.ConfigGet (L"BackupPeriod", _R_SECONDSCLOCK_HOUR (BACKUP_HOURS_PERIOD)).AsLonglong ()) || !_r_fs_exists (config.apps_path_backup) || !_r_fs_exists (config.rules_custom_path_backup) || !_r_fs_exists (config.rules_config_path_backup)); + + bool is_backupcreated = false; + + _r_fastlock_acquireshared (&lock_access); + + // save apps + { + pugi::xml_document doc; + pugi::xml_node root = doc.append_child (L"root"); + + if (root) + { + root.append_attribute (L"timestamp").set_value (current_time); + root.append_attribute (L"type").set_value (XmlApps); + + for (auto &p : apps) + { + PITEM_APP const ptr_app = &p.second; + + if (!ptr_app->original_path || !_app_isappused (ptr_app, p.first)) + continue; + + pugi::xml_node item = root.append_child (L"item"); + + if (item) + { + item.append_attribute (L"path").set_value (ptr_app->original_path); + + if (ptr_app->timestamp) + item.append_attribute (L"timestamp").set_value (ptr_app->timestamp); + + // set timer (if presented) + if (_app_istimeractive (ptr_app)) + item.append_attribute (L"timer").set_value (ptr_app->timer); + + // set last notification timestamp (if presented) + if (ptr_app->last_notify) + item.append_attribute (L"last_notify").set_value (ptr_app->last_notify); + + if (ptr_app->is_silent) + item.append_attribute (L"is_silent").set_value (ptr_app->is_silent); + + if (ptr_app->is_enabled) + item.append_attribute (L"is_enabled").set_value (ptr_app->is_enabled); + } + } + + doc.save_file (path_apps ? path_apps : config.apps_path, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + + // make backup + if (!path_apps && !apps.empty () && is_backuprequired) + { + doc.save_file (config.apps_path_backup, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + is_backupcreated = true; + } + } + } + + // save internal rules config + { + pugi::xml_document doc; + pugi::xml_node root = doc.append_child (L"root"); + + if (root) + { + root.append_attribute (L"timestamp").set_value (current_time); + root.append_attribute (L"type").set_value (XmlRulesConfig); + + for (auto const &p : rules_config) + { + const size_t hash = _r_str_hash (p.second->pname); + + if (!_app_isrulepresent (hash)) + continue; + + pugi::xml_node item = root.append_child (L"item"); + + if (item) + { + item.append_attribute (L"name").set_value (p.second->pname); + + // save apps + const PITEM_RULE ptr_rule = _app_getrule (hash, TypeCustom, true); + + if (ptr_rule && !ptr_rule->apps.empty ()) + { + const rstring rule_apps = _app_rulesexpand (ptr_rule, false, L";"); + + if (!rule_apps.IsEmpty ()) + item.append_attribute (L"apps").set_value (rule_apps); + } + + if (p.second->is_enabled) + item.append_attribute (L"is_enabled").set_value (p.second->is_enabled); + } + } + + doc.save_file (config.rules_config_path, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + + // make backup + if (!rules_config.empty () && is_backuprequired) + { + doc.save_file (config.rules_config_path_backup, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + is_backupcreated = true; + } + } + } + + // save custom rules + { + pugi::xml_document doc; + pugi::xml_node root = doc.append_child (L"root"); + + if (root) + { + root.append_attribute (L"timestamp").set_value (current_time); + root.append_attribute (L"type").set_value (XmlRules); + + size_t rules_count = 0; + + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE const ptr_rule = rules_arr.at (i); + + if (!ptr_rule || ptr_rule->is_readonly) + continue; + + pugi::xml_node item = root.append_child (L"item"); + + if (item) + { + item.append_attribute (L"name").set_value (ptr_rule->pname); + + if (ptr_rule->prule_remote && ptr_rule->prule_remote[0]) + item.append_attribute (L"rule").set_value (ptr_rule->prule_remote); + + if (ptr_rule->prule_local && ptr_rule->prule_local[0]) + item.append_attribute (L"rule_local").set_value (ptr_rule->prule_local); + + if (ptr_rule->dir != FWP_DIRECTION_OUTBOUND) + item.append_attribute (L"dir").set_value (ptr_rule->dir); + + if (ptr_rule->protocol != 0) + item.append_attribute (L"protocol").set_value (ptr_rule->protocol); + + if (ptr_rule->af != AF_UNSPEC) + item.append_attribute (L"version").set_value (ptr_rule->af); + + // add apps attribute + if (!ptr_rule->apps.empty ()) + { + rstring arr; + bool is_haveapps = false; + + for (auto const &p : ptr_rule->apps) + { + PITEM_APP const ptr_app = _app_getapplication (p.first); + + if (ptr_app) + { + arr.Append (_r_path_unexpand (ptr_app->original_path)); + arr.Append (RULE_DELIMETER); + + if (!is_haveapps) + is_haveapps = true; + } + } + + if (is_haveapps) + item.append_attribute (L"apps").set_value (arr.Trim (RULE_DELIMETER)); + } + + if (ptr_rule->is_block) + item.append_attribute (L"is_block").set_value (ptr_rule->is_block); + + if (ptr_rule->is_enabled) + item.append_attribute (L"is_enabled").set_value (ptr_rule->is_enabled); + + rules_count += 1; + } + } + + doc.save_file (path_rules ? path_rules : config.rules_custom_path, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + + // make backup + if (is_backupcreated || (!path_rules && rules_count && is_backuprequired)) + { + doc.save_file (config.rules_custom_path_backup, L"\t", PUGIXML_SAVE_FLAGS, PUGIXML_SAVE_ENCODING); + is_backupcreated = true; + } + } + } + + _r_fastlock_releaseshared (&lock_access); + + if (is_backupcreated) + app.ConfigSet (L"BackupTimestamp", current_time); +} diff --git a/src/profile.hpp b/src/profile.hpp new file mode 100644 index 00000000..96f17aa6 --- /dev/null +++ b/src/profile.hpp @@ -0,0 +1,40 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +size_t _app_addapplication (HWND hwnd, rstring path, time_t timestamp, time_t timer, time_t last_notify, bool is_silent, bool is_enabled, bool is_fromdb); + +ITEM_APP* _app_getapplication (size_t hash); +PITEM_RULE _app_getrule (size_t hash, EnumRuleType type, BOOL is_readonly); + +bool _app_freeapplication (size_t hash); +void _app_freerule (PITEM_RULE* ptr); + +void _app_getcount (PITEM_STATUS ptr_status); + +size_t _app_getappgroup (size_t hash, PITEM_APP const ptr_app); +size_t _app_getrulegroup (PITEM_RULE const ptr_rule); +size_t _app_getruleicon (PITEM_RULE const ptr_rule); + +rstring _app_gettooltip (UINT listview_id, size_t idx); +void _app_setruleitem (HWND hwnd, UINT listview_id, size_t item, PITEM_RULE ptr_rule); + +bool _app_ruleenable (PITEM_RULE ptr_rule, bool is_enable); +rstring _app_rulesexpand (PITEM_RULE const ptr_rule, bool is_forservices, LPCWSTR delimeter); + +bool _app_isapphaverule (size_t hash); +bool _app_isappused (ITEM_APP const *ptr_app, size_t hash); +bool _app_isappexists (ITEM_APP const *ptr_app); + +bool _app_isrulehost (LPCWSTR rule); +bool _app_isruleip (LPCWSTR rule); +bool _app_isruleport (LPCWSTR rule); + +bool _app_isrulesexists (EnumRuleType type, BOOL is_readonly, BOOL is_global); +bool _app_isrulepresent (size_t hash); + +void _app_profile_loadrules (HWND hwnd, LPCWSTR path, LPCWSTR path_backup, bool is_internal, MFILTER_RULES *ptr_rules, EnumRuleType type, UINT8 weight, time_t *ptimestamp); + +void _app_profile_load (HWND hwnd, LPCWSTR path_apps = nullptr, LPCWSTR path_rules = nullptr); +void _app_profile_save (HWND /*hwnd*/, LPCWSTR path_apps = nullptr, LPCWSTR path_rules = nullptr); diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 00000000..965d77c4 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,160 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +void _app_timer_create (HWND hwnd, const MFILTER_APPS* ptr_apps, time_t seconds) +{ + if (!config.hengine || !ptr_apps || ptr_apps->empty ()) + return; + + const time_t current_time = _r_unixtime_now (); + + for (size_t i = 0; i < ptr_apps->size (); i++) + { + PITEM_APP ptr_app = ptr_apps->at (i); + + if (!ptr_app) + continue; + + if (ptr_app->htimer) + { + DeleteTimerQueueTimer (config.htimer, ptr_app->htimer, nullptr); + ptr_app->htimer = nullptr; + } + + if (ptr_app->timer) + ptr_app->timer = 0; + + const size_t hash = _r_str_hash (ptr_app->original_path); // note: be carefull (!) + + if (CreateTimerQueueTimer (&ptr_app->htimer, config.htimer, &_app_timer_callback, (PVOID)hash, DWORD (seconds * _R_SECONDSCLOCK_MSEC), 0, WT_EXECUTEONLYONCE | WT_EXECUTEINTIMERTHREAD)) + { + ptr_app->is_enabled = true; + ptr_app->timer = current_time + seconds; + + const size_t item = _app_getposition (hwnd, hash); + + if (item != LAST_VALUE) + { + _r_fastlock_acquireshared (&lock_checkbox); + + _r_listview_setitem (hwnd, IDC_APPS_PROFILE, item, 0, nullptr, LAST_VALUE, _app_getappgroup (hash, ptr_app)); + _r_listview_setitemcheck (hwnd, IDC_APPS_PROFILE, item, ptr_app->is_enabled); + + _r_fastlock_releaseshared (&lock_checkbox); + } + } + } + + _wfp_create3filters (ptr_apps, __LINE__); +} + +size_t _app_timer_remove (HWND hwnd, const MFILTER_APPS* ptr_apps) +{ + if (!config.hengine || !ptr_apps || ptr_apps->empty ()) + return false; + + const time_t current_time = _r_unixtime_now (); + size_t count = 0; + + MARRAY ids; + + for (size_t i = 0; i < ptr_apps->size (); i++) + { + PITEM_APP ptr_app = ptr_apps->at (i); + + if (!ptr_app || !_app_istimeractive (ptr_app)) + continue; + + ids.insert (ids.end (), ptr_app->mfarr.begin (), ptr_app->mfarr.end ()); + ptr_app->mfarr.clear (); + + if (ptr_app->htimer) + { + DeleteTimerQueueTimer (config.htimer, ptr_app->htimer, nullptr); + ptr_app->htimer = nullptr; + ptr_app->timer = 0; + + ptr_app->is_enabled = false; + + const size_t hash = _r_str_hash (ptr_app->original_path); // note: be carefull (!) + const size_t item = _app_getposition (hwnd, hash); + + if (item != LAST_VALUE) + { + if (!_app_isappexists (ptr_app) || ptr_app->is_temp) + { + SendDlgItemMessage (hwnd, IDC_APPS_PROFILE, LVM_DELETEITEM, item, 0); + _app_freeapplication (hash); + } + else + { + _r_fastlock_acquireshared (&lock_checkbox); + + _r_listview_setitem (hwnd, IDC_APPS_PROFILE, item, 0, nullptr, LAST_VALUE, _app_getappgroup (hash, ptr_app)); + _r_listview_setitemcheck (hwnd, IDC_APPS_PROFILE, item, ptr_app->is_enabled); + + _r_fastlock_releaseshared (&lock_checkbox); + } + } + + count += 1; + } + } + + _wfp_destroy2filters (&ids, __LINE__); + + return count; +} + +bool _app_istimeractive (ITEM_APP const *ptr_app) +{ + return ptr_app->htimer || (ptr_app->timer && (ptr_app->timer > _r_unixtime_now ())); +} + +bool _app_istimersactive () +{ + _r_fastlock_acquireshared (&lock_access); + + for (auto &p : apps) + { + if (_app_istimeractive (&p.second)) + { + _r_fastlock_releaseshared (&lock_access); + + return true; + } + } + + _r_fastlock_releaseshared (&lock_access); + + return false; +} + +void CALLBACK _app_timer_callback (PVOID lparam, BOOLEAN) +{ + _r_fastlock_acquireexclusive (&lock_access); + + const size_t hash = (size_t)lparam; + const PITEM_APP ptr_app = _app_getapplication (hash); + const HWND hwnd = app.GetHWND (); + + MFILTER_APPS rules; + rules.push_back (ptr_app); + + const bool is_succcess = _app_timer_remove (hwnd, &rules); + + _r_fastlock_releaseexclusive (&lock_access); + + if (is_succcess) + { + _app_listviewsort (hwnd, IDC_APPS_PROFILE, -1, false); + _app_profile_save (hwnd); + + _r_listview_redraw (hwnd, IDC_APPS_PROFILE); + + if (app.ConfigGet (L"IsNotificationsTimer", true).AsBool ()) + app.TrayPopup (hwnd, UID, nullptr, NIIF_USER | (app.ConfigGet (L"IsNotificationsSound", true).AsBool () ? 0 : NIIF_NOSOUND), APP_NAME, _r_fmt (app.LocaleString (IDS_STATUS_TIMER_DONE, nullptr), ptr_app->display_name)); + } +} diff --git a/src/timer.hpp b/src/timer.hpp new file mode 100644 index 00000000..6d0dd63e --- /dev/null +++ b/src/timer.hpp @@ -0,0 +1,12 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +void _app_timer_create (HWND hwnd, const MFILTER_APPS* ptr_apps, time_t seconds); +size_t _app_timer_remove (HWND hwnd, const MFILTER_APPS* ptr_apps); + +bool _app_istimeractive (ITEM_APP const *ptr_app); +bool _app_istimersactive (); + +void CALLBACK _app_timer_callback (PVOID lparam, BOOLEAN); diff --git a/src/wfp.cpp b/src/wfp.cpp new file mode 100644 index 00000000..345523f1 --- /dev/null +++ b/src/wfp.cpp @@ -0,0 +1,1797 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#include "global.hpp" + +bool _wfp_isfiltersapplying () +{ + return _r_fastlock_islocked (&lock_apply) || _r_fastlock_islocked (&lock_transaction); +} + +bool _wfp_isfiltersinstalled () +{ + HKEY hkey = nullptr; + bool result = false; + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\services\\BFE\\Parameters\\Policy\\Persistent\\Provider", 0, KEY_READ, &hkey) == ERROR_SUCCESS) + { + static const rstring guidString = _r_str_fromguid (GUID_WfpProvider); + + if (RegQueryValueEx (hkey, guidString, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) + result = true; + + RegCloseKey (hkey); + } + + return result; +} + +bool _wfp_initialize (bool is_full) +{ + bool result = true; + DWORD rc; + + _r_fastlock_acquireexclusive (&lock_transaction); + + if (!config.hengine) + { + // generate unique session key + if (!config.psession) + { + config.psession = new GUID; + + if (config.psession) + { + if (CoCreateGuid (config.psession) != S_OK) + SAFE_DELETE (config.psession); + } + } + + SecureZeroMemory (&session, sizeof (session)); + + session.displayData.name = APP_NAME; + session.displayData.description = APP_NAME; + + session.txnWaitTimeoutInMSec = TRANSACTION_TIMEOUT; + + if (config.psession) + CopyMemory (&session.sessionKey, config.psession, sizeof (GUID)); + + rc = FwpmEngineOpen (nullptr, RPC_C_AUTHN_WINNT, nullptr, &session, &config.hengine); + + if (rc != ERROR_SUCCESS || !config.hengine) + { + _app_logerror (L"FwpmEngineOpen", rc, nullptr, false); + + if (config.hengine) + config.hengine = nullptr; + } + } + + if (!config.hengine) + { + _r_fastlock_releaseexclusive (&lock_transaction); + + return false; + } + + // set security info + if (config.pusersid && (!config.pacl_engine || !config.pacl_default || !config.pacl_secure)) + { + SAFE_LOCAL_FREE (config.pacl_engine); + SAFE_LOCAL_FREE (config.pacl_default); + SAFE_LOCAL_FREE (config.pacl_secure); + + PSID pWorldSID = nullptr; + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + + if (!AllocateAndInitializeSid (&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pWorldSID)) + { + _app_logerror (L"AllocateAndInitializeSid", GetLastError (), nullptr, true); + } + else + { + static const size_t count = 2; + EXPLICIT_ACCESS access[count] = {0}; + + SecureZeroMemory (access, count * sizeof (EXPLICIT_ACCESS)); + + // create default (engine) acl + access[0].grfAccessPermissions = FWPM_GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER; + access[0].grfAccessMode = GRANT_ACCESS; + access[0].grfInheritance = NO_INHERITANCE; + BuildTrusteeWithSid (&(access[0].Trustee), config.pusersid); + + access[1].grfAccessPermissions = FWPM_GENERIC_ALL; + access[1].grfAccessMode = GRANT_ACCESS; + access[1].grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; + BuildTrusteeWithSid (&(access[1].Trustee), pWorldSID); + + SetEntriesInAcl (_countof (access), access, nullptr, &config.pacl_engine); + + // create default (simplewall) acl + access[0].grfAccessPermissions = FWPM_GENERIC_EXECUTE | FWPM_GENERIC_WRITE | DELETE | WRITE_DAC | WRITE_OWNER; + access[0].grfAccessMode = GRANT_ACCESS; + access[0].grfInheritance = NO_INHERITANCE; + BuildTrusteeWithSid (&(access[0].Trustee), config.pusersid); + + access[1].grfAccessPermissions = FWPM_GENERIC_EXECUTE | FWPM_GENERIC_READ; + access[1].grfAccessMode = GRANT_ACCESS; + access[1].grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; + BuildTrusteeWithSid (&(access[1].Trustee), pWorldSID); + + SetEntriesInAcl (_countof (access), access, nullptr, &config.pacl_default); + + // create secure (simplewall) acl + access[0].grfAccessPermissions = FWPM_GENERIC_EXECUTE | FWPM_GENERIC_READ; + access[0].grfAccessMode = GRANT_ACCESS; + access[0].grfInheritance = NO_INHERITANCE; + BuildTrusteeWithSid (&(access[0].Trustee), config.pusersid); + + access[1].grfAccessPermissions = FWPM_ACTRL_WRITE | DELETE | WRITE_DAC | WRITE_OWNER; + access[1].grfAccessMode = DENY_ACCESS; + access[1].grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; + BuildTrusteeWithSid (&(access[1].Trustee), pWorldSID); + + SetEntriesInAcl (_countof (access), access, nullptr, &config.pacl_secure); + + FreeSid (pWorldSID); + } + } + + // set security information + { + if (config.pusersid) + { + FwpmEngineSetSecurityInfo (config.hengine, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + FwpmNetEventsSetSecurityInfo (config.hengine, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + } + + if (config.pacl_engine) + { + FwpmEngineSetSecurityInfo (config.hengine, DACL_SECURITY_INFORMATION, nullptr, nullptr, config.pacl_engine, nullptr); + FwpmNetEventsSetSecurityInfo (config.hengine, DACL_SECURITY_INFORMATION, nullptr, nullptr, config.pacl_engine, nullptr); + } + } + + // set engine options + { + FWP_VALUE val; + + // dropped packets logging (win7+) + if (is_full && !config.is_neteventset && _r_sys_validversion (6, 1)) + { + val.type = FWP_UINT32; + val.uint32 = 1; + + rc = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_COLLECT_NET_EVENTS, &val); + + if (rc != ERROR_SUCCESS) + { + _app_logerror (L"FwpmEngineSetOption", rc, L"FWPM_ENGINE_COLLECT_NET_EVENTS", false); + } + else + { + // configure dropped packets logging (win8+) + if (_r_sys_validversion (6, 2)) + { + // the filter engine will collect wfp network events that match any supplied key words + val.type = FWP_UINT32; + val.uint32 = FWPM_NET_EVENT_KEYWORD_CLASSIFY_ALLOW | FWPM_NET_EVENT_KEYWORD_INBOUND_MCAST | FWPM_NET_EVENT_KEYWORD_INBOUND_BCAST; + + rc = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_NET_EVENT_MATCH_ANY_KEYWORDS, &val); + + if (rc != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", rc, L"FWPM_ENGINE_NET_EVENT_MATCH_ANY_KEYWORDS", true); + + // enables the connection monitoring feature and starts logging creation and deletion events (and notifying any subscribers) + val.type = FWP_UINT32; + val.uint32 = 1; + + rc = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_MONITOR_IPSEC_CONNECTIONS, &val); + + if (rc != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", rc, L"FWPM_ENGINE_MONITOR_IPSEC_CONNECTIONS", true); + } + + config.is_neteventset = true; + } + } + + // packet queuing (win8+) + if (is_full && _r_sys_validversion (6, 2) && app.ConfigGet (L"IsPacketQueuingEnabled", true).AsBool ()) + { + // enables inbound or forward packet queuing independently. when enabled, the system is able to evenly distribute cpu load to multiple cpus for site-to-site ipsec tunnel scenarios. + val.type = FWP_UINT32; + val.uint32 = FWPM_ENGINE_OPTION_PACKET_QUEUE_INBOUND | FWPM_ENGINE_OPTION_PACKET_QUEUE_FORWARD; + + rc = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_PACKET_QUEUING, &val); + + if (rc != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", rc, L"FWPM_ENGINE_PACKET_QUEUING", true); + } + } + + if (is_full) + { + const bool is_intransact = _wfp_transact_start (__LINE__); + + // create provider + FWPM_PROVIDER provider = {0}; + + provider.displayData.name = APP_NAME; + provider.displayData.description = APP_NAME; + + provider.providerKey = GUID_WfpProvider; + provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT; + + rc = FwpmProviderAdd (config.hengine, &provider, nullptr); + + if (rc != ERROR_SUCCESS && rc != FWP_E_ALREADY_EXISTS) + { + if (is_intransact) + FwpmTransactionAbort (config.hengine); + + _app_logerror (L"FwpmProviderAdd", rc, nullptr, false); + result = false; + } + else + { + FWPM_SUBLAYER sublayer = {0}; + + sublayer.displayData.name = APP_NAME; + sublayer.displayData.description = APP_NAME; + + sublayer.providerKey = (LPGUID)&GUID_WfpProvider; + sublayer.subLayerKey = GUID_WfpSublayer; + sublayer.flags = FWPM_SUBLAYER_FLAG_PERSISTENT; + sublayer.weight = (UINT16)app.ConfigGet (L"SublayerWeight", SUBLAYER_WEIGHT_DEFAULT).AsUint (); // highest weight for UINT16 + + rc = FwpmSubLayerAdd (config.hengine, &sublayer, nullptr); + + if (rc != ERROR_SUCCESS && rc != FWP_E_ALREADY_EXISTS) + { + if (is_intransact) + FwpmTransactionAbort (config.hengine); + + _app_logerror (L"FwpmSubLayerAdd", rc, nullptr, false); + result = false; + } + else + { + if (is_intransact) + { + if (_wfp_transact_commit (__LINE__)) + result = true; + } + else + { + result = true; + } + } + } + } + + // set security information + if (_wfp_isfiltersinstalled ()) + { + const bool is_secure = app.ConfigGet (L"IsSecureFilters", false).AsBool (); + + if (config.pusersid) + { + FwpmProviderSetSecurityInfoByKey (config.hengine, &GUID_WfpProvider, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + FwpmSubLayerSetSecurityInfoByKey (config.hengine, &GUID_WfpSublayer, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + } + + if (is_secure ? config.pacl_secure : config.pacl_default) + { + FwpmProviderSetSecurityInfoByKey (config.hengine, &GUID_WfpProvider, DACL_SECURITY_INFORMATION, nullptr, nullptr, is_secure ? config.pacl_secure : config.pacl_default, nullptr); + FwpmSubLayerSetSecurityInfoByKey (config.hengine, &GUID_WfpSublayer, DACL_SECURITY_INFORMATION, nullptr, nullptr, is_secure ? config.pacl_secure : config.pacl_default, nullptr); + } + } + + _r_fastlock_releaseexclusive (&lock_transaction); + + return result; +} + +void _wfp_uninitialize (bool is_full) +{ + SAFE_DELETE (config.psession); + + if (!config.hengine) + return; + + _r_fastlock_acquireexclusive (&lock_transaction); + + DWORD result; + + FWP_VALUE val; + + // dropped packets logging (win7+) + if (config.is_neteventset && _r_sys_validversion (6, 1)) + { + _wfp_logunsubscribe (); + + // collect ipsec connection (win8+) + if (_r_sys_validversion (6, 2)) + { + val.type = FWP_UINT32; + val.uint32 = 0; + + result = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_MONITOR_IPSEC_CONNECTIONS, &val); + + if (result != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", result, L"FWPM_ENGINE_MONITOR_IPSEC_CONNECTIONS", true); + } + + val.type = FWP_UINT32; + val.uint32 = 0; + + result = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_COLLECT_NET_EVENTS, &val); + + if (result != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", result, L"FWPM_ENGINE_COLLECT_NET_EVENTS", true); + + else + config.is_neteventset = false; + } + + // packet queuing (win8+) + if (_r_sys_validversion (6, 2)) + { + val.type = FWP_UINT32; + val.uint32 = FWPM_ENGINE_OPTION_PACKET_QUEUE_NONE; + + result = FwpmEngineSetOption (config.hengine, FWPM_ENGINE_PACKET_QUEUING, &val); + + if (result != ERROR_SUCCESS) + _app_logerror (L"FwpmEngineSetOption", result, L"FWPM_ENGINE_PACKET_QUEUING", true); + } + + if (is_full) + { + // set security information + if (config.pusersid) + { + FwpmProviderSetSecurityInfoByKey (config.hengine, &GUID_WfpProvider, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + FwpmSubLayerSetSecurityInfoByKey (config.hengine, &GUID_WfpSublayer, OWNER_SECURITY_INFORMATION, (const SID*)config.pusersid, nullptr, nullptr, nullptr); + + } + + if (config.pacl_default) + { + FwpmProviderSetSecurityInfoByKey (config.hengine, &GUID_WfpProvider, DACL_SECURITY_INFORMATION, nullptr, nullptr, config.pacl_default, nullptr); + FwpmSubLayerSetSecurityInfoByKey (config.hengine, &GUID_WfpSublayer, DACL_SECURITY_INFORMATION, nullptr, nullptr, config.pacl_default, nullptr); + } + + const bool is_intransact = _wfp_transact_start (__LINE__); + + // destroy callouts (deprecated) + { + static const GUID callouts[] = { + GUID_WfpOutboundCallout4_DEPRECATED, + GUID_WfpOutboundCallout6_DEPRECATED, + GUID_WfpInboundCallout4_DEPRECATED, + GUID_WfpInboundCallout6_DEPRECATED, + GUID_WfpListenCallout4_DEPRECATED, + GUID_WfpListenCallout6_DEPRECATED + }; + + for (UINT i = 0; i < _countof (callouts); i++) + FwpmCalloutDeleteByKey (config.hengine, &callouts[i]); + } + + // destroy sublayer + result = FwpmSubLayerDeleteByKey (config.hengine, &GUID_WfpSublayer); + + if (result != ERROR_SUCCESS && result != FWP_E_SUBLAYER_NOT_FOUND) + _app_logerror (L"FwpmSubLayerDeleteByKey", result, nullptr, false); + + // destroy provider + result = FwpmProviderDeleteByKey (config.hengine, &GUID_WfpProvider); + + if (result != ERROR_SUCCESS && result != FWP_E_PROVIDER_NOT_FOUND) + _app_logerror (L"FwpmProviderDeleteByKey", result, nullptr, false); + + if (is_intransact) + _wfp_transact_commit (__LINE__); + } + + FwpmEngineClose (config.hengine); + config.hengine = nullptr; + + _r_fastlock_releaseexclusive (&lock_transaction); +} + +bool _wfp_transact_start (UINT line) +{ + if (config.hengine) + { + const DWORD result = FwpmTransactionBegin (config.hengine, 0); + + if (result == ERROR_SUCCESS) + return true; + + _app_logerror (L"FwpmTransactionBegin", result, _r_fmt (L"#%d", line), false); + } + + return false; +} + +bool _wfp_transact_commit (UINT line) +{ + if (config.hengine) + { + const DWORD result = FwpmTransactionCommit (config.hengine); + + if (result == ERROR_SUCCESS) + return true; + + FwpmTransactionAbort (config.hengine); + + _app_logerror (L"FwpmTransactionCommit", result, _r_fmt (L"#%d", line), false); + } + + return false; +} + +bool _wfp_deletefilter (HANDLE engineHandle, const GUID* pfilter_id) +{ + if (!engineHandle || !pfilter_id || *pfilter_id == GUID_NULL) + return false; + + const DWORD result = FwpmFilterDeleteByKey (config.hengine, pfilter_id); + + if (result != ERROR_SUCCESS && result != FWP_E_FILTER_NOT_FOUND) + { + _app_logerror (L"FwpmFilterDeleteByKey", result, _r_str_fromguid (*pfilter_id).GetString (), false); + return false; + } + + return true; +} + +DWORD _wfp_createfilter (LPCWSTR name, FWPM_FILTER_CONDITION* lpcond, UINT32 const count, UINT8 weight, const GUID* layer, const GUID* callout, FWP_ACTION_TYPE action, UINT32 flags, MARRAY* pmar) +{ + FWPM_FILTER filter = {0}; + + WCHAR fltr_name[128] = {0}; + StringCchCopy (fltr_name, _countof (fltr_name), name ? name : APP_NAME); + + filter.displayData.name = fltr_name; + filter.displayData.description = fltr_name; + + // set filter flags + if ((flags & FWPM_FILTER_FLAG_BOOTTIME) == 0) + { + filter.flags = FWPM_FILTER_FLAG_PERSISTENT; + + // filter is indexed to help enable faster lookup during classification (win8+) + if (_r_sys_validversion (6, 2)) + filter.flags |= FWPM_FILTER_FLAG_INDEXED; + } + + if (flags) + filter.flags |= flags; + + filter.providerKey = (LPGUID)&GUID_WfpProvider; + filter.subLayerKey = GUID_WfpSublayer; + CoCreateGuid (&filter.filterKey); // set filter guid + + if (count) + { + filter.numFilterConditions = count; + filter.filterCondition = lpcond; + } + + if (layer) + CopyMemory (&filter.layerKey, layer, sizeof (GUID)); + + if (callout) + CopyMemory (&filter.action.calloutKey, callout, sizeof (GUID)); + + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = weight; + + filter.action.type = action; + + UINT64 filter_id = 0; + const DWORD result = FwpmFilterAdd (config.hengine, &filter, nullptr, &filter_id); + + // issue #229 + //if (result == FWP_E_PROVIDER_NOT_FOUND) + //{ + //} + + if (result == ERROR_SUCCESS) + { + if (pmar) + pmar->push_back (filter.filterKey); + } + else + { + _app_logerror (L"FwpmFilterAdd", result, name, false); + } + + return result; +} + +void _wfp_destroyfilters () +{ + if (!config.hengine) + return; + + // clear common filters + filter_ids.clear (); + + // clear apps filters + for (auto &p : apps) + { + p.second.is_haveerrors = false; + p.second.mfarr.clear (); + } + + // clear rules filters + for (size_t i = 0; i < rules_arr.size (); i++) + { + PITEM_RULE ptr_rule = rules_arr.at (i); + + if (ptr_rule) + { + ptr_rule->is_haveerrors = false; + ptr_rule->mfarr.clear (); + } + } + + // destroy all filters + MARRAY filter_all; + + if (_wfp_dumpfilters (&GUID_WfpProvider, &filter_all)) + _wfp_destroy2filters (&filter_all, __LINE__); +} + +bool _wfp_destroy2filters (const MARRAY* pmar, UINT line) +{ + if (!config.hengine || !pmar || pmar->empty ()) + return false; + + const bool is_enabled = _r_ctrl_isenabled (app.GetHWND (), IDC_START_BTN); + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, false); + + _r_fastlock_acquireexclusive (&lock_transaction); + + for (size_t i = 0; i < pmar->size (); i++) + _wfp_setfiltersecurity (config.hengine, &pmar->at (i), config.pusersid, config.pacl_default, line); + + const bool is_intransact = _wfp_transact_start (line); + + for (size_t i = 0; i < pmar->size (); i++) + _wfp_deletefilter (config.hengine, &pmar->at (i)); + + if (is_intransact) + _wfp_transact_commit (line); + + _r_fastlock_releaseexclusive (&lock_transaction); + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, true); + + return true; +} + +bool _wfp_createrulefilter (LPCWSTR name, size_t app_hash, LPCWSTR rule_remote, LPCWSTR rule_local, UINT8 protocol, ADDRESS_FAMILY af, FWP_DIRECTION dir, UINT8 weight, FWP_ACTION_TYPE action, UINT32 flag, MARRAY* pmfarr) +{ + UINT32 count = 0; + FWPM_FILTER_CONDITION fwfc[8] = {0}; + + FWP_BYTE_BLOB* bPath = nullptr; + FWP_BYTE_BLOB* bSid = nullptr; + + FWP_V4_ADDR_AND_MASK addr4 = {0}; + FWP_V6_ADDR_AND_MASK addr6 = {0}; + + FWP_RANGE range; + SecureZeroMemory (&range, sizeof (range)); + + ITEM_ADDRESS addr; + SecureZeroMemory (&addr, sizeof (addr)); + + addr.paddr4 = &addr4; + addr.paddr6 = &addr6; + addr.prange = ⦥ + + bool is_remoteaddr_set = false; + bool is_remoteport_set = false; + + // set path condition + if (app_hash) + { + PITEM_APP ptr_app = _app_getapplication (app_hash); + + if (!ptr_app) + return false; + + if (ptr_app->type == AppPackage) // windows store app (win8+) + { + if (ptr_app->psid) + { + fwfc[count].fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_SID; + fwfc[count].conditionValue.sid = (SID*)ptr_app->psid; + + count += 1; + } + else + { + _app_logerror (TEXT (__FUNCTION__), 0, ptr_app->display_name, true); + return false; + } + } + else if (ptr_app->type == AppService) // windows service + { + const rstring path = _r_path_expand (PATH_SVCHOST); + const DWORD rc = _FwpmGetAppIdFromFileName1 (path, &bPath, ptr_app->type); + + if (rc == ERROR_SUCCESS) + { + fwfc[count].fieldKey = FWPM_CONDITION_ALE_APP_ID; + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_BYTE_BLOB_TYPE; + fwfc[count].conditionValue.byteBlob = bPath; + + count += 1; + } + + if (ptr_app->psd && ByteBlobAlloc (ptr_app->psd, GetSecurityDescriptorLength (ptr_app->psd), &bSid)) + { + fwfc[count].fieldKey = FWPM_CONDITION_ALE_USER_ID; + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_SECURITY_DESCRIPTOR_TYPE; + fwfc[count].conditionValue.sd = bSid; + + count += 1; + } + else + { + ByteBlobFree (&bPath); + ByteBlobFree (&bSid); + + _app_logerror (TEXT (__FUNCTION__), 0, ptr_app->display_name, true); + + return false; + } + } + else + { + if (!ptr_app->original_path) + { + ByteBlobFree (&bPath); + ByteBlobFree (&bSid); + + _app_logerror (TEXT (__FUNCTION__), 0, ptr_app->display_name, true); + + return false; + } + + LPCWSTR path = ptr_app->original_path; + const DWORD rc = _FwpmGetAppIdFromFileName1 (path, &bPath, ptr_app->type); + + if (rc == ERROR_SUCCESS) + { + fwfc[count].fieldKey = FWPM_CONDITION_ALE_APP_ID; + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_BYTE_BLOB_TYPE; + fwfc[count].conditionValue.byteBlob = bPath; + + count += 1; + } + else + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + // do not log file not found to error log + if (rc != ERROR_FILE_NOT_FOUND && rc != ERROR_PATH_NOT_FOUND) + _app_logerror (L"FwpmGetAppIdFromFileName", rc, path, true); + + return false; + } + } + } + + // set ip/port condition + { + LPCWSTR rules[] = {rule_remote, rule_local}; + + for (size_t i = 0; i < _countof (rules); i++) + { + if (rules[i] && rules[i][0] && rules[i][0] != L'*') + { + if (!_app_parserulestring (rules[i], &addr)) + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + _app_logerror (L"_app_parserulestring", ERROR_INVALID_NETNAME, _r_fmt (L"[%s (%s)]", rules[i], name), false); + + return false; + } + else + { + if (i == 0) + { + if (addr.type == TypeIp || addr.type == TypeHost) + is_remoteaddr_set = true; + + else if (addr.type == TypePort) + is_remoteport_set = true; + } + + if (addr.is_range && (addr.type == TypeIp || addr.type == TypePort)) + { + if (addr.type == TypeIp) + { + if (addr.format == NET_ADDRESS_IPV4) + { + af = AF_INET; + } + else if (addr.format == NET_ADDRESS_IPV6) + { + af = AF_INET6; + } + else + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + return false; + } + } + + fwfc[count].fieldKey = (addr.type == TypePort) ? ((i == 0) ? FWPM_CONDITION_IP_REMOTE_PORT : FWPM_CONDITION_IP_LOCAL_PORT) : ((i == 0) ? FWPM_CONDITION_IP_REMOTE_ADDRESS : FWPM_CONDITION_IP_LOCAL_ADDRESS); + fwfc[count].matchType = FWP_MATCH_RANGE; + fwfc[count].conditionValue.type = FWP_RANGE_TYPE; + fwfc[count].conditionValue.rangeValue = ⦥ + + count += 1; + } + else if (addr.type == TypePort) + { + fwfc[count].fieldKey = ((i == 0) ? FWPM_CONDITION_IP_REMOTE_PORT : FWPM_CONDITION_IP_LOCAL_PORT); + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_UINT16; + fwfc[count].conditionValue.uint16 = addr.port; + + count += 1; + } + else if (addr.type == TypeHost || addr.type == TypeIp) + { + fwfc[count].fieldKey = ((i == 0) ? FWPM_CONDITION_IP_REMOTE_ADDRESS : FWPM_CONDITION_IP_LOCAL_ADDRESS); + fwfc[count].matchType = FWP_MATCH_EQUAL; + + if (addr.format == NET_ADDRESS_IPV4) + { + af = AF_INET; + + fwfc[count].conditionValue.type = FWP_V4_ADDR_MASK; + fwfc[count].conditionValue.v4AddrMask = &addr4; + + count += 1; + } + else if (addr.format == NET_ADDRESS_IPV6) + { + af = AF_INET6; + + fwfc[count].conditionValue.type = FWP_V6_ADDR_MASK; + fwfc[count].conditionValue.v6AddrMask = &addr6; + + count += 1; + } + else if (addr.format == NET_ADDRESS_DNS_NAME) + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + const rstring::rvector arr2 = rstring (addr.host).AsVector (RULE_DELIMETER); + + if (arr2.empty ()) + { + return false; + } + else + { + for (size_t j = 0; j < arr2.size (); j++) + { + if (!_wfp_createrulefilter (name, app_hash, arr2[j], nullptr, protocol, af, dir, weight, action, flag, pmfarr)) + return false; + } + } + + return true; + } + else + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + return false; + } + + // set port if available + if (addr.port) + { + fwfc[count].fieldKey = ((i == 0) ? FWPM_CONDITION_IP_REMOTE_PORT : FWPM_CONDITION_IP_LOCAL_PORT); + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_UINT16; + fwfc[count].conditionValue.uint16 = addr.port; + + count += 1; + } + } + else + { + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + return false; + } + } + } + } + } + + // set protocol condition + if (protocol) + { + fwfc[count].fieldKey = FWPM_CONDITION_IP_PROTOCOL; + fwfc[count].matchType = FWP_MATCH_EQUAL; + fwfc[count].conditionValue.type = FWP_UINT8; + fwfc[count].conditionValue.uint8 = protocol; + + count += 1; + } + + // create outbound layer filter + if (dir == FWP_DIRECTION_OUTBOUND || dir == FWP_DIRECTION_MAX) + { + if (af == AF_INET || af == AF_UNSPEC) + { + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, nullptr, action, flag, pmfarr); + + // win7+ + if (_r_sys_validversion (6, 1)) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, nullptr, action, flag, pmfarr); + } + + if (af == AF_INET6 || af == AF_UNSPEC) + { + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, nullptr, action, flag, pmfarr); + + // win7+ + if (_r_sys_validversion (6, 1)) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, nullptr, action, flag, pmfarr); + } + } + + // create inbound layer filter + if (dir == FWP_DIRECTION_INBOUND || dir == FWP_DIRECTION_MAX) + { + if (af == AF_INET || af == AF_UNSPEC) + { + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, action, flag, pmfarr); + + if (action == FWP_ACTION_BLOCK && (!is_remoteaddr_set && !is_remoteport_set)) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, nullptr, FWP_ACTION_PERMIT, flag, pmfarr); + } + + if (af == AF_INET6 || af == AF_UNSPEC) + { + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, action, flag, pmfarr); + + if (action == FWP_ACTION_BLOCK && (!is_remoteaddr_set && !is_remoteport_set)) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, nullptr, FWP_ACTION_PERMIT, flag, pmfarr); + } + } + + // create listen layer filter + if (!app.ConfigGet (L"AllowListenConnections2", true).AsBool () && !protocol && dir != FWP_DIRECTION_OUTBOUND && (!is_remoteaddr_set && !is_remoteport_set)) + { + if (af == AF_INET || af == AF_UNSPEC) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_LISTEN_V4, nullptr, action, flag, pmfarr); + + if (af == AF_INET6 || af == AF_UNSPEC) + _wfp_createfilter (name, fwfc, count, weight, &FWPM_LAYER_ALE_AUTH_LISTEN_V6, nullptr, action, flag, pmfarr); + } + + ByteBlobFree (&bSid); + ByteBlobFree (&bPath); + + return true; +} + +bool _wfp_create4filters (const MFILTER_RULES* ptr_rules, UINT line, bool is_intransact) +{ + if (!config.hengine || !ptr_rules || ptr_rules->empty ()) + return false; + + const bool is_enabled = _r_ctrl_isenabled (app.GetHWND (), IDC_START_BTN); + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, false); + + if (!is_intransact && _wfp_isfiltersapplying ()) + is_intransact = true; + + if (!is_intransact) + { + MARRAY ids; + + for (size_t i = 0; i < ptr_rules->size (); i++) + { + PITEM_RULE ptr_rule = ptr_rules->at (i); + + if (ptr_rule) + { + ids.insert (ids.end (), ptr_rule->mfarr.begin (), ptr_rule->mfarr.end ()); + + ptr_rule->is_haveerrors = false; + ptr_rule->mfarr.clear (); + } + } + + _wfp_destroy2filters (&ids, line); + + _r_fastlock_acquireexclusive (&lock_transaction); + is_intransact = !_wfp_transact_start (line); + } + + for (size_t i = 0; i < ptr_rules->size (); i++) + { + PITEM_RULE ptr_rule = ptr_rules->at (i); + + if (ptr_rule) + { + ptr_rule->mfarr.clear (); + + if (ptr_rule->is_enabled) + { + rstring::rvector rule_remote_arr = rstring (ptr_rule->prule_remote).AsVector (RULE_DELIMETER); + rstring::rvector rule_local_arr = rstring (ptr_rule->prule_local).AsVector (RULE_DELIMETER); + + const size_t rules_remote_length = rule_remote_arr.size (); + const size_t rules_local_length = rule_local_arr.size (); + const size_t count = max (1, max (rules_remote_length, rules_local_length)); + + for (size_t j = 0; j < count; j++) + { + rstring rule_remote = L""; + rstring rule_local = L""; + + // sync remote rules and local rules + if (!rule_remote_arr.empty () && rules_remote_length > j) + rule_remote = rule_remote_arr.at (j).Trim (L"\r\n "); + + // sync local rules and remote rules + if (!rule_local_arr.empty () && rules_local_length > j) + rule_local = rule_local_arr.at (j).Trim (L"\r\n "); + + // apply rules for services hosts + if (ptr_rule->is_forservices) + { + if (!_wfp_createrulefilter (ptr_rule->pname, config.ntoskrnl_hash, rule_remote, rule_local, ptr_rule->protocol, ptr_rule->af, ptr_rule->dir, ptr_rule->weight, ptr_rule->is_block ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT, 0, &ptr_rule->mfarr)) + ptr_rule->is_haveerrors = true; + + if (!_wfp_createrulefilter (ptr_rule->pname, config.svchost_hash, rule_remote, rule_local, ptr_rule->protocol, ptr_rule->af, ptr_rule->dir, ptr_rule->weight, ptr_rule->is_block ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT, 0, &ptr_rule->mfarr)) + ptr_rule->is_haveerrors = true; + } + + if (!ptr_rule->apps.empty ()) + { + for (auto const& p : ptr_rule->apps) + { + if (ptr_rule->is_forservices && (p.first == config.ntoskrnl_hash || p.first == config.svchost_hash)) + continue; + + if (!_wfp_createrulefilter (ptr_rule->pname, p.first, rule_remote, rule_local, ptr_rule->protocol, ptr_rule->af, ptr_rule->dir, ptr_rule->weight, ptr_rule->is_block ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT, 0, &ptr_rule->mfarr)) + ptr_rule->is_haveerrors = true; + } + } + else + { + if (!_wfp_createrulefilter (ptr_rule->pname, 0, rule_remote, rule_local, ptr_rule->protocol, ptr_rule->af, ptr_rule->dir, ptr_rule->weight, ptr_rule->is_block ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT, 0, &ptr_rule->mfarr)) + ptr_rule->is_haveerrors = true; + } + } + } + } + } + + if (!is_intransact) + { + _wfp_transact_commit (line); + + const bool is_secure = app.ConfigGet (L"IsSecureFilters", false).AsBool (); + + if (is_secure ? config.pacl_secure : config.pacl_default) + { + for (size_t i = 0; i < ptr_rules->size (); i++) + { + PITEM_RULE ptr_rule = ptr_rules->at (i); + + if (ptr_rule && ptr_rule->is_enabled) + { + for (size_t j = 0; j < ptr_rule->mfarr.size (); j++) + _wfp_setfiltersecurity (config.hengine, &ptr_rule->mfarr.at (j), config.pusersid, is_secure ? config.pacl_secure : config.pacl_default, line); + } + } + } + + _r_fastlock_releaseexclusive (&lock_transaction); + } + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, true); + + return true; +} + +bool _wfp_create3filters (const MFILTER_APPS* ptr_apps, UINT line, bool is_intransact) +{ + if (!config.hengine || !ptr_apps || ptr_apps->empty ()) + return false; + + const EnumMode mode = (EnumMode)app.ConfigGet (L"Mode", ModeWhitelist).AsUint (); + const FWP_ACTION_TYPE action = (mode == ModeBlacklist) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT; + + const bool is_enabled = _r_ctrl_isenabled (app.GetHWND (), IDC_START_BTN); + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, false); + + if (!is_intransact && _wfp_isfiltersapplying ()) + is_intransact = true; + + if (!is_intransact) + { + MARRAY ids; + + for (size_t i = 0; i < ptr_apps->size (); i++) + { + PITEM_APP ptr_app = ptr_apps->at (i); + + if (ptr_app) + { + ids.insert (ids.end (), ptr_app->mfarr.begin (), ptr_app->mfarr.end ()); + + ptr_app->is_haveerrors = false; + ptr_app->mfarr.clear (); + } + } + + _wfp_destroy2filters (&ids, line); + + _r_fastlock_acquireexclusive (&lock_transaction); + is_intransact = !_wfp_transact_start (line); + } + + for (size_t i = 0; i < ptr_apps->size (); i++) + { + PITEM_APP ptr_app = ptr_apps->at (i); + + if (ptr_app && ptr_app->is_enabled) + ptr_app->is_haveerrors = !_wfp_createrulefilter (ptr_app->display_name, _r_str_hash (ptr_app->original_path), nullptr, nullptr, 0, AF_UNSPEC, FWP_DIRECTION_MAX, FILTER_WEIGHT_APPLICATION, action, 0, &ptr_app->mfarr); + } + + if (!is_intransact) + { + _wfp_transact_commit (line); + + const bool is_secure = app.ConfigGet (L"IsSecureFilters", false).AsBool (); + + if (is_secure ? config.pacl_secure : config.pacl_default) + { + for (size_t i = 0; i < ptr_apps->size (); i++) + { + const PITEM_APP ptr_app = ptr_apps->at (i); + + if (ptr_app) + { + for (size_t j = 0; j < ptr_app->mfarr.size (); j++) + _wfp_setfiltersecurity (config.hengine, &ptr_app->mfarr.at (j), config.pusersid, is_secure ? config.pacl_secure : config.pacl_default, line); + } + } + } + + _r_fastlock_releaseexclusive (&lock_transaction); + } + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, true); + + return true; +} + +bool _wfp_create2filters (UINT line, bool is_intransact) +{ + if (!config.hengine) + return false; + + const EnumMode mode = (EnumMode)app.ConfigGet (L"Mode", ModeWhitelist).AsUint (); + + const bool is_enabled = _r_ctrl_isenabled (app.GetHWND (), IDC_START_BTN); + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, false); + + if (!is_intransact && _wfp_isfiltersapplying ()) + is_intransact = true; + + if (!is_intransact) + { + _wfp_destroy2filters (&filter_ids, line); + filter_ids.clear (); + + _r_fastlock_acquireexclusive (&lock_transaction); + is_intransact = !_wfp_transact_start (line); + } + + FWPM_FILTER_CONDITION fwfc[3] = {0}; + + // add loopback connections permission + if (app.ConfigGet (L"AllowLoopbackConnections", true).AsBool ()) + { + // match all loopback (localhost) data + fwfc[0].fieldKey = FWPM_CONDITION_FLAGS; + fwfc[0].matchType = FWP_MATCH_FLAGS_ANY_SET; + fwfc[0].conditionValue.type = FWP_UINT32; + fwfc[0].conditionValue.uint32 = FWP_CONDITION_FLAG_IS_LOOPBACK; + + // tests if the network traffic is (non-)app container loopback traffic (win8+) + if (_r_sys_validversion (6, 2)) + fwfc[0].conditionValue.uint32 |= FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK; + + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // win7+ + if (_r_sys_validversion (6, 1)) + { + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + } + + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_LISTEN_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_LISTEN_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // ipv4/ipv6 loopback + static LPCWSTR ip_list[] = { + L"0.0.0.0/8", + L"10.0.0.0/8", + L"100.64.0.0/10", + L"127.0.0.0/8", + L"169.254.0.0/16", + L"172.16.0.0/12", + L"192.0.0.0/24", + L"192.0.2.0/24", + L"192.88.99.0/24", + L"192.168.0.0/16", + L"198.18.0.0/15", + L"198.51.100.0/24", + L"203.0.113.0/24", + L"224.0.0.0/4", + L"240.0.0.0/4", + L"255.255.255.255/32", + L"::/0", + L"::/128", + L"::1/128", + L"::ffff:0:0/96", + L"::ffff:0:0:0/96", + L"64:ff9b::/96", + L"100::/64", + L"2001::/32", + L"2001:20::/28", + L"2001:db8::/32", + L"2002::/16", + L"fc00::/7", + L"fe80::/10", + L"ff00::/8" + }; + + for (size_t i = 0; i < _countof (ip_list); i++) + { + FWP_V4_ADDR_AND_MASK addr4 = {0}; + FWP_V6_ADDR_AND_MASK addr6 = {0}; + + ITEM_ADDRESS addr; + SecureZeroMemory (&addr, sizeof (addr)); + + addr.paddr4 = &addr4; + addr.paddr6 = &addr6; + + if (_app_parserulestring (ip_list[i], &addr)) + { + fwfc[1].matchType = FWP_MATCH_EQUAL; + + if (addr.format == NET_ADDRESS_IPV4) + { + fwfc[1].conditionValue.type = FWP_V4_ADDR_MASK; + fwfc[1].conditionValue.v4AddrMask = &addr4; + + fwfc[1].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // win7+ + if (_r_sys_validversion (6, 1)) + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + fwfc[1].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS; + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_LISTEN_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + } + else if (addr.format == NET_ADDRESS_IPV6) + { + fwfc[1].conditionValue.type = FWP_V6_ADDR_MASK; + fwfc[1].conditionValue.v6AddrMask = &addr6; + + fwfc[1].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // win7+ + if (_r_sys_validversion (6, 1)) + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + fwfc[1].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS; + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (nullptr, fwfc, 2, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_LISTEN_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + } + } + } + } + + // firewall service rules + // https://msdn.microsoft.com/en-us/library/gg462153.aspx + if (app.ConfigGet (L"AllowIPv6", true).AsBool ()) + { + // allows 6to4 tunneling, which enables ipv6 to run over an ipv4 network + fwfc[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL; + fwfc[0].matchType = FWP_MATCH_EQUAL; + fwfc[0].conditionValue.type = FWP_UINT8; + fwfc[0].conditionValue.uint8 = IPPROTO_IPV6; // ipv6 header + + _wfp_createfilter (L"Allow6to4", fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // allows icmpv6 router solicitation messages, which are required for the ipv6 stack to work properly + fwfc[0].fieldKey = FWPM_CONDITION_ICMP_TYPE; + fwfc[0].matchType = FWP_MATCH_EQUAL; + fwfc[0].conditionValue.type = FWP_UINT16; + fwfc[0].conditionValue.uint16 = 0x85; + + _wfp_createfilter (L"AllowIcmpV6Type133", fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // allows icmpv6 router advertise messages, which are required for the ipv6 stack to work properly + fwfc[0].conditionValue.uint16 = 0x86; + _wfp_createfilter (L"AllowIcmpV6Type134", fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // allows icmpv6 neighbor solicitation messages, which are required for the ipv6 stack to work properly + fwfc[0].conditionValue.uint16 = 0x87; + _wfp_createfilter (L"AllowIcmpV6Type135", fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // allows icmpv6 neighbor advertise messages, which are required for the ipv6 stack to work properly + fwfc[0].conditionValue.uint16 = 0x88; + _wfp_createfilter (L"AllowIcmpV6Type136", fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + } + + // prevent port scanning using stealth discards and silent drops + // https://docs.microsoft.com/ru-ru/windows/desktop/FWP/preventing-port-scanning + if (app.ConfigGet (L"UseStealthMode", false).AsBool ()) + { + // blocks udp port scanners + fwfc[0].fieldKey = FWPM_CONDITION_FLAGS; + fwfc[0].matchType = FWP_MATCH_FLAGS_NONE_SET; + fwfc[0].conditionValue.type = FWP_UINT32; + fwfc[0].conditionValue.uint32 = FWP_CONDITION_FLAG_IS_LOOPBACK; + + // tests if the network traffic is (non-)app container loopback traffic (win8+) + if (_r_sys_validversion (6, 2)) + fwfc[0].conditionValue.uint32 |= FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK; + + fwfc[1].fieldKey = FWPM_CONDITION_ICMP_TYPE; + fwfc[1].matchType = FWP_MATCH_EQUAL; + fwfc[1].conditionValue.type = FWP_UINT16; + fwfc[1].conditionValue.uint16 = 0x03; // destination unreachable + + _wfp_createfilter (L"BlockIcmpErrorV4", fwfc, 2, FILTER_WEIGHT_HIGHEST, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + _wfp_createfilter (L"BlockIcmpErrorV6", fwfc, 2, FILTER_WEIGHT_HIGHEST, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + + // blocks tcp port scanners (exclude loopback) + fwfc[0].conditionValue.uint32 |= FWP_CONDITION_FLAG_IS_IPSEC_SECURED; + + _wfp_createfilter (L"BlockTcpRstOnCloseV4", fwfc, 1, FILTER_WEIGHT_HIGHEST, &FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, &FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V4_SILENT_DROP, FWP_ACTION_CALLOUT_TERMINATING, 0, &filter_ids); + _wfp_createfilter (L"BlockTcpRstOnCloseV6", fwfc, 1, FILTER_WEIGHT_HIGHEST, &FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, &FWPM_CALLOUT_WFP_TRANSPORT_LAYER_V6_SILENT_DROP, FWP_ACTION_CALLOUT_TERMINATING, 0, &filter_ids); + } + + // block all outbound traffic (only on "whitelist" mode) + if (mode == ModeWhitelist) + { + _wfp_createfilter (L"BlockOutboundConnectionsV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + _wfp_createfilter (L"BlockOutboundConnectionsV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + + // win7+ + if (_r_sys_validversion (6, 1)) + { + _wfp_createfilter (L"BlockOutboundRedirectionV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + _wfp_createfilter (L"BlockOutboundRedirectionV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + } + } + else + { + _wfp_createfilter (L"AllowOutboundConnectionsV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (L"AllowOutboundConnectionsV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + + // win7+ + if (_r_sys_validversion (6, 1)) + { + _wfp_createfilter (L"AllowOutboundRedirectionV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + _wfp_createfilter (L"AllowOutboundRedirectionV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, nullptr, FWP_ACTION_PERMIT, 0, &filter_ids); + } + } + + // block all inbound traffic (only on "stealth" mode) + if (mode == ModeWhitelist && (app.ConfigGet (L"UseStealthMode", false).AsBool () || !app.ConfigGet (L"AllowInboundConnections", false).AsBool ())) + { + _wfp_createfilter (L"BlockInboundConnectionsV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + _wfp_createfilter (L"BlockInboundConnectionsV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + } + + // block all listen traffic (NOT RECOMMENDED!!!!) + // issue: https://github.com/henrypp/simplewall/issues/9 + if (mode == ModeWhitelist && !app.ConfigGet (L"AllowListenConnections2", true).AsBool ()) + { + _wfp_createfilter (L"BlockListenConnectionsV4", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_LISTEN_V4, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + _wfp_createfilter (L"BlockListenConnectionsV6", nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_LISTEN_V6, nullptr, FWP_ACTION_BLOCK, 0, &filter_ids); + } + + // install boot-time filters (enforced at boot-time, even before "base filtering engine" service starts) + if (app.ConfigGet (L"InstallBoottimeFilters", false).AsBool ()) + { + { + fwfc[0].fieldKey = FWPM_CONDITION_FLAGS; + fwfc[0].matchType = FWP_MATCH_FLAGS_ANY_SET; + fwfc[0].conditionValue.type = FWP_UINT32; + fwfc[0].conditionValue.uint32 = FWP_CONDITION_FLAG_IS_LOOPBACK; + + // tests if the network traffic is (non-)app container loopback traffic (win8+) + if (_r_sys_validversion (6, 2)) + fwfc[0].conditionValue.uint32 |= FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK; + + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_IPFORWARD_V4, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_IPFORWARD_V6, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_INBOUND_ICMP_ERROR_V4, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_INBOUND_ICMP_ERROR_V6, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_HIGHEST_IMPORTANT, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, nullptr, FWP_ACTION_PERMIT, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_INBOUND_ICMP_ERROR_V4, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_INBOUND_ICMP_ERROR_V6, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, nullptr, 0, FILTER_WEIGHT_LOWEST, &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + } + + // win7+ boot-time features + if (_r_sys_validversion (6, 1)) + { + fwfc[0].fieldKey = FWPM_CONDITION_FLAGS; + fwfc[0].matchType = FWP_MATCH_FLAGS_NONE_SET; + fwfc[0].conditionValue.type = FWP_UINT32; + fwfc[0].conditionValue.uint32 = FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU | FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU; + + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_APPLICATION, &FWPM_LAYER_IPFORWARD_V4, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + _wfp_createfilter (BOOTTIME_FILTER_NAME, fwfc, 1, FILTER_WEIGHT_APPLICATION, &FWPM_LAYER_IPFORWARD_V6, nullptr, FWP_ACTION_BLOCK, FWPM_FILTER_FLAG_BOOTTIME, &filter_ids); + } + } + + if (!is_intransact) + { + _wfp_transact_commit (line); + + const bool is_secure = app.ConfigGet (L"IsSecureFilters", false).AsBool (); + + if (is_secure ? config.pacl_secure : config.pacl_default) + { + for (size_t i = 0; i < filter_ids.size (); i++) + _wfp_setfiltersecurity (config.hengine, &filter_ids.at (i), config.pusersid, is_secure ? config.pacl_secure : config.pacl_default, line); + } + + _r_fastlock_releaseexclusive (&lock_transaction); + } + + if (is_enabled) + _r_ctrl_enable (app.GetHWND (), IDC_START_BTN, true); + + return true; +} + +void _wfp_setfiltersecurity (HANDLE engineHandle, const GUID* pfilter_id, const PSID psid, PACL pacl, UINT line) +{ + if (!engineHandle || !pfilter_id || *pfilter_id == GUID_NULL || (!psid && !pacl)) + return; + + if (psid) + { + const DWORD result = FwpmFilterSetSecurityInfoByKey (engineHandle, pfilter_id, OWNER_SECURITY_INFORMATION, (const SID*)psid, nullptr, nullptr, nullptr); + + if (result != ERROR_SUCCESS) + _app_logerror (L"FwpmFilterSetSecurityInfoByKey", result, _r_fmt (L"#%d", line), true); + } + + if (pacl) + { + const DWORD result = FwpmFilterSetSecurityInfoByKey (engineHandle, pfilter_id, DACL_SECURITY_INFORMATION, nullptr, nullptr, pacl, nullptr); + + if (result != ERROR_SUCCESS) + _app_logerror (L"FwpmFilterSetSecurityInfoByKey", result, _r_fmt (L"#%d", line), true); + } +} + +size_t _wfp_dumpfilters (const GUID* pprovider, MARRAY* pfilters) +{ + if (!config.hengine || !pprovider || !pfilters) + return 0; + + UINT32 count = 0; + HANDLE henum = nullptr; + + DWORD result = FwpmFilterCreateEnumHandle (config.hengine, nullptr, &henum); + + if (result != ERROR_SUCCESS) + { + _app_logerror (L"FwpmFilterCreateEnumHandle", result, nullptr, false); + return 0; + } + else + { + FWPM_FILTER** matchingFwpFilter = nullptr; + + result = FwpmFilterEnum (config.hengine, henum, UINT32_MAX, &matchingFwpFilter, &count); + + if (result != ERROR_SUCCESS) + { + _app_logerror (L"FwpmFilterEnum", result, nullptr, false); + } + else + { + if (matchingFwpFilter) + { + for (UINT32 i = 0; i < count; i++) + { + if (matchingFwpFilter[i]->providerKey && memcmp (matchingFwpFilter[i]->providerKey, pprovider, sizeof (GUID)) == 0) + pfilters->push_back (matchingFwpFilter[i]->filterKey); + } + + FwpmFreeMemory ((void**)&matchingFwpFilter); + } + else + { + count = 0; + } + } + } + + if (henum) + FwpmFilterDestroyEnumHandle (config.hengine, henum); + + return count; +} + +bool _mps_firewallapi (bool* pis_enabled, const bool* pis_enable) +{ + if (!pis_enabled && !pis_enable) + return false; + + bool result = false; + + const HRESULT hrComInit = CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED); + + if ((hrComInit == RPC_E_CHANGED_MODE) || SUCCEEDED (hrComInit)) + { + INetFwPolicy2* pNetFwPolicy2 = nullptr; + HRESULT hr = CoCreateInstance (__uuidof (NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof (INetFwPolicy2), (void**)&pNetFwPolicy2); + + if (SUCCEEDED (hr) && pNetFwPolicy2) + { + static const NET_FW_PROFILE_TYPE2 profileTypes[] = { + NET_FW_PROFILE2_DOMAIN, + NET_FW_PROFILE2_PRIVATE, + NET_FW_PROFILE2_PUBLIC + }; + + if (pis_enabled) + { + *pis_enabled = false; + + for (size_t i = 0; i < _countof (profileTypes); i++) + { + VARIANT_BOOL bIsEnabled = FALSE; + + hr = pNetFwPolicy2->get_FirewallEnabled (profileTypes[i], &bIsEnabled); + + if (SUCCEEDED (hr)) + { + result = true; + + if (bIsEnabled == VARIANT_TRUE) + { + *pis_enabled = true; + break; + } + } + } + } + + if (pis_enable) + { + for (size_t i = 0; i < _countof (profileTypes); i++) + { + hr = pNetFwPolicy2->put_FirewallEnabled (profileTypes[i], *pis_enable ? VARIANT_TRUE : VARIANT_FALSE); + + if (SUCCEEDED (hr)) + result = true; + + else + _app_logerror (L"put_FirewallEnabled", hr, _r_fmt (L"%d", profileTypes[i]), true); + } + } + } + else + { + _app_logerror (L"CoCreateInstance", hr, L"INetFwPolicy2", true); + } + + if (pNetFwPolicy2) + pNetFwPolicy2->Release (); + + if (SUCCEEDED (hrComInit)) + CoUninitialize (); + } + + return result; +} + +void _mps_changeconfig2 (bool is_enable) +{ + // check settings + bool is_wfenabled = false; + _mps_firewallapi (&is_wfenabled, nullptr); + + if (is_wfenabled == is_enable) + return; + + const SC_HANDLE scm = OpenSCManager (nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + + if (!scm) + { + _app_logerror (L"OpenSCManager", GetLastError (), nullptr, true); + } + else + { + static LPCWSTR arr[] = { + L"mpssvc", + L"mpsdrv", + }; + + bool is_started = false; + + for (INT i = 0; i < _countof (arr); i++) + { + const SC_HANDLE sc = OpenService (scm, arr[i], SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP); + + if (!sc) + { + const DWORD result = GetLastError (); + + if (result != ERROR_ACCESS_DENIED) + _app_logerror (L"OpenService", GetLastError (), arr[i], false); + } + else + { + if (!is_started) + { + SERVICE_STATUS status; + + if (QueryServiceStatus (sc, &status)) + is_started = (status.dwCurrentState == SERVICE_RUNNING); + } + + if (!ChangeServiceConfig (sc, SERVICE_NO_CHANGE, is_enable ? SERVICE_AUTO_START : SERVICE_DISABLED, SERVICE_NO_CHANGE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) + _app_logerror (L"ChangeServiceConfig", GetLastError (), arr[i], true); + + CloseServiceHandle (sc); + } + } + + // start services + if (is_enable) + { + for (INT i = 0; i < _countof (arr); i++) + { + SC_HANDLE sc = OpenService (scm, arr[i], SERVICE_QUERY_STATUS | SERVICE_START); + + if (!sc) + { + _app_logerror (L"OpenService", GetLastError (), arr[i], false); + } + else + { + DWORD dwBytesNeeded = 0; + SERVICE_STATUS_PROCESS ssp = {0}; + + if (!QueryServiceStatusEx (sc, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof (ssp), &dwBytesNeeded)) + { + _app_logerror (L"QueryServiceStatusEx", GetLastError (), arr[i], false); + } + else + { + if (ssp.dwCurrentState != SERVICE_RUNNING) + { + if (!StartService (sc, 0, nullptr)) + _app_logerror (L"StartService", GetLastError (), arr[i], false); + } + + CloseServiceHandle (sc); + } + } + } + + _r_sleep (250); + + } + + _mps_firewallapi (nullptr, &is_enable); + + CloseServiceHandle (scm); + } +} + +DWORD _FwpmGetAppIdFromFileName1 (LPCWSTR path, FWP_BYTE_BLOB** lpblob, EnumAppType type) +{ + if (!path || !path[0] || !lpblob) + return ERROR_BAD_ARGUMENTS; + + rstring path_buff; + + if (type == AppRegular || type == AppNetwork || type == AppService) + { + path_buff = path; + + if (_r_str_hash (path) == config.ntoskrnl_hash) + { + if (ByteBlobAlloc ((LPVOID)path_buff.GetString (), (path_buff.GetLength () + 1) * sizeof (WCHAR), lpblob)) + return ERROR_SUCCESS; + } + else + { + DWORD result = _r_path_ntpathfromdos (path_buff); + + // file is inaccessible or not found, maybe low-level driver preventing file access? + // try another way! + if ( + result == ERROR_ACCESS_DENIED || + result == ERROR_FILE_NOT_FOUND || + result == ERROR_PATH_NOT_FOUND + ) + { + if (PathIsRelative (path)) + { + return result; + } + else + { + // file path (root) + WCHAR path_root[MAX_PATH] = {0}; + StringCchCopy (path_root, _countof (path_root), path); + PathStripToRoot (path_root); + + // file path (without root) + WCHAR path_noroot[MAX_PATH] = {0}; + StringCchCopy (path_noroot, _countof (path_noroot), PathSkipRoot (path)); + + path_buff = path_root; + result = _r_path_ntpathfromdos (path_buff); + + if (result != ERROR_SUCCESS) + return result; + + path_buff.Append (path_noroot); + path_buff.ToLower (); // lower is important! + } + } + else if (result != ERROR_SUCCESS) + { + return result; + } + + if (ByteBlobAlloc ((LPVOID)path_buff.GetString (), (path_buff.GetLength () + 1) * sizeof (WCHAR), lpblob)) + return ERROR_SUCCESS; + } + } + else if (type == AppPico || type == AppDevice) + { + path_buff = path; + + if (type == AppDevice) + path_buff.ToLower (); // lower is important! + + if (ByteBlobAlloc ((LPVOID)path_buff.GetString (), (path_buff.GetLength () + 1) * sizeof (WCHAR), lpblob)) + return ERROR_SUCCESS; + } + + return ERROR_FILE_NOT_FOUND; +} + +bool ByteBlobAlloc (PVOID data, size_t length, FWP_BYTE_BLOB** lpblob) +{ + if (!data || !length || !lpblob) + return false; + + *lpblob = new FWP_BYTE_BLOB; + + if (*lpblob) + { + const PUINT8 tmp_ptr = new UINT8[length]; + + if (!tmp_ptr) + { + SAFE_DELETE (*lpblob); + + return false; + } + else + { + (*lpblob)->data = tmp_ptr; + (*lpblob)->size = (UINT32)length; + + CopyMemory ((*lpblob)->data, data, length); + + return true; + } + } + + return false; +} + +void ByteBlobFree (FWP_BYTE_BLOB** lpblob) +{ + if (lpblob && *lpblob) + { + FWP_BYTE_BLOB* blob = *lpblob; + + if (blob) + { + SAFE_DELETE_ARRAY (blob->data); + SAFE_DELETE (blob); + + *lpblob = nullptr; + } + } +} diff --git a/src/wfp.hpp b/src/wfp.hpp new file mode 100644 index 00000000..edcdf93b --- /dev/null +++ b/src/wfp.hpp @@ -0,0 +1,41 @@ +// simplewall +// Copyright (c) 2016-2019 Henry++ + +#pragma once + +bool _wfp_isfiltersapplying (); +bool _wfp_isfiltersinstalled (); + +bool _wfp_initialize (bool is_full); +void _wfp_uninitialize (bool is_full); + +bool _wfp_isfiltersinstalled (); +bool _wfp_destroy2filters (const MARRAY* pmar, UINT line); + +bool _wfp_transact_start (UINT line); +bool _wfp_transact_commit (UINT line); + +bool _wfp_deletefilter (HANDLE engineHandle, const GUID* pfilter_id); +DWORD _wfp_createfilter (LPCWSTR name, FWPM_FILTER_CONDITION* lpcond, UINT32 const count, UINT8 weight, const GUID* layer, const GUID* callout, FWP_ACTION_TYPE action, UINT32 flags, MARRAY* pmar = nullptr); + +void _wfp_destroyfilters (); + +bool _wfp_destroy2filters (const MARRAY* pmar, UINT line); + +bool _wfp_createrulefilter (LPCWSTR name, size_t app_hash, LPCWSTR rule_remote, LPCWSTR rule_local, UINT8 protocol, ADDRESS_FAMILY af, FWP_DIRECTION dir, UINT8 weight, FWP_ACTION_TYPE action, UINT32 flag, MARRAY* pmfarr); + +bool _wfp_create4filters (const MFILTER_RULES* ptr_rules, UINT line, bool is_intransact = false); +bool _wfp_create3filters (const MFILTER_APPS* ptr_apps, UINT line, bool is_intransact = false); +bool _wfp_create2filters (UINT line, bool is_intransact = false); + +void _wfp_setfiltersecurity (HANDLE engineHandle, const GUID* pfilter_id, const PSID psid, PACL pacl, UINT line); + +size_t _wfp_dumpfilters (const GUID* pprovider, MARRAY* pfilters); + +bool _mps_firewallapi (bool* pis_enabled, const bool* pis_enable); +void _mps_changeconfig2 (bool is_enable); + +DWORD _FwpmGetAppIdFromFileName1 (LPCWSTR path, FWP_BYTE_BLOB** lpblob, EnumAppType type); + +bool ByteBlobAlloc (PVOID data, size_t length, FWP_BYTE_BLOB** lpblob); +void ByteBlobFree (FWP_BYTE_BLOB** lpblob);