diff --git a/Makefile.am b/Makefile.am index 457dcb0..eac78c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -91,7 +91,8 @@ openvpn_gui_LDADD = \ openvpn-gui-res.o \ $(OPENSSL_CRYPTO_LIBS) \ -lws2_32 \ - -lcomctl32 + -lcomctl32 \ + -lwinhttp openvpn-gui-res.o: $(openvpn_gui_RESOURCES) $(srcdir)/openvpn-gui-res.h $(RCCOMPILE) -i $< -o $@ diff --git a/main.c b/main.c index 398b140..f5cdcad 100644 --- a/main.c +++ b/main.c @@ -105,6 +105,7 @@ int WINAPI _tWinMain (HINSTANCE hThisInstance, { log, OnLogLine }, { state, OnStateChange }, { password, OnPassword }, + { proxy, OnProxy }, { stop, OnStop }, { 0, NULL } }; diff --git a/manage.c b/manage.c index 17072cf..49dcdf7 100644 --- a/manage.c +++ b/manage.c @@ -256,6 +256,11 @@ OnManagement(SOCKET sk, LPARAM lParam) if (rtmsg_handler[password]) rtmsg_handler[password](c, pos + 9); } + else if (strncmp(pos, "PROXY:", 6) == 0) + { + if (rtmsg_handler[proxy]) + rtmsg_handler[proxy](c, pos + 6); + } else if (strncmp(pos, "INFO:", 5) == 0) { /* delay until management interface accepts input */ diff --git a/manage.h b/manage.h index 952512f..7e424ed 100644 --- a/manage.h +++ b/manage.h @@ -34,6 +34,7 @@ typedef enum { hold, log, password, + proxy, state, needok, needstr, diff --git a/misc.c b/misc.c index 9d76a5f..6a4127d 100644 --- a/misc.c +++ b/misc.c @@ -102,3 +102,24 @@ ManagementCommandFromInput(connection_t *c, LPSTR fmt, HWND hDlg, int id) return retval; } + +/* + * Various string helper functions + */ +BOOL +streq(LPCSTR str1, LPCSTR str2) +{ + return (strcmp(str1, str2) == 0); +} + +BOOL +wcsbegins(LPCWSTR str, LPCWSTR begin) +{ + return (wcsncmp(str, begin, wcslen(begin)) == 0); +} + +BOOL +wcseq(LPCWSTR str1, LPCWSTR str2) +{ + return (wcscmp(str1, str2) == 0); +} diff --git a/misc.h b/misc.h index 48da5a2..4a351ff 100644 --- a/misc.h +++ b/misc.h @@ -24,4 +24,8 @@ BOOL ManagementCommandFromInput(connection_t *, LPSTR, HWND, int); +BOOL streq(LPCSTR, LPCSTR); +BOOL wcsbegins(LPCWSTR, LPCWSTR); +BOOL wcseq(LPCWSTR, LPCWSTR); + #endif diff --git a/openvpn.c b/openvpn.c index a63d872..959f46d 100644 --- a/openvpn.c +++ b/openvpn.c @@ -621,7 +621,6 @@ StartOpenVPN(connection_t *c) { TCHAR cmdline[1024]; TCHAR *options = cmdline + 8; - TCHAR proxy_string[100]; TCHAR exit_event_name[17]; HANDLE hStdInRead = NULL, hStdInWrite = NULL; HANDLE hNul = NULL, hThread = NULL, service = NULL; @@ -659,18 +658,14 @@ StartOpenVPN(connection_t *c) /* Create a management interface password */ GetRandomPassword(c->manage.password, sizeof(c->manage.password) - 1); - /* Construct proxy string to append to command line */ - ConstructProxyCmdLine(proxy_string, _countof(proxy_string)); - /* Construct command line */ _sntprintf_0(cmdline, _T("openvpn " - "--config \"%s\" %s --service %s 0 --log%s \"%s\" " - "--management %S %hd stdin --auth-retry interact " - "--management-hold --management-query-passwords --tls-exit"), - c->config_file, proxy_string, exit_event_name, - (o.append_string[0] == '1' ? _T("-append") : _T("")), - c->log_path, inet_ntoa(c->manage.skaddr.sin_addr), - ntohs(c->manage.skaddr.sin_port)); + "--config \"%s\" --service %s 0 --log%s \"%s\" --auth-retry interact " + "--management %S %hd stdin --management-query-passwords %s" + "--management-hold --tls-exit"), c->config_file, exit_event_name, + (o.append_string[0] == '1' ? _T("-append") : _T("")), c->log_path, + inet_ntoa(c->manage.skaddr.sin_addr), ntohs(c->manage.skaddr.sin_port), + (o.proxy_source != config ? _T("--management-query-proxy ") : _T(""))); /* Try to open the service pipe */ service = CreateFile(_T("\\\\.\\pipe\\openvpn\\service"), diff --git a/proxy.c b/proxy.c index fb779c4..8590f1e 100644 --- a/proxy.c +++ b/proxy.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include "main.h" @@ -361,28 +361,211 @@ ProxyAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) } -/* - * Construct the proxy options to append to the cmd-line. - */ -void ConstructProxyCmdLine(TCHAR *proxy_string, unsigned int size) +typedef enum { HTTPS_URL, SOCKS_URL } url_scheme; +static LPCWSTR +UrlSchemeStr(const url_scheme scheme) { - proxy_string[0] = _T('\0'); + 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"%s://%S", UrlSchemeStr(scheme), host) + 1; + LPWSTR url = malloc(size * sizeof(WCHAR)); + if (url) + { + _snwprintf(url, size, L"%s://%S", 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) +{ + LPCWSTR delim = L"; "; + LPWSTR token = wcstok(proxy_str, delim); + + 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(NULL, delim); + } +} + + +/* + * 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) + if (o.proxy_type == http && streq(proto, "TCP")) { - __sntprintf_0(proxy_string, size, _T(" --http-proxy %s %s auto"), - o.proxy_http_address, o.proxy_http_port); + type = "HTTP"; + addr = o.proxy_http_address; + port = o.proxy_http_port; } else if (o.proxy_type == socks) { - __sntprintf_0(proxy_string, size, _T(" --socks-proxy %s %s"), - o.proxy_socks_address, o.proxy_socks_port); + type = "SOCKS"; + addr = o.proxy_socks_address; + port = o.proxy_socks_port; } } else if (o.proxy_source == windows) { - __sntprintf_0(proxy_string, size, _T(" --auto-proxy")); + 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 %S %S", type, addr, port); + cmd[sizeof(cmd) - 1] = '\0'; + ManagementCommand(c, cmd, NULL, regular); + + GlobalFree(proxy_str); } diff --git a/proxy.h b/proxy.h index 3784451..338ca6e 100644 --- a/proxy.h +++ b/proxy.h @@ -19,10 +19,17 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -INT_PTR CALLBACK ProxySettingsDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -int CheckProxySettings(HWND hwndDlg); -void LoadProxySettings(HWND hwndDlg); -void SaveProxySettings(HWND hwndDlg); +#ifndef PROXY_H +#define PROXY_H + +INT_PTR CALLBACK ProxySettingsDialogFunc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK ProxyAuthDialogFunc(HWND, UINT, WPARAM, LPARAM); + +void OnProxy(connection_t *, char *); + +int CheckProxySettings(HWND); +void LoadProxySettings(HWND); +void SaveProxySettings(HWND); void GetProxyRegistrySettings(); -INT_PTR CALLBACK ProxyAuthDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -void ConstructProxyCmdLine(TCHAR *proxy_string_ptr, unsigned int size); + +#endif