1144 lines
32 KiB
C++
1144 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;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|