1143 lines
32 KiB
C++
1143 lines
32 KiB
C++
// SPDX-License-Identifier: AGPL-1.0-only
|
||
// Copyright (C) 2018 Ludvig Strigeus <info@tunsafe.com>. All Rights Reserved.
|
||
#include "stdafx.h"
|
||
#include "wireguard_config.h"
|
||
#include "network_win32_api.h"
|
||
#include "network_win32_dnsblock.h"
|
||
#include <Commctrl.h>
|
||
#include <stdlib.h>
|
||
#include <assert.h>
|
||
#include <malloc.h>
|
||
#include <stddef.h>
|
||
#include "resource.h"
|
||
#include <string.h>
|
||
#include <Richedit.h>
|
||
#include <vector>
|
||
#include <Iphlpapi.h>
|
||
#include <assert.h>
|
||
#include <shldisp.h>
|
||
#include <shlobj.h>
|
||
#include <exdisp.h>
|
||
#include "tunsafe_endian.h"
|
||
#include "util.h"
|
||
#include <atlbase.h>
|
||
#include <algorithm>
|
||
#include "crypto/curve25519-donna.h"
|
||
|
||
#undef min
|
||
#pragma comment(lib, "iphlpapi.lib")
|
||
#pragma comment(lib, "rpcrt4.lib")
|
||
#pragma comment(lib,"comctl32.lib")
|
||
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||
|
||
void InitCpuFeatures();
|
||
void PrintCpuFeatures();
|
||
void Benchmark();
|
||
static const char *GetCurrentConfigTitle(char *buf, size_t max_size);
|
||
|
||
#pragma warning(disable: 4200)
|
||
|
||
static void MyPostMessage(int msg, WPARAM wparam, LPARAM lparam);
|
||
|
||
static HWND g_ui_window;
|
||
static in_addr_t g_ui_ip;
|
||
static HICON g_icons[2];
|
||
static bool g_minimize_on_connect;
|
||
|
||
static bool g_ui_visible;
|
||
static char *g_current_filename;
|
||
static HKEY g_reg_key;
|
||
static HINSTANCE g_hinstance;
|
||
static TunsafeBackendWin32 *g_backend;
|
||
static bool g_last_popup_is_tray;
|
||
|
||
int RegReadInt(const char *key, int def) {
|
||
DWORD value = def, n = sizeof(value);
|
||
RegQueryValueEx(g_reg_key, key, NULL, NULL, (BYTE*)&value, &n);
|
||
return value;
|
||
}
|
||
|
||
void RegWriteInt(const char *key, int value) {
|
||
RegSetValueEx(g_reg_key, key, NULL, REG_DWORD, (BYTE*)&value, sizeof(value));
|
||
}
|
||
|
||
char *RegReadStr(const char *key, const char *def) {
|
||
char buf[1024];
|
||
DWORD n = sizeof(buf) - 1;
|
||
DWORD type = 0;
|
||
if (RegQueryValueEx(g_reg_key, key, NULL, &type, (BYTE*)buf, &n) != ERROR_SUCCESS || type != REG_SZ)
|
||
return def ? _strdup(def) : NULL;
|
||
if (n && buf[n - 1] == 0)
|
||
n--;
|
||
buf[n] = 0;
|
||
return _strdup(buf);
|
||
}
|
||
|
||
void RegWriteStr(const char *key, const char *v) {
|
||
RegSetValueEx(g_reg_key, key, NULL, REG_SZ, (BYTE*)v, (DWORD)strlen(v) + 1);
|
||
}
|
||
|
||
void str_set(char **x, const char *s) {
|
||
free(*x);
|
||
*x = _strdup(s);
|
||
}
|
||
|
||
char *str_cat_alloc(const char *a, const char *b) {
|
||
size_t al = strlen(a);
|
||
size_t bl = strlen(b);
|
||
char *r = (char *)malloc(al + bl + 1);
|
||
memcpy(r, a, al);
|
||
r[al + bl] = 0;
|
||
memcpy(r + al, b, bl);
|
||
return r;
|
||
}
|
||
|
||
static const char *FindLastFolderSep(const char *s) {
|
||
size_t len = strlen(s);
|
||
for (;;) {
|
||
if (len == 0)
|
||
return NULL;
|
||
len--;
|
||
if (s[len] == '\\' || s[len] == '/')
|
||
break;
|
||
}
|
||
return s + len;
|
||
}
|
||
|
||
|
||
static bool GetConfigFullName(const char *basename, char *fullname, size_t fullname_size) {
|
||
size_t len = strlen(basename);
|
||
|
||
if (FindLastFolderSep(basename)) {
|
||
if (len >= fullname_size)
|
||
return false;
|
||
memcpy(fullname, basename, len + 1);
|
||
return true;
|
||
}
|
||
if (!GetModuleFileName(NULL, fullname, (DWORD)fullname_size))
|
||
return false;
|
||
char *last = (char *)FindLastFolderSep(fullname);
|
||
if (!last || last + len + 8 >= fullname + fullname_size)
|
||
return false;
|
||
memcpy(last + 1, "Config\\", 7 * sizeof(last[0]));
|
||
memcpy(last + 8, basename, (len + 1) * sizeof(last[0]));
|
||
return true;
|
||
}
|
||
|
||
|
||
enum UpdateIconWhy {
|
||
UIW_NONE = 0,
|
||
UIW_STOPPED_WORKING_FAIL = 1,
|
||
UIW_STOPPED_WORKING_RETRY = 2,
|
||
UIW_EXITING = 3,
|
||
};
|
||
static void UpdateIcon(UpdateIconWhy error);
|
||
static void UpdateButtons();
|
||
|
||
|
||
void StopService(UpdateIconWhy error) {
|
||
if (g_backend->is_started()) {
|
||
g_backend->Stop();
|
||
|
||
g_ui_ip = 0;
|
||
|
||
if (error != UIW_EXITING) {
|
||
UpdateIcon(error);
|
||
RINFO("Disconnecting");
|
||
UpdateButtons();
|
||
RegWriteInt("IsConnected", 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
const char *print_ip(char buf[kSizeOfAddress], in_addr_t ip) {
|
||
snprintf(buf, kSizeOfAddress, "%d.%d.%d.%d", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
|
||
return buf;
|
||
}
|
||
|
||
class MyProcessorDelegate : public ProcessorDelegate {
|
||
public:
|
||
virtual void OnConnected(in_addr_t my_ip) {
|
||
if (my_ip != g_ui_ip) {
|
||
|
||
if (my_ip) {
|
||
char buf[kSizeOfAddress];
|
||
print_ip(buf, my_ip);
|
||
RINFO("Connection established. IP %s", buf);
|
||
}
|
||
g_ui_ip = my_ip;
|
||
MyPostMessage(WM_USER + 2, 0, 0);
|
||
}
|
||
}
|
||
virtual void OnDisconnected() {
|
||
MyProcessorDelegate::OnConnected(0);
|
||
}
|
||
};
|
||
|
||
static MyProcessorDelegate my_procdel;
|
||
|
||
void StartService(bool skip_clear = false) {
|
||
char buf[1024];
|
||
if (!GetConfigFullName(g_current_filename, buf, ARRAYSIZE(buf)))
|
||
return;
|
||
|
||
if (!g_backend->is_started()) {
|
||
if (!skip_clear)
|
||
PostMessage(g_ui_window, WM_USER + 6, NULL, NULL);
|
||
|
||
g_backend->Start(&my_procdel, buf);
|
||
|
||
UpdateButtons();
|
||
RegWriteInt("IsConnected", 1);
|
||
}
|
||
}
|
||
|
||
static bool g_has_icon;
|
||
|
||
static char *PrintMB(char *buf, int64 bytes) {
|
||
char *bo = buf;
|
||
if (bytes < 0) {
|
||
*buf++ = '-';
|
||
bytes = -bytes;
|
||
}
|
||
int64 big = bytes / (1024*1024);
|
||
int little = bytes % (1024*1024);
|
||
if (bytes < 10*1024*1024) {
|
||
// X.XXX
|
||
snprintf(buf, 64, "%lld.%.3d MB", big, 1000 * little / (1024*1024));
|
||
} else if (bytes < 100*1024*1024) {
|
||
// XX.XX
|
||
snprintf(buf, 64, "%lld.%.2d MB", big, 100 * little / (1024*1024));
|
||
} else {
|
||
// XX.X
|
||
snprintf(buf, 64, "%lld.%.1d MB", big, 10 * little / (1024*1024));
|
||
}
|
||
return bo;
|
||
}
|
||
|
||
static void UpdateStats() {
|
||
ProcessorStats stats = g_backend->GetStats();
|
||
|
||
char tmp[64], tmp2[64];
|
||
char buf[512];
|
||
snprintf(buf, 512, "%s received (%lld packets), %s sent (%lld packets)",
|
||
PrintMB(tmp, stats.udp_bytes_in), stats.udp_packets_in,
|
||
PrintMB(tmp2, stats.udp_bytes_out), stats.udp_packets_out/*, udp_qsize2 - udp_qsize1, g_tun_reads*/);
|
||
SetDlgItemText(g_ui_window, IDTXT_UDP, buf);
|
||
|
||
snprintf(buf, 512, "%s received (%lld packets), %s sent (%lld packets)",
|
||
PrintMB(tmp, stats.tun_bytes_in), stats.tun_packets_in,
|
||
PrintMB(tmp2, stats.tun_bytes_out), stats.tun_packets_out/*,
|
||
tpq_last_qsize, g_tun_writes*/);
|
||
SetDlgItemText(g_ui_window, IDTXT_TUN, buf);
|
||
|
||
char *d = buf;
|
||
if (stats.last_complete_handskake_timestamp) {
|
||
uint32 ago = (uint32)((OsGetMilliseconds() - stats.last_complete_handskake_timestamp) / 1000);
|
||
uint32 hours = ago / 3600;
|
||
uint32 minutes = (ago - hours * 3600) / 60;
|
||
uint32 seconds = (ago - hours * 3600 - minutes * 60);
|
||
|
||
if (hours)
|
||
d += snprintf(d, 32, hours == 1 ? "%d hour, " : "%d hours, ", hours);
|
||
if (minutes)
|
||
d += snprintf(d, 32, minutes == 1 ? "%d minute, " : "%d minutes, ", minutes);
|
||
if (d == buf || seconds)
|
||
d += snprintf(d, 32, seconds == 1 ? "%d second, " : "%d seconds, ", seconds);
|
||
memcpy(d - 2, " ago", 5);
|
||
} else {
|
||
memcpy(buf, "(never)", 8);
|
||
}
|
||
SetDlgItemText(g_ui_window, IDTXT_HANDSHAKE, buf);
|
||
}
|
||
|
||
void UpdatePublicKey(char *s) {
|
||
SetDlgItemText(g_ui_window, IDC_PUBLIC_KEY, s);
|
||
free(s);
|
||
}
|
||
|
||
static void UpdateButtons() {
|
||
bool running = g_backend->is_started();
|
||
SetDlgItemText(g_ui_window, ID_START, running ? "Re&connect" : "&Connect");
|
||
EnableWindow(GetDlgItem(g_ui_window, ID_STOP), running);
|
||
}
|
||
|
||
static void UpdateIcon(UpdateIconWhy why) {
|
||
in_addr_t ip = g_ui_ip;
|
||
NOTIFYICONDATA nid;
|
||
memset(&nid, 0, sizeof(nid));
|
||
nid.cbSize = sizeof(nid);
|
||
nid.hWnd = g_ui_window;
|
||
nid.uID = 1;
|
||
nid.uVersion = NOTIFYICON_VERSION;
|
||
nid.uCallbackMessage = WM_USER + 1;
|
||
nid.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON;
|
||
nid.hIcon = g_icons[ip ? 0 : 1];
|
||
|
||
char buf[kSizeOfAddress];
|
||
char namebuf[64];
|
||
if (ip != 0) {
|
||
snprintf(nid.szTip, sizeof(nid.szTip), "TunSafe [%s - %s]", GetCurrentConfigTitle(namebuf, sizeof(namebuf)), print_ip(buf, ip));
|
||
nid.uFlags |= NIF_INFO;
|
||
snprintf(nid.szInfoTitle, sizeof(nid.szInfoTitle), "Connected to: %s", namebuf);
|
||
snprintf(nid.szInfo, sizeof(nid.szInfo), "IP: %s", buf);
|
||
nid.uTimeout = 5000;
|
||
nid.dwInfoFlags = NIIF_INFO;
|
||
} else {
|
||
snprintf(nid.szTip, sizeof(nid.szTip), "TunSafe [%s]", "Disconnected");
|
||
|
||
if (why == UIW_STOPPED_WORKING_FAIL) {
|
||
nid.uFlags |= NIF_INFO;
|
||
strcpy(nid.szInfoTitle, "Disconnected!");
|
||
strcpy(nid.szInfo, "There was a problem with the connection. You are now disconnected.");
|
||
nid.uTimeout = 5000;
|
||
nid.dwInfoFlags = NIIF_ERROR;
|
||
}
|
||
}
|
||
Shell_NotifyIcon(g_has_icon ? NIM_MODIFY : NIM_ADD, &nid);
|
||
|
||
SendMessage(g_ui_window, WM_SETICON, ICON_SMALL, (LPARAM)g_icons[ip ? 0 : 1]);
|
||
|
||
g_has_icon = true;
|
||
}
|
||
|
||
static void RemoveIcon() {
|
||
if (g_has_icon) {
|
||
NOTIFYICONDATA nid;
|
||
memset(&nid, 0, sizeof(nid));
|
||
nid.cbSize = sizeof(nid);
|
||
nid.hWnd = g_ui_window;
|
||
nid.uID = 1;
|
||
Shell_NotifyIcon(NIM_DELETE, &nid);
|
||
}
|
||
}
|
||
|
||
#define MAX_CONFIG_FILES 100
|
||
#define ID_POPUP_CONFIG_FILE 10000
|
||
char *config_filenames[MAX_CONFIG_FILES];
|
||
|
||
static void RestartService(UpdateIconWhy why, bool only_if_active) {
|
||
if (!only_if_active || g_backend->is_started()) {
|
||
StopService(why);
|
||
StartService(why != UIW_NONE);
|
||
}
|
||
}
|
||
|
||
static char *StripConfExtension(const char *src, char *target, size_t size) {
|
||
size_t len = strlen(src);
|
||
if (len >= 5 && memcmp(src + len - 5, ".conf", 5) == 0)
|
||
len -= 5;
|
||
|
||
len = std::min(len, size - 1);
|
||
target[len] = 0;
|
||
memcpy(target, src, len);
|
||
return target;
|
||
}
|
||
|
||
static const char *GetCurrentConfigTitle(char *target, size_t size) {
|
||
const char *ll = FindLastFolderSep(g_current_filename);
|
||
return StripConfExtension(ll ? ll + 1 : g_current_filename, target, size);
|
||
}
|
||
|
||
static void LoadConfigFile(const char *filename, bool save, bool force_start) {
|
||
str_set(&g_current_filename, filename);
|
||
char namebuf[64];
|
||
char *f = str_cat_alloc("TunSafe VPN Client - ", GetCurrentConfigTitle(namebuf, sizeof(namebuf)));
|
||
SetWindowText(g_ui_window, f);
|
||
free(f);
|
||
RestartService(UIW_NONE, !force_start);
|
||
if (save)
|
||
RegWriteStr("ConfigFile", filename);
|
||
}
|
||
|
||
static void AddToAvailableFilesPopup(HMENU menu, int max_num_items, bool is_settings) {
|
||
char buf[1024];
|
||
int nfiles = 0;
|
||
if (!GetConfigFullName("*.*", buf, ARRAYSIZE(buf)))
|
||
return;
|
||
|
||
int selected_item = -1;
|
||
WIN32_FIND_DATA wfd;
|
||
HANDLE handle = FindFirstFile(buf, &wfd);
|
||
if (handle != INVALID_HANDLE_VALUE) {
|
||
do {
|
||
if (wfd.cFileName[0] == '.')
|
||
continue;
|
||
|
||
if (strcmp(g_current_filename, wfd.cFileName) == 0)
|
||
selected_item = nfiles;
|
||
|
||
str_set(&config_filenames[nfiles], wfd.cFileName);
|
||
|
||
nfiles++;
|
||
if (nfiles == MAX_CONFIG_FILES)
|
||
break;
|
||
} while (FindNextFile(handle, &wfd));
|
||
FindClose(handle);
|
||
}
|
||
|
||
HMENU where;
|
||
|
||
bool is_connected = g_backend->is_started();
|
||
|
||
where = menu;
|
||
for (int i = 0; i < nfiles; i++) {
|
||
if (i == max_num_items) {
|
||
where = CreatePopupMenu();
|
||
AppendMenu(menu, MF_POPUP, (UINT_PTR)where, "&More");
|
||
}
|
||
|
||
AppendMenu(where, (i == selected_item && is_connected) ? MF_CHECKED : 0, ID_POPUP_CONFIG_FILE + i, StripConfExtension(config_filenames[i], buf, sizeof(buf)));
|
||
|
||
if (i == selected_item)
|
||
SetMenuDefaultItem(where, ID_POPUP_CONFIG_FILE + i, MF_BYCOMMAND);
|
||
}
|
||
if (nfiles)
|
||
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||
}
|
||
|
||
static void ShowSettingsMenu(HWND wnd) {
|
||
HMENU menu = CreatePopupMenu();
|
||
|
||
AddToAvailableFilesPopup(menu, 10, true);
|
||
|
||
AppendMenu(menu, 0, IDSETT_OPEN_FILE, "&Import File...");
|
||
AppendMenu(menu, 0, IDSETT_BROWSE_FILES, "&Browse in Explorer");
|
||
|
||
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||
AppendMenu(menu, 0, IDSETT_KEYPAIR, "Generate &Key Pair...");
|
||
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||
|
||
HMENU blockinternet = CreatePopupMenu();
|
||
AppendMenu(blockinternet, 0, IDSETT_BLOCKINTERNET_OFF, "Off");
|
||
AppendMenu(blockinternet, MF_SEPARATOR, 0, 0);
|
||
AppendMenu(blockinternet, 0, IDSETT_BLOCKINTERNET_ROUTE, "Yes, with Routing Rules");
|
||
AppendMenu(blockinternet, 0, IDSETT_BLOCKINTERNET_FIREWALL, "Yes, with Firewall Rules");
|
||
AppendMenu(blockinternet, 0, IDSETT_BLOCKINTERNET_BOTH, "Yes, Both Methods");
|
||
bool is_activated = false;
|
||
int value = GetInternetBlockState(&is_activated);
|
||
CheckMenuRadioItem(blockinternet, IDSETT_BLOCKINTERNET_OFF, IDSETT_BLOCKINTERNET_BOTH, IDSETT_BLOCKINTERNET_OFF + value, MF_BYCOMMAND);
|
||
AppendMenu(menu, MF_POPUP + is_activated * MF_CHECKED, (UINT_PTR)blockinternet, "Block &All Internet Traffic");
|
||
|
||
if (g_allow_pre_post || GetAsyncKeyState(VK_SHIFT) < 0) {
|
||
AppendMenu(menu, g_allow_pre_post ? MF_CHECKED : 0, IDSETT_PREPOST, "&Allow Pre/Post commands");
|
||
}
|
||
|
||
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||
AppendMenu(menu, 0, IDSETT_WEB_PAGE, "Go to &Web Page");
|
||
AppendMenu(menu, 0, IDSETT_OPENSOURCE, "See Open Source Licenses");
|
||
AppendMenu(menu, 0, IDSETT_ABOUT, "&About TunSafe...");
|
||
|
||
POINT pt;
|
||
GetCursorPos(&pt);
|
||
|
||
g_last_popup_is_tray = false;
|
||
int rv = TrackPopupMenu(menu, 0, pt.x, pt.y, 0, wnd, NULL);
|
||
DestroyMenu(menu);
|
||
}
|
||
|
||
void FindDesktopFolderView(REFIID riid, void **ppv) {
|
||
CComPtr<IShellWindows> spShellWindows;
|
||
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
|
||
|
||
CComVariant vtLoc(CSIDL_DESKTOP);
|
||
CComVariant vtEmpty;
|
||
long lhwnd;
|
||
CComPtr<IDispatch> spdisp;
|
||
spShellWindows->FindWindowSW(
|
||
&vtLoc, &vtEmpty,
|
||
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
|
||
|
||
CComPtr<IShellBrowser> spBrowser;
|
||
CComQIPtr<IServiceProvider>(spdisp)->
|
||
QueryService(SID_STopLevelBrowser,
|
||
IID_PPV_ARGS(&spBrowser));
|
||
|
||
CComPtr<IShellView> spView;
|
||
spBrowser->QueryActiveShellView(&spView);
|
||
|
||
spView->QueryInterface(riid, ppv);
|
||
}
|
||
|
||
void GetDesktopAutomationObject(REFIID riid, void **ppv) {
|
||
CComPtr<IShellView> spsv;
|
||
FindDesktopFolderView(IID_PPV_ARGS(&spsv));
|
||
CComPtr<IDispatch> spdispView;
|
||
spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
|
||
spdispView->QueryInterface(riid, ppv);
|
||
}
|
||
|
||
void ShellExecuteFromExplorer(
|
||
PCSTR pszFile,
|
||
PCSTR pszParameters = nullptr,
|
||
PCSTR pszDirectory = nullptr,
|
||
PCSTR pszOperation = nullptr,
|
||
int nShowCmd = SW_SHOWNORMAL) {
|
||
CComPtr<IShellFolderViewDual> spFolderView;
|
||
GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
|
||
CComPtr<IDispatch> spdispShell;
|
||
spFolderView->get_Application(&spdispShell);
|
||
|
||
CComQIPtr<IShellDispatch2>(spdispShell)
|
||
->ShellExecute(CComBSTR(pszFile),
|
||
CComVariant(pszParameters ? pszParameters : ""),
|
||
CComVariant(pszDirectory ? pszDirectory : ""),
|
||
CComVariant(pszOperation ? pszOperation : ""),
|
||
CComVariant(nShowCmd));
|
||
}
|
||
|
||
static void OpenEditor() {
|
||
char buf[MAX_PATH];
|
||
if (GetConfigFullName(g_current_filename, buf, ARRAYSIZE(buf))) {
|
||
SHELLEXECUTEINFO shinfo = {0};
|
||
shinfo.cbSize = sizeof(shinfo);
|
||
shinfo.fMask = SEE_MASK_CLASSNAME;
|
||
shinfo.lpFile = buf;
|
||
shinfo.lpParameters = "";
|
||
shinfo.lpClass = ".txt";
|
||
shinfo.nShow = SW_SHOWNORMAL;
|
||
ShellExecuteEx(&shinfo);
|
||
}
|
||
}
|
||
|
||
static void BrowseFiles() {
|
||
char buf[MAX_PATH];
|
||
if (GetConfigFullName("", buf, ARRAYSIZE(buf))) {
|
||
size_t l = strlen(buf);
|
||
buf[l - 1] = 0;
|
||
ShellExecuteFromExplorer(buf, NULL, NULL, "explore");
|
||
}
|
||
}
|
||
|
||
bool FileExists(const CHAR *fileName) {
|
||
DWORD fileAttr = GetFileAttributes(fileName);
|
||
return (0xFFFFFFFF != fileAttr);
|
||
}
|
||
|
||
__int64 FileSize(const char* name) {
|
||
WIN32_FILE_ATTRIBUTE_DATA fad;
|
||
if (!GetFileAttributesEx(name, GetFileExInfoStandard, &fad))
|
||
return -1; // error condition, could call GetLastError to find out more
|
||
LARGE_INTEGER size;
|
||
size.HighPart = fad.nFileSizeHigh;
|
||
size.LowPart = fad.nFileSizeLow;
|
||
return size.QuadPart;
|
||
}
|
||
|
||
static bool is_space(uint8_t c) {
|
||
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
|
||
}
|
||
|
||
static bool is_valid(uint8_t c) {
|
||
return c >= ' ' || c == '\r' || c == '\n' || c == '\t';
|
||
}
|
||
|
||
bool SanityCheckBuf(uint8 *buf, size_t n) {
|
||
for (size_t i = 0; i < n; i++) {
|
||
if (!is_space(buf[i])) {
|
||
if (buf[i] != '[' && buf[i] != '#')
|
||
return false;
|
||
for (; i < n; i++)
|
||
if (!is_valid(buf[i]))
|
||
return false;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
uint8* LoadFileSane(const char *name, size_t *size) {
|
||
FILE *f = fopen(name, "rb");
|
||
uint8 *new_file = NULL, *file = NULL;
|
||
size_t j, i, n;
|
||
if (!f) return false;
|
||
fseek(f, 0, SEEK_END);
|
||
long x = ftell(f);
|
||
fseek(f, 0, SEEK_SET);
|
||
if (x < 0 || x >= 65536) goto error;
|
||
file = (uint8*)malloc(x + 1);
|
||
if (!file) goto error;
|
||
n = fread(file, 1, x + 1, f);
|
||
if (n != x || !SanityCheckBuf(file, n))
|
||
goto error;
|
||
// Convert the file to DOS new lines
|
||
for (i = j = 0; i < n; i++)
|
||
j += (file[i] == '\n');
|
||
new_file = (uint8*)malloc(n + 1 + j);
|
||
if (!new_file) goto error;
|
||
for (i = j = 0; i < n; i++) {
|
||
uint8 c = file[i];
|
||
if (c == '\r')
|
||
continue;
|
||
if (c == '\n')
|
||
new_file[j++] = '\r';
|
||
new_file[j++] = c;
|
||
}
|
||
new_file[j] = 0;
|
||
*size = j;
|
||
|
||
error:
|
||
fclose(f);
|
||
free(file);
|
||
return new_file;
|
||
}
|
||
|
||
bool WriteOutFile(const char *filename, uint8 *filedata, size_t filesize) {
|
||
FILE *f = fopen(filename, "wb");
|
||
if (!f) return false;
|
||
if (fwrite(filedata, 1, filesize, f) != filesize) {
|
||
fclose(f);
|
||
return false;
|
||
}
|
||
fclose(f);
|
||
return true;
|
||
}
|
||
|
||
void ImportFile(const char *s) {
|
||
char buf[1024];
|
||
char mesg[1024];
|
||
size_t filesize;
|
||
const char *last = FindLastFolderSep(s);
|
||
if (!last || !GetConfigFullName(last + 1, buf, ARRAYSIZE(buf)) || _stricmp(buf, s) == 0)
|
||
return;
|
||
|
||
uint8 *filedata = LoadFileSane(s, &filesize);
|
||
if (!filedata) goto fail;
|
||
|
||
if (FileExists(buf)) {
|
||
snprintf(mesg, ARRAYSIZE(mesg), "A file already exists with the name '%s' in the configuration folder. Do you want to overwrite it?", last + 1);
|
||
if (MessageBoxA(g_ui_window, mesg, "TunSafe", MB_OKCANCEL | MB_ICONEXCLAMATION) != IDOK)
|
||
goto out;
|
||
} else {
|
||
snprintf(mesg, ARRAYSIZE(mesg), "Do you want to import '%s' into TunSafe?", last + 1);
|
||
if (MessageBoxA(g_ui_window, mesg, "TunSafe", MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
|
||
goto out;
|
||
}
|
||
|
||
if (!WriteOutFile(buf, filedata, filesize)) {
|
||
DeleteFileA(buf);
|
||
fail:
|
||
MessageBoxA(g_ui_window, "There was a problem importing the file.", "TunSafe", MB_ICONEXCLAMATION);
|
||
} else {
|
||
LoadConfigFile(last + 1, true, false);
|
||
}
|
||
|
||
out:
|
||
free(filedata);
|
||
}
|
||
|
||
void ShowUI(HWND hWnd) {
|
||
g_ui_visible = true;
|
||
UpdateStats();
|
||
ShowWindow(hWnd, SW_SHOW);
|
||
BringWindowToTop(hWnd);
|
||
SetForegroundWindow(hWnd);
|
||
}
|
||
|
||
void HandleDroppedFiles(HWND wnd, HDROP hdrop) {
|
||
char buf[MAX_PATH];
|
||
if (DragQueryFile(hdrop, -1, NULL, 0) == 1) {
|
||
if (DragQueryFile(hdrop, 0, buf, ARRAYSIZE(buf))) {
|
||
SetForegroundWindow(wnd);
|
||
ImportFile(buf);
|
||
}
|
||
}
|
||
DragFinish(hdrop);
|
||
}
|
||
|
||
void BrowseFile(HWND wnd) {
|
||
char szFile[1024];
|
||
|
||
// open a file name
|
||
OPENFILENAME ofn = {0};
|
||
ofn.lStructSize = sizeof(ofn);
|
||
ofn.hwndOwner = g_ui_window;
|
||
ofn.lpstrFile = szFile;
|
||
ofn.lpstrFile[0] = '\0';
|
||
ofn.nMaxFile = sizeof(szFile);
|
||
ofn.lpstrFilter = "Config Files (*.conf)\0*.conf\0";
|
||
ofn.nFilterIndex = 1;
|
||
ofn.lpstrFileTitle = NULL;
|
||
ofn.nMaxFileTitle = 0;
|
||
ofn.lpstrInitialDir = NULL;
|
||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
||
if (GetOpenFileName(&ofn))
|
||
ImportFile(szFile);
|
||
}
|
||
|
||
static const uint8 kCurve25519Basepoint[32] = {9};
|
||
|
||
static void SetKeyBox(HWND wnd, int ctr, uint8 buf[32]) {
|
||
uint8 *privs = base64_encode(buf, 32, NULL);
|
||
SetDlgItemText(wnd, ctr, (char*)privs);
|
||
free(privs);
|
||
}
|
||
|
||
static INT_PTR WINAPI KeyPairDlgProc(HWND hWnd, UINT message, WPARAM wParam,
|
||
LPARAM lParam) {
|
||
switch (message) {
|
||
case WM_INITDIALOG:
|
||
return TRUE;
|
||
case WM_CLOSE:
|
||
EndDialog(hWnd, 0);
|
||
return TRUE;
|
||
case WM_COMMAND:
|
||
switch (wParam) {
|
||
case IDCANCEL:
|
||
EndDialog(hWnd, 0);
|
||
return TRUE;
|
||
case IDC_PRIVATE_KEY | (EN_CHANGE << 16) : {
|
||
char buf[128];
|
||
uint8 pub[32];
|
||
uint8 priv[32];
|
||
buf[0] = 0;
|
||
size_t len = GetDlgItemText(hWnd, IDC_PRIVATE_KEY, buf, sizeof(buf));
|
||
size_t olen = 32;
|
||
if (base64_decode((uint8*)buf, len, priv, &olen) && olen == 32) {
|
||
curve25519_donna(pub, priv, kCurve25519Basepoint);
|
||
SetKeyBox(hWnd, IDC_PUBLIC_KEY, pub);
|
||
} else {
|
||
SetDlgItemText(hWnd, IDC_PUBLIC_KEY, "(Invalid Private Key)");
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
case IDRAND: {
|
||
uint8 priv[32];
|
||
uint8 pub[32];
|
||
OsGetRandomBytes(priv, 32);
|
||
curve25519_normalize(priv);
|
||
curve25519_donna(pub, priv, kCurve25519Basepoint);
|
||
SetKeyBox(hWnd, IDC_PRIVATE_KEY, priv);
|
||
SetKeyBox(hWnd, IDC_PUBLIC_KEY, pub);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
bool wm_dropfiles_recursive;
|
||
uint64 last_auto_service_restart;
|
||
static INT_PTR WINAPI DlgProc(HWND hWnd, UINT message, WPARAM wParam,
|
||
LPARAM lParam) {
|
||
switch(message) {
|
||
case WM_INITDIALOG:
|
||
return TRUE;
|
||
case WM_CLOSE:
|
||
g_ui_visible = false;
|
||
ShowWindow(hWnd, SW_HIDE);
|
||
return TRUE;
|
||
case WM_COMMAND:
|
||
if (wParam >= ID_POPUP_CONFIG_FILE && wParam < ID_POPUP_CONFIG_FILE + MAX_CONFIG_FILES) {
|
||
const char *new_conf = config_filenames[wParam - ID_POPUP_CONFIG_FILE];
|
||
if (!new_conf)
|
||
return TRUE;
|
||
|
||
if (g_last_popup_is_tray && strcmp(new_conf, g_current_filename) == 0 && g_backend->is_started()) {
|
||
StopService(UIW_NONE);
|
||
} else {
|
||
LoadConfigFile(new_conf, true, g_last_popup_is_tray);
|
||
}
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
switch(wParam) {
|
||
case ID_START:
|
||
StopService(UIW_NONE);
|
||
StartService();
|
||
break;
|
||
case ID_STOP: StopService(UIW_NONE); break;
|
||
case ID_EXIT: PostQuitMessage(0); break;
|
||
case ID_RESET: g_backend->ResetStats(); break;
|
||
case ID_MORE_BUTTON: ShowSettingsMenu(hWnd); break;
|
||
case IDSETT_WEB_PAGE: ShellExecute(NULL, NULL, "https://tunsafe.com/", NULL, NULL, 0); break;
|
||
case IDSETT_OPENSOURCE: ShellExecute(NULL, NULL, "https://tunsafe.com/open-source", NULL, NULL, 0); break;
|
||
case ID_EDITCONF: OpenEditor(); break;
|
||
case IDSETT_BROWSE_FILES:BrowseFiles(); break;
|
||
case IDSETT_OPEN_FILE: BrowseFile(hWnd); break;
|
||
case IDSETT_ABOUT:
|
||
MessageBoxA(g_ui_window, TUNSAFE_VERSION_STRING "\r\n\r\nCopyright <20> 2018, Ludvig Strigeus\r\n\r\nThanks for choosing TunSafe!\r\n\r\nThis version was built on " __DATE__ " " __TIME__, "About TunSafe", MB_ICONINFORMATION);
|
||
break;
|
||
case IDSETT_KEYPAIR:
|
||
DialogBox(g_hinstance, MAKEINTRESOURCE(IDD_DIALOG2), hWnd, &KeyPairDlgProc);
|
||
break;
|
||
case IDSETT_BLOCKINTERNET_OFF:
|
||
case IDSETT_BLOCKINTERNET_ROUTE:
|
||
case IDSETT_BLOCKINTERNET_FIREWALL:
|
||
case IDSETT_BLOCKINTERNET_BOTH: {
|
||
InternetBlockState old_state = GetInternetBlockState(NULL);
|
||
InternetBlockState new_state = (InternetBlockState)(wParam - IDSETT_BLOCKINTERNET_OFF);
|
||
|
||
if (old_state == kBlockInternet_Off && new_state != kBlockInternet_Off) {
|
||
if (MessageBoxA(g_ui_window, "Warning! All Internet traffic will be blocked until you restart your computer. Only traffic through TunSafe will be allowed.\r\n\r\nThe blocking is activated the next time you connect to a VPN server.\r\n\r\nDo you want to continue?", "TunSafe", MB_ICONWARNING | MB_OKCANCEL) == IDCANCEL)
|
||
return TRUE;
|
||
}
|
||
|
||
SetInternetBlockState(new_state);
|
||
|
||
if ((~old_state & new_state) && g_backend->is_started()) {
|
||
StopService(UIW_NONE);
|
||
StartService();
|
||
}
|
||
return TRUE;
|
||
}
|
||
case IDSETT_PREPOST: {
|
||
g_allow_pre_post = !g_allow_pre_post;
|
||
RegWriteInt("AllowPrePost", g_allow_pre_post);
|
||
return TRUE;
|
||
}
|
||
}
|
||
break;
|
||
case WM_DROPFILES:
|
||
if (!wm_dropfiles_recursive) {
|
||
wm_dropfiles_recursive = true;
|
||
HandleDroppedFiles(hWnd, (HDROP)wParam);
|
||
wm_dropfiles_recursive = false;
|
||
}
|
||
break;
|
||
case WM_USER + 1:
|
||
if (lParam == WM_RBUTTONUP) {
|
||
HMENU menu = CreatePopupMenu();
|
||
AddToAvailableFilesPopup(menu, 10, false);
|
||
|
||
bool active = g_backend->is_started();
|
||
AppendMenu(menu, 0, ID_START, active ? "Re&connect" : "&Connect");
|
||
AppendMenu(menu, active ? 0 : MF_GRAYED, ID_STOP, "&Disconnect");
|
||
AppendMenu(menu, MF_SEPARATOR, 0, NULL);
|
||
AppendMenu(menu, 0, ID_EXIT, "&Exit");
|
||
POINT pt;
|
||
GetCursorPos(&pt);
|
||
|
||
SetForegroundWindow(hWnd);
|
||
|
||
g_last_popup_is_tray = true;
|
||
|
||
int rv = TrackPopupMenu(menu, 0, pt.x, pt.y, 0, hWnd, NULL);
|
||
DestroyMenu(menu);
|
||
} else if (lParam == WM_LBUTTONDBLCLK) {
|
||
if (IsWindowVisible(hWnd)) {
|
||
g_ui_visible = false;
|
||
ShowWindow(hWnd, SW_HIDE);
|
||
} else {
|
||
ShowUI(hWnd);
|
||
}
|
||
}
|
||
return TRUE;
|
||
case WM_USER + 2:
|
||
if (g_ui_ip != 0 && g_minimize_on_connect) {
|
||
g_minimize_on_connect = false;
|
||
g_ui_visible = false;
|
||
ShowWindow(hWnd, SW_HIDE);
|
||
}
|
||
UpdateIcon(UIW_NONE);
|
||
return TRUE;
|
||
case WM_USER + 3: {
|
||
CHARRANGE cr;
|
||
cr.cpMin = -1;
|
||
cr.cpMax = -1;
|
||
// hwnd = rich edit hwnd
|
||
SendDlgItemMessage(hWnd, IDC_RICHEDIT21, EM_EXSETSEL, 0, (LPARAM)&cr);
|
||
SendDlgItemMessage(hWnd, IDC_RICHEDIT21, EM_REPLACESEL, 0, (LPARAM)lParam);
|
||
free( (void*) lParam);
|
||
return true;
|
||
}
|
||
case WM_USER + 6:
|
||
SetDlgItemText(hWnd, IDC_RICHEDIT21, "");
|
||
return true;
|
||
case WM_USER + 5:
|
||
UpdatePublicKey((char*)lParam);
|
||
return true;
|
||
case WM_USER + 4: {
|
||
UpdateStats();
|
||
return true;
|
||
}
|
||
case WM_USER + 10:
|
||
break;
|
||
|
||
case WM_USER + 11: {
|
||
uint64 now = GetTickCount64();
|
||
if (now < last_auto_service_restart + 5000) {
|
||
RERROR("Too many automatic restarts...");
|
||
StopService(UIW_STOPPED_WORKING_FAIL);
|
||
} else {
|
||
last_auto_service_restart = now;
|
||
RestartService(UIW_STOPPED_WORKING_RETRY, true);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
struct PostMsg {
|
||
int msg;
|
||
WPARAM wparam;
|
||
LPARAM lparam;
|
||
PostMsg(int a, WPARAM b, LPARAM c) : msg(a), wparam(b), lparam(c) {}
|
||
};
|
||
|
||
static HANDLE msg_event;
|
||
static CRITICAL_SECTION msg_section;
|
||
static std::vector<PostMsg> msgvect;
|
||
|
||
static DWORD WINAPI MessageThread(void *x) {
|
||
std::vector<PostMsg> proc;
|
||
for(;;) {
|
||
WaitForSingleObject(msg_event, INFINITE);
|
||
proc.clear();
|
||
EnterCriticalSection(&msg_section);
|
||
std::swap(proc, msgvect);
|
||
LeaveCriticalSection(&msg_section);
|
||
for(size_t i = 0; i != proc.size(); i++)
|
||
PostMessage(g_ui_window, proc[i].msg, proc[i].wparam, proc[i].lparam);
|
||
}
|
||
}
|
||
|
||
static void MyPostMessage(int msg, WPARAM wparam, LPARAM lparam) {
|
||
size_t count;
|
||
EnterCriticalSection(&msg_section);
|
||
count = msgvect.size();
|
||
msgvect.emplace_back(msg, wparam, lparam);
|
||
LeaveCriticalSection(&msg_section);
|
||
if (count == 0) SetEvent(msg_event);
|
||
}
|
||
|
||
static void InitMyPostMessage() {
|
||
msg_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
InitializeCriticalSection(&msg_section);
|
||
DWORD thread_id;
|
||
CloseHandle(CreateThread(NULL, 0, &MessageThread, NULL, 0, &thread_id));
|
||
}
|
||
|
||
|
||
void OsGetRandomBytes(uint8 *data, size_t data_size) {
|
||
#if defined(OS_WIN)
|
||
static BOOLEAN(APIENTRY *pfn)(void*, ULONG);
|
||
static bool resolved;
|
||
if (!resolved) {
|
||
pfn = (BOOLEAN(APIENTRY *)(void*, ULONG))GetProcAddress(LoadLibrary("ADVAPI32.DLL"), "SystemFunction036");
|
||
resolved = true;
|
||
}
|
||
if (pfn && pfn(data, (ULONG)data_size))
|
||
return;
|
||
int r = 0;
|
||
#else
|
||
int fd = open("/dev/urandom", O_RDONLY);
|
||
int r = read(fd, data, data_size);
|
||
if (r < 0) r = 0;
|
||
close(fd);
|
||
#endif
|
||
for (; r < data_size; r++)
|
||
data[r] = rand() >> 6;
|
||
}
|
||
|
||
void OsInterruptibleSleep(int millis) {
|
||
SleepEx(millis, TRUE);
|
||
}
|
||
|
||
|
||
uint64 OsGetMilliseconds() {
|
||
return GetTickCount64();
|
||
}
|
||
|
||
void OsGetTimestampTAI64N(uint8 dst[12]) {
|
||
SYSTEMTIME systime;
|
||
uint64 file_time_uint64 = 0;
|
||
GetSystemTime(&systime);
|
||
SystemTimeToFileTime(&systime, (FILETIME*)&file_time_uint64);
|
||
uint64 time_since_epoch_100ns = (file_time_uint64 - 116444736000000000);
|
||
uint64 secs_since_epoch = time_since_epoch_100ns / 10000000 + 0x400000000000000a;
|
||
uint32 nanos = (uint32)(time_since_epoch_100ns % 10000000) * 100;
|
||
WriteBE64(dst, secs_since_epoch);
|
||
WriteBE32(dst + 8, nanos);
|
||
}
|
||
|
||
|
||
|
||
void PushLine(const char *s) {
|
||
size_t l = strlen(s);
|
||
char buf[64];
|
||
SYSTEMTIME t;
|
||
|
||
GetLocalTime(&t);
|
||
|
||
snprintf(buf, sizeof(buf), "[%.2d:%.2d:%.2d] ", t.wHour, t.wMinute, t.wSecond);
|
||
size_t tl = strlen(buf);
|
||
|
||
char *x = (char*)malloc(tl + l + 3);
|
||
if (!x) return;
|
||
memcpy(x, buf, tl);
|
||
memcpy(x + tl, s, l);
|
||
x[l + tl] = '\r';
|
||
x[l + tl + 1] = '\n';
|
||
x[l + tl + 2] = '\0';
|
||
MyPostMessage(WM_USER + 3, 0, (LPARAM)x);
|
||
}
|
||
|
||
void EnsureConfigDirCreated() {
|
||
char fullname[1024];
|
||
if (GetConfigFullName("", fullname, sizeof(fullname)))
|
||
CreateDirectory(fullname, NULL);
|
||
}
|
||
|
||
void EnableControl(int wnd, bool b) {
|
||
EnableWindow(GetDlgItem(g_ui_window, wnd), b);
|
||
}
|
||
|
||
|
||
LRESULT CALLBACK NotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||
switch (uMsg) {
|
||
case WM_USER + 10:
|
||
if (wParam == 1) {
|
||
PostQuitMessage(0);
|
||
return 31337;
|
||
} else if (wParam == 0) {
|
||
ShowUI(g_ui_window);
|
||
return 31337;
|
||
}
|
||
break;
|
||
}
|
||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
}
|
||
|
||
void CreateNotificationWindow() {
|
||
WNDCLASSEX wce = {0};
|
||
wce.cbSize = sizeof(wce);
|
||
wce.lpfnWndProc = &NotifyWndProc;
|
||
wce.hInstance = g_hinstance;
|
||
wce.lpszClassName = "TunSafe-f19e092db01cbe0fb6aee132f8231e5b71c98f90";
|
||
RegisterClassEx(&wce);
|
||
CreateWindow("TunSafe-f19e092db01cbe0fb6aee132f8231e5b71c98f90", "TunSafe-f19e092db01cbe0fb6aee132f8231e5b71c98f90", 0, 0, 0, 0, 0, 0, 0, g_hinstance, NULL);
|
||
}
|
||
|
||
|
||
void CallbackUpdateUI() {
|
||
if (g_ui_visible)
|
||
MyPostMessage(WM_USER + 4, NULL, NULL);
|
||
}
|
||
|
||
void CallbackTriggerReconnect() {
|
||
PostMessage(g_ui_window, WM_USER + 11, 0, 0);
|
||
}
|
||
|
||
void CallbackSetPublicKey(const uint8 public_key[32]) {
|
||
char *str = (char*)base64_encode(public_key, 32, NULL);
|
||
PostMessage(g_ui_window, WM_USER + 5, NULL, (LPARAM)str);
|
||
}
|
||
|
||
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
|
||
g_hinstance = hInstance;
|
||
InitCpuFeatures();
|
||
|
||
// Check if the app is already running.
|
||
CreateMutexA(0, FALSE, "TunSafe-f19e092db01cbe0fb6aee132f8231e5b71c98f90");
|
||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||
HWND window = FindWindow("TunSafe-f19e092db01cbe0fb6aee132f8231e5b71c98f90", NULL);
|
||
DWORD_PTR result;
|
||
if (!window || !SendMessageTimeout(window, WM_USER + 10, 0, 0, SMTO_BLOCK, 3000, &result) || result != 31337) {
|
||
MessageBoxA(NULL, "It looks like TunSafe is already running, but not responding. Please kill the old process first.", "TunSafe", MB_ICONWARNING);
|
||
}
|
||
return 1;
|
||
}
|
||
CreateNotificationWindow();
|
||
|
||
WSADATA wsaData = {0};
|
||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||
RERROR("WSAStartup failed");
|
||
return 1;
|
||
}
|
||
|
||
LoadLibrary(TEXT("Riched20.dll"));
|
||
|
||
g_backend = new TunsafeBackendWin32();
|
||
|
||
InitMyPostMessage();
|
||
InitCommonControls();
|
||
|
||
g_icons[0] = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
|
||
g_icons[1] = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON0));
|
||
g_ui_window = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, &DlgProc);
|
||
|
||
if (!g_ui_window)
|
||
return 1;
|
||
|
||
RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\TunSafe", NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &g_reg_key, NULL);
|
||
DragAcceptFiles(g_ui_window, TRUE);
|
||
|
||
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
|
||
ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
|
||
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);
|
||
|
||
static const int ctrls[] = {IDTXT_UDP, IDTXT_TUN, IDTXT_HANDSHAKE};
|
||
for (int i = 0; i < 3; i++) {
|
||
HWND w = GetDlgItem(g_ui_window, ctrls[i]);
|
||
SetWindowLong(w, GWL_EXSTYLE, GetWindowLong(w, GWL_EXSTYLE) | WS_EX_COMPOSITED);
|
||
}
|
||
|
||
g_allow_pre_post = RegReadInt("AllowPrePost", 0) != 0;
|
||
|
||
bool minimize = false;
|
||
const char *filename = NULL;
|
||
|
||
for (size_t i = 1; i < __argc; i++) {
|
||
const char *arg = __argv[i];
|
||
|
||
if (_stricmp(arg, "/minimize") == 0) {
|
||
minimize = true;
|
||
} else if (_stricmp(arg, "/minimize_on_connect") == 0) {
|
||
g_minimize_on_connect = true;
|
||
} else if (_stricmp(arg, "/allow_pre_post") == 0) {
|
||
g_allow_pre_post = true;
|
||
} else {
|
||
filename = arg;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!minimize) {
|
||
g_ui_visible = true;
|
||
ShowWindow(g_ui_window, SW_SHOW);
|
||
}
|
||
|
||
UpdateIcon(UIW_NONE);
|
||
|
||
g_logger = &PushLine;
|
||
|
||
EnsureConfigDirCreated();
|
||
|
||
if (filename) {
|
||
LoadConfigFile(filename, false, false);
|
||
} else {
|
||
char *conf = RegReadStr("ConfigFile", "TunSafe.conf");
|
||
LoadConfigFile(conf, false, false);
|
||
free(conf);
|
||
}
|
||
|
||
// PrintCpuFeatures();
|
||
|
||
// Benchmark();
|
||
|
||
if (filename != NULL || RegReadInt("IsConnected", 0)) {
|
||
StartService();
|
||
} else {
|
||
RINFO("Press Connect to initiate a connection to the WireGuard server.");
|
||
}
|
||
|
||
MSG msg;
|
||
|
||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||
if (!IsDialogMessage(g_ui_window, &msg)) {
|
||
TranslateMessage(&msg);
|
||
DispatchMessage(&msg);
|
||
}
|
||
}
|
||
StopService(UIW_EXITING);
|
||
RemoveIcon();
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
|