/* * OpenVPN-GUI -- A Windows GUI for OpenVPN. * * Copyright (C) 2004 Mathias Sundman * 2011 Heiko Hund * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "main.h" #include "options.h" #include "registry.h" #include "proxy.h" #include "openvpn-gui-res.h" #include "localization.h" #include "manage.h" #include "openvpn.h" #include "misc.h" extern options_t o; INT_PTR CALLBACK ProxySettingsDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, UNUSED LPARAM lParam) { HICON hIcon; LPPSHNOTIFY psn; switch (msg) { case WM_INITDIALOG: hIcon = LoadLocalizedIcon(ID_ICO_APP); if (hIcon) { SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_SMALL), (LPARAM) (hIcon)); SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_BIG), (LPARAM) (hIcon)); } /* Limit Port editbox to 5 chars. */ SendMessage(GetDlgItem(hwndDlg, ID_EDT_PROXY_PORT), EM_SETLIMITTEXT, 5, 0); LoadProxySettings(hwndDlg); break; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_RB_PROXY_OPENVPN: if (HIWORD(wParam) == BN_CLICKED) { EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_HTTP), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_SOCKS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_ADDRESS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_PORT), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_ADDRESS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_PORT), FALSE); } break; case ID_RB_PROXY_MSIE: if (HIWORD(wParam) == BN_CLICKED) { EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_HTTP), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_SOCKS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_ADDRESS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_PORT), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_ADDRESS), FALSE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_PORT), FALSE); } break; case ID_RB_PROXY_MANUAL: if (HIWORD(wParam) == BN_CLICKED) { EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_HTTP), TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_RB_PROXY_SOCKS), TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_ADDRESS), TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_EDT_PROXY_PORT), TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_ADDRESS), TRUE); EnableWindow(GetDlgItem(hwndDlg, ID_TXT_PROXY_PORT), TRUE); } break; case ID_RB_PROXY_HTTP: if (HIWORD(wParam) == BN_CLICKED) { SetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_http_address); SetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_http_port); } break; case ID_RB_PROXY_SOCKS: if (HIWORD(wParam) == BN_CLICKED) { SetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_socks_address); SetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_socks_port); } break; } break; case WM_NOTIFY: psn = (LPPSHNOTIFY) lParam; if (psn->hdr.code == (UINT) PSN_KILLACTIVE) { SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (CheckProxySettings(hwndDlg) ? FALSE : TRUE)); return TRUE; } else if (psn->hdr.code == (UINT) PSN_APPLY) { SaveProxySettings(hwndDlg); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); return TRUE; } break; case WM_CLOSE: EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } return FALSE; } /* Check that proxy settings are valid */ int CheckProxySettings(HWND hwndDlg) { if (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_MANUAL) == BST_CHECKED) { TCHAR text[100]; BOOL http = (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_HTTP) == BST_CHECKED); GetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, text, _countof(text)); if (_tcslen(text) == 0) { /* proxy address not specified */ ShowLocalizedMsg((http ? IDS_ERR_HTTP_PROXY_ADDRESS : IDS_ERR_SOCKS_PROXY_ADDRESS)); return 0; } GetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, text, _countof(text)); if (_tcslen(text) == 0) { /* proxy port not specified */ ShowLocalizedMsg((http ? IDS_ERR_HTTP_PROXY_PORT : IDS_ERR_SOCKS_PROXY_PORT)); return 0; } long port = _tcstol(text, NULL, 10); if ((port < 1) || (port > 65535)) { /* proxy port range error */ ShowLocalizedMsg((http ? IDS_ERR_HTTP_PROXY_PORT_RANGE : IDS_ERR_SOCKS_PROXY_PORT_RANGE)); return 0; } } return 1; } void LoadProxySettings(HWND hwndDlg) { /* Set Proxy type, address and port */ if (o.proxy_type == http) { CheckRadioButton(hwndDlg, ID_RB_PROXY_HTTP, ID_RB_PROXY_SOCKS, ID_RB_PROXY_HTTP); SetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_http_address); SetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_http_port); } else if (o.proxy_type == socks) { CheckRadioButton(hwndDlg, ID_RB_PROXY_HTTP, ID_RB_PROXY_SOCKS, ID_RB_PROXY_SOCKS); SetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_socks_address); SetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_socks_port); } /* Set Proxy Settings Source */ if (o.proxy_source == config) { SendMessage(GetDlgItem(hwndDlg, ID_RB_PROXY_OPENVPN), BM_CLICK, 0, 0); } else if (o.proxy_source == windows) { SendMessage(GetDlgItem(hwndDlg, ID_RB_PROXY_MSIE), BM_CLICK, 0, 0); } else if (o.proxy_source == manual) { SendMessage(GetDlgItem(hwndDlg, ID_RB_PROXY_MANUAL), BM_CLICK, 0, 0); } } void SaveProxySettings(HWND hwndDlg) { HKEY regkey; DWORD dwDispos; TCHAR proxy_source_string[2] = _T("0"); TCHAR proxy_type_string[2] = _T("0"); TCHAR proxy_subkey[MAX_PATH]; /* Save Proxy Settings Source */ if (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_OPENVPN) == BST_CHECKED) { o.proxy_source = config; proxy_source_string[0] = _T('0'); } else if (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_MSIE) == BST_CHECKED) { o.proxy_source = windows; proxy_source_string[0] = _T('1'); } else if (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_MANUAL) == BST_CHECKED) { o.proxy_source = manual; proxy_source_string[0] = _T('2'); } /* Save Proxy type, address and port */ if (IsDlgButtonChecked(hwndDlg, ID_RB_PROXY_HTTP) == BST_CHECKED) { o.proxy_type = http; proxy_type_string[0] = _T('0'); GetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_http_address, _countof(o.proxy_http_address)); GetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_http_port, _countof(o.proxy_http_port)); } else { o.proxy_type = socks; proxy_type_string[0] = _T('1'); GetDlgItemText(hwndDlg, ID_EDT_PROXY_ADDRESS, o.proxy_socks_address, _countof(o.proxy_socks_address)); GetDlgItemText(hwndDlg, ID_EDT_PROXY_PORT, o.proxy_socks_port, _countof(o.proxy_socks_port)); } /* Open Registry for writing */ _sntprintf_0(proxy_subkey, _T("%ls\\proxy"), GUI_REGKEY_HKCU); if (RegCreateKeyEx(HKEY_CURRENT_USER, proxy_subkey, 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®key, &dwDispos) != ERROR_SUCCESS) { /* error creating Registry-Key */ ShowLocalizedMsg(IDS_ERR_CREATE_REG_HKCU_KEY, proxy_subkey); return; } /* Save Settings to registry */ SetRegistryValue(regkey, _T("proxy_source"), proxy_source_string); SetRegistryValue(regkey, _T("proxy_type"), proxy_type_string); SetRegistryValue(regkey, _T("proxy_http_address"), o.proxy_http_address); SetRegistryValue(regkey, _T("proxy_http_port"), o.proxy_http_port); SetRegistryValue(regkey, _T("proxy_socks_address"), o.proxy_socks_address); SetRegistryValue(regkey, _T("proxy_socks_port"), o.proxy_socks_port); RegCloseKey(regkey); } void GetProxyRegistrySettings() { LONG status; HKEY regkey; TCHAR proxy_source_string[2] = _T("0"); TCHAR proxy_type_string[2] = _T("0"); TCHAR proxy_subkey[MAX_PATH]; /* Open Registry for reading */ _sntprintf_0(proxy_subkey, _T("%ls\\proxy"), GUI_REGKEY_HKCU); status = RegOpenKeyEx(HKEY_CURRENT_USER, proxy_subkey, 0, KEY_READ, ®key); if (status != ERROR_SUCCESS) { return; } /* get registry settings */ GetRegistryValue(regkey, _T("proxy_http_address"), o.proxy_http_address, _countof(o.proxy_http_address)); GetRegistryValue(regkey, _T("proxy_http_port"), o.proxy_http_port, _countof(o.proxy_http_port)); GetRegistryValue(regkey, _T("proxy_socks_address"), o.proxy_socks_address, _countof(o.proxy_socks_address)); GetRegistryValue(regkey, _T("proxy_socks_port"), o.proxy_socks_port, _countof(o.proxy_socks_port)); GetRegistryValue(regkey, _T("proxy_source"), proxy_source_string, _countof(proxy_source_string)); GetRegistryValue(regkey, _T("proxy_type"), proxy_type_string, _countof(proxy_type_string)); if (proxy_source_string[0] == _T('0')) { o.proxy_source = config; } else if (proxy_source_string[0] == _T('1')) { o.proxy_source = windows; } else if (proxy_source_string[0] == _T('2')) { o.proxy_source = manual; } o.proxy_type = (proxy_type_string[0] == _T('0') ? http : socks); RegCloseKey(regkey); } INT_PTR CALLBACK ProxyAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { LPCSTR proxy_type; connection_t *c; char fmt[32]; switch (msg) { case WM_INITDIALOG: /* Set connection for this dialog and show it */ c = (connection_t *) lParam; TRY_SETPROP(hwndDlg, cfgProp, (HANDLE) c); if (c->state == resuming) { ForceForegroundWindow(hwndDlg); } else { SetForegroundWindow(hwndDlg); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_EDT_PROXY_USER: if (HIWORD(wParam) == EN_UPDATE) { int len = Edit_GetTextLength((HWND) lParam); EnableWindow(GetDlgItem(hwndDlg, IDOK), (len ? TRUE : FALSE)); } break; case IDOK: c = (connection_t *) GetProp(hwndDlg, cfgProp); proxy_type = (c->proxy_type == http ? "HTTP" : "SOCKS"); snprintf(fmt, sizeof(fmt), "username \"%s Proxy\" \"%%s\"", proxy_type); ManagementCommandFromInput(c, fmt, hwndDlg, ID_EDT_PROXY_USER); snprintf(fmt, sizeof(fmt), "password \"%s Proxy\" \"%%s\"", proxy_type); ManagementCommandFromInput(c, fmt, hwndDlg, ID_EDT_PROXY_PASS); EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } break; case WM_OVPN_STATE: /* state changed -- destroy the dialog */ EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; case WM_CLOSE: EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; case WM_NCDESTROY: RemoveProp(hwndDlg, cfgProp); break; } return FALSE; } void QueryProxyAuth(connection_t *c, proxy_t type) { c->proxy_type = type; LocalizedDialogBoxParamEx(ID_DLG_PROXY_AUTH, c->hwndStatus, ProxyAuthDialogFunc, (LPARAM) c); } typedef enum { HTTPS_URL, SOCKS_URL } url_scheme; static LPCWSTR UrlSchemeStr(const url_scheme scheme) { static LPCWSTR scheme_strings[] = { L"https", L"socks" }; return scheme_strings[scheme]; } static LPWSTR QueryWindowsProxySettings(const url_scheme scheme, LPCSTR host) { LPWSTR proxy = NULL; BOOL auto_detect = TRUE; LPWSTR auto_config_url = NULL; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config; if (WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) { proxy = proxy_config.lpszProxy; auto_detect = proxy_config.fAutoDetect; auto_config_url = proxy_config.lpszAutoConfigUrl; GlobalFree(proxy_config.lpszProxyBypass); } if (auto_detect) { LPWSTR old_url = auto_config_url; DWORD flags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; if (WinHttpDetectAutoProxyConfigUrl(flags, &auto_config_url)) { GlobalFree(old_url); } } if (auto_config_url) { HINTERNET session = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (session) { int size = _snwprintf(NULL, 0, L"%ls://%hs", UrlSchemeStr(scheme), host) + 1; LPWSTR url = malloc(size * sizeof(WCHAR)); if (url) { _snwprintf(url, size, L"%ls://%hs", UrlSchemeStr(scheme), host); LPWSTR old_proxy = proxy; WINHTTP_PROXY_INFO proxy_info; WINHTTP_AUTOPROXY_OPTIONS options = { .fAutoLogonIfChallenged = TRUE, .dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL, .lpszAutoConfigUrl = auto_config_url, .dwAutoDetectFlags = 0, .lpvReserved = NULL, .dwReserved = 0 }; if (WinHttpGetProxyForUrl(session, url, &options, &proxy_info)) { GlobalFree(old_proxy); GlobalFree(proxy_info.lpszProxyBypass); proxy = proxy_info.lpszProxy; } free(url); } WinHttpCloseHandle(session); } GlobalFree(auto_config_url); } return proxy; } static VOID ParseProxyString(LPWSTR proxy_str, url_scheme scheme, LPCSTR *type, LPCWSTR *host, LPCWSTR *port) { if (proxy_str == NULL) { return; } LPCWSTR delim = L"; "; LPWSTR ctx = NULL; LPWSTR token = wcstok_s(proxy_str, delim, &ctx); LPCWSTR scheme_str = UrlSchemeStr(scheme); LPCWSTR socks_str = UrlSchemeStr(SOCKS_URL); /* Token format: [=]["://"][":"] */ while (token) { BOOL match = FALSE; LPWSTR eq = wcschr(token, '='); LPWSTR css = wcsstr(token, L"://"); /* * If the token has a , test for the one we're looking for. * If we're looking for a https proxy, socks will also do. * If it's a proxy without a it's only good for https. */ if (eq || css) { if (wcsbegins(token, scheme_str)) { match = TRUE; } else if (scheme == HTTPS_URL && wcsbegins(token, socks_str)) { match = TRUE; scheme = SOCKS_URL; } } else if (scheme == HTTPS_URL) { match = TRUE; } if (match) { LPWSTR server = token; if (css) { server = css + 3; } else if (eq) { server = eq + 1; } /* IPv6 addresses are surrounded by brackets */ LPWSTR port_delim; if (server[0] == '[') { server += 1; LPWSTR end = wcschr(server, ']'); if (end == NULL) { continue; } *end++ = '\0'; port_delim = (*end == ':' ? end : NULL); } else { port_delim = wcsrchr(server, ':'); if (port_delim) { *port_delim = '\0'; } } *type = (scheme == HTTPS_URL ? "HTTP" : "SOCKS"); *host = server; if (port_delim) { *port = port_delim + 1; } else { *port = (scheme == HTTPS_URL ? L"80" : L"1080"); } break; } token = wcstok_s(NULL, delim, &ctx); } } /* * Respond to management interface PROXY notifications * Input format: REMOTE_NO,PROTOCOL,HOST */ void OnProxy(connection_t *c, char *line) { LPSTR proto, host; char *pos = strchr(line, ','); if (pos == NULL) { return; } proto = ++pos; pos = strchr(pos, ','); if (pos == NULL) { return; } *pos = '\0'; host = ++pos; if (host[0] == '\0') { return; } LPCSTR type = "NONE"; LPCWSTR addr = L"", port = L""; LPWSTR proxy_str = NULL; if (o.proxy_source == manual) { if (o.proxy_type == http && streq(proto, "TCP")) { type = "HTTP"; addr = o.proxy_http_address; port = o.proxy_http_port; } else if (o.proxy_type == socks) { type = "SOCKS"; addr = o.proxy_socks_address; port = o.proxy_socks_port; } } else if (o.proxy_source == windows) { url_scheme scheme = (streq(proto, "TCP") ? HTTPS_URL : SOCKS_URL); proxy_str = QueryWindowsProxySettings(scheme, host); ParseProxyString(proxy_str, scheme, &type, &addr, &port); } char cmd[128]; snprintf(cmd, sizeof(cmd), "proxy %s %ls %ls", type, addr, port); cmd[sizeof(cmd) - 1] = '\0'; ManagementCommand(c, cmd, NULL, regular); GlobalFree(proxy_str); }