@@ -10,6 +10,8 @@
*.log
*.tar.gz
*.bak
+*.lo
+*.la
.deps
Makefile
@@ -30,3 +32,4 @@ depcomp
stamp-h1
install-sh
missing
+libtool
@@ -24,6 +24,7 @@ their dependencies. You can install these packages using the standard
- mingw64-x86_64-gcc-core
- mingw64-x86_64-g++
- mingw64-x86_64-openssl
+- libtool
Build
@@ -2,6 +2,7 @@
#
# Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
# 2010 Heiko Hund <heikoh@users.sf.net>
+# Portions Copyright (C) 2018 Microsoft Corporation
#
# 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
@@ -22,6 +23,7 @@ RCCOMPILE = $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS)
AUTOMAKE_OPTIONS = foreign 1.9
+ACLOCAL_AMFLAGS = -I m4
MAINTAINERCLEANFILES = \
config.log config.status \
@@ -106,7 +108,13 @@ openvpn_gui_LDADD = \
-lnetapi32 \
-lole32 \
-lshlwapi \
- -lsecur32
+ -lsecur32 \
+ -lrasapi32
openvpn-gui-res.o: $(openvpn_gui_RESOURCES) $(srcdir)/openvpn-gui-res.h
$(RCCOMPILE) -i $< -o $@
+
+lib_LTLIBRARIES = libopenvpndialer.la
+libopenvpndialer_la_SOURCES = dialer.c
+libopenvpndialer_la_LDFLAGS = -no-undefined
+libopenvpndialer_la_LIBADD = -lrasapi32
\ No newline at end of file
@@ -2,6 +2,7 @@ dnl OpenVPN-GUI -- A Windows GUI for OpenVPN.
dnl
dnl Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
dnl 2010 Heiko Hund <heikoh@users.sf.net>
+dnl Portions Copyright (C) 2018 Microsoft Corporation
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
@@ -29,10 +30,13 @@ AC_DEFINE([CORE_COPYRIGHT_YEAR_END], ["2018"], [Last copyright year for daemon i
AC_CONFIG_AUX_DIR([.])
AM_CONFIG_HEADER([config.h])
AC_CONFIG_SRCDIR([main.h])
+AC_CONFIG_MACRO_DIRS([m4])
AM_INIT_AUTOMAKE
AC_CANONICAL_HOST
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC_C99
+LT_INIT([win32-dll])
+AC_LIBTOOL_WIN32_DLL
AC_CHECK_TOOL([WINDRES], [windres])
AC_ARG_ENABLE(
new file mode 100644
@@ -0,0 +1,171 @@
+/*
+ * OpenVPN-GUI -- A Windows GUI for OpenVPN.
+ *
+ * Copyright (C) 2018 Microsoft Corporation
+ *
+ * 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 <config.h>
+#endif
+
+#include <windows.h>
+#include <tchar.h>
+#include <ras.h>
+#include <rasdlg.h>
+#include <raserror.h>
+
+__declspec(dllexport)
+DWORD WINAPI RasCustomDial(
+ HINSTANCE hInstDll __attribute__((unused)),
+ LPRASDIALEXTENSIONS lpRasDialExtensions __attribute__((unused)),
+ LPWSTR lpszPhonebook __attribute__((unused)),
+ LPRASDIALPARAMSW lpRasDialParams __attribute__((unused)),
+ DWORD dwNotifierType __attribute__((unused)),
+ LPVOID lpvNotifier __attribute__((unused)),
+ LPHRASCONN lphRasConn __attribute__((unused)),
+ DWORD dwFlags __attribute__((unused)))
+{
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/* Copied from OpenVPN-GUI main.c */
+static const TCHAR OpenVPNGuiClassName[] = _T("OpenVPN-GUI");
+
+/* Copied from OpenVPN-GUI tray.h */
+#define IDM_CONNECTMENU 300
+
+static const TCHAR WindowCaption[] = _T("Walrus VPN");
+
+__declspec(dllexport)
+BOOL WINAPI RasCustomDialDlg(
+ HINSTANCE hInstDll __attribute__((unused)),
+ DWORD dwFlags __attribute__((unused)),
+ LPWSTR lpszwPhonebook,
+ LPWSTR lpszwEntry,
+ LPWSTR lpszwPhoneNumber __attribute__((unused)),
+ LPRASDIALDLG lpInfo,
+ PVOID pVoid __attribute__((unused)))
+{
+ HWND window = FindWindow(OpenVPNGuiClassName, NULL);
+ if (NULL == window)
+ {
+ MessageBox(NULL, _T("OpenVPN-GUI is not running. Please launch the GUI."), WindowCaption, MB_OK);
+ lpInfo->dwError = ERROR_UNKNOWN;
+ return FALSE;
+ }
+ else
+ {
+ DWORD result;
+ DWORD entrySize = 0;
+ LPRASENTRYW entry = NULL;
+
+ result = RasGetEntryPropertiesW(lpszwPhonebook, lpszwEntry, NULL, &entrySize, NULL, NULL);
+ if (ERROR_SUCCESS != result && ERROR_BUFFER_TOO_SMALL != result)
+ {
+ MessageBox(NULL, _T("Could not get size of VPN entry"), WindowCaption, MB_OK);
+ lpInfo->dwError = result;
+ return FALSE;
+ }
+
+ entry = (LPRASENTRYW)malloc(entrySize);
+ if (NULL == entry)
+ {
+ lpInfo->dwError = ERROR_OUTOFMEMORY;
+ return FALSE;
+ }
+
+ memset(entry, 0, entrySize);
+ entry->dwSize = sizeof(RASENTRYW);
+
+ result = RasGetEntryPropertiesW(lpszwPhonebook, lpszwEntry, entry, &entrySize, NULL, NULL);
+ if (ERROR_SUCCESS != result)
+ {
+ MessageBox(NULL, _T("Could not get VPN entry"), WindowCaption, MB_OK);
+ lpInfo->dwError = result;
+ free(entry);
+ return FALSE;
+ }
+
+ /* Convert the number in the local phone number field to get the offset. */
+ int offset = _wtoi(entry->szLocalPhoneNumber);
+
+ /* Sanity check: There should not be more than 10 entries, and the value should never be negative.*/
+ if (0 > offset || 10 < offset)
+ {
+ MessageBox(NULL, _T("Offset is invalid number"), WindowCaption, MB_OK);
+ lpInfo->dwError = ERROR_UNKNOWN;
+ free(entry);
+ return FALSE;
+ }
+
+ if (0 == offset)
+ {
+ /* 0 is also used for an error condition. If that's the case, check that the string is actually
+ * a zero.
+ */
+ if (entry->szLocalPhoneNumber[0] != L'0' && entry->szLocalPhoneNumber[1] != L'\0')
+ {
+ MessageBox(NULL, _T("Offset not recognized as a number"), WindowCaption, MB_OK);
+ lpInfo->dwError = ERROR_UNKNOWN;
+ free(entry);
+ return FALSE;
+ }
+ }
+
+ /* No longer need entry at this point. */
+ free(entry);
+
+ if (FALSE == PostMessage(window, WM_COMMAND, IDM_CONNECTMENU + offset, 0))
+ {
+ MessageBox(NULL, _T("Could not communicate with OpenVPN-GUI"), WindowCaption, MB_OK);
+ lpInfo->dwError = GetLastError();
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+}
+
+__declspec(dllexport)
+DWORD WINAPI RasCustomHangUp(
+ HRASCONN hRasConn __attribute__((unused)))
+{
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+__declspec(dllexport)
+BOOL WINAPI RasCustomEntryDlg(HINSTANCE hInstDll __attribute__((unused)),
+ LPWSTR lpszwPhonebook __attribute__((unused)),
+ LPWSTR lpszwEntry __attribute__((unused)),
+ LPRASENTRYDLG lpInfo,
+ DWORD dwFlags __attribute__((unused)))
+{
+ lpInfo->dwError = ERROR_CALL_NOT_IMPLEMENTED;
+ return FALSE;
+}
+
+__declspec(dllexport)
+DWORD CALLBACK RasCustomDeleteEntryNotify(
+ LPCTSTR lpszPhonebook __attribute__((unused)),
+ LPCTSTR lpszEntry __attribute__((unused)),
+ DWORD dwFlags __attribute__((unused)))
+{
+ return ERROR_SUCCESS;
+}
@@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
* 2010 Heiko Hund <heikoh@users.sf.net>
+ * Portions Copyright (C) 2018 Microsoft Corporation
*
* 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
@@ -28,6 +29,8 @@
#include <shellapi.h>
#include <tchar.h>
#include <time.h>
+#include <ras.h>
+#include <raserror.h>
#include "tray.h"
#include "service.h"
@@ -47,6 +50,116 @@ HMENU hMenuService;
NOTIFYICONDATA ni;
extern options_t o;
+static const TCHAR VpnEntryPrefix[] = _T("OpenVPN ");
+static const TCHAR WindowCaption[] = _T("OpenVPN-GUI");
+
+/* Delete OpenVPN entries from Windows networking/VPN flyout */
+void
+ClearNetworkFlyout()
+{
+ DWORD result = ERROR_SUCCESS;
+ LPRASENTRYNAME entryName = NULL;
+ DWORD entryNameSize = 0;
+ DWORD entryCount = 0;
+
+ /* Determine memory required */
+ result = RasEnumEntries(NULL, NULL, NULL, &entryNameSize, &entryCount);
+ if (ERROR_BUFFER_TOO_SMALL != result)
+ {
+ /* Anything else is unexpected. */
+ MessageBox(NULL, _T("ClearNetworkFlyout: Could not get memory needed for RAS entries"), WindowCaption, MB_OK);
+ return;
+ }
+
+ entryName = (LPRASENTRYNAME)malloc(entryNameSize);
+ if (NULL == entryName)
+ {
+ /* No memory! */
+ MessageBox(NULL, _T("ClearNetworkFlyout: No memory allocating entry name"), WindowCaption, MB_OK);
+ return;
+ }
+
+ entryName[0].dwSize = sizeof(RASENTRYNAME);
+
+ result = RasEnumEntries(NULL, NULL, entryName, &entryNameSize, &entryCount);
+ if (ERROR_SUCCESS != result)
+ {
+ MessageBox(NULL, _T("ClearNetworkFlyout: Could not enumerate RAS entries"), WindowCaption, MB_OK);
+ free(entryName);
+ return;
+ }
+
+ for (DWORD i = 0; i < entryCount; i++)
+ {
+ if ((entryName[i].dwFlags == REN_User) &&
+ (0 == _tcsncmp(VpnEntryPrefix, entryName[i].szEntryName, _countof(VpnEntryPrefix) - 1)))
+ {
+ /* TODO: Retrieve the entry and verify that it references our custom dialer DLL? RasEnumEntries doesn't
+ * give us this information but we could retrieve the entry and check. This would keep us from purging any
+ * entries which for some inexplicable reason started with the VpnEntryPrefix string.
+ */
+ result = RasDeleteEntry(NULL, entryName[i].szEntryName);
+ if (ERROR_SUCCESS != result)
+ {
+ MessageBox(NULL, _T("ClearNetworkFlyout: Failed to delete entry"), WindowCaption, MB_OK);
+ /* Continue processing; don't return. */
+ }
+ }
+ }
+
+ free(entryName);
+
+ return;
+}
+
+static const TCHAR DialerFileName[] = _T("libopenvpndialer-0.dll");
+static const TCHAR DeviceName[] = _T("TAP-Windows Adapter V9");
+
+/* Create VPN flyout entry for a new connection */
+static void
+CreateNetworkFlyoutEntry(const connection_t *c, int number)
+{
+ DWORD result = ERROR_SUCCESS;
+ RASENTRY entry;
+ TCHAR entryName[RAS_MaxEntryName + 1];
+
+ memset(&entry, 0, sizeof(entry));
+ entry.dwSize = sizeof(entry);
+ _tcsncpy(entry.szDeviceType, RASDT_Vpn, _countof(RASDT_Vpn));
+ _tcsncpy(entry.szDeviceName, DeviceName, _countof(DeviceName));
+
+ entry.dwfOptions = RASEO_Custom;
+ /* Take the exe_path, strip off everything after the last backslash, and append DialerFileName */
+ TCHAR* lastBackslash = _tcsrchr(o.exe_path, _T('\\'));
+ if (NULL == lastBackslash)
+ {
+ MessageBox(NULL, _T("CreateNetworkFlyoutEntry: No last backslash found"), WindowCaption, MB_OK);
+ return;
+ }
+ _tcsncpy(entry.szCustomDialDll, o.exe_path, (lastBackslash - o.exe_path + 1));
+ /* Check for possible overrun */
+ if (_tcslen(entry.szCustomDialDll) + _countof(DialerFileName) >= _countof(entry.szCustomDialDll))
+ {
+ MessageBox(NULL, _T("CreateNetworkFlyoutEntry: Not enough space for custom dialer DLL filename"), WindowCaption, MB_OK);
+ return;
+ }
+ _tcsncat(entry.szCustomDialDll, DialerFileName, _countof(DialerFileName));
+
+ /* Use the local phone number space to store the entry number for the custom dialer to use. */
+ _sntprintf_0(entry.szLocalPhoneNumber, _T("%d"), number);
+ entry.dwType = RASET_Vpn;
+ entry.dwEncryptionType = ET_Optional;
+
+ _sntprintf_0(entryName, _T("%s%s"), VpnEntryPrefix, c->config_name);
+ result = RasSetEntryProperties(NULL, entryName, &entry, sizeof(entry), NULL, 0);
+ if (ERROR_SUCCESS != result)
+ {
+ TCHAR message[512];
+ _sntprintf_0(message, _T("CreateNetworkFlyoutEntry: Could not RasSetEntryProperties: %lu"), result);
+ MessageBox(NULL, message, WindowCaption, MB_OK);
+ return;
+ }
+}
/* Create popup menus */
void
@@ -92,6 +205,7 @@ CreatePopupMenus()
AppendMenu(hMenu, MF_STRING ,IDM_CLOSE, LoadLocalizedString(IDS_MENU_CLOSE));
SetMenuStatus(&o.conn[0], o.conn[0].state);
+ CreateNetworkFlyoutEntry(&o.conn[0], 0);
}
else {
/* Create Main menu with all connections */
@@ -135,6 +249,7 @@ CreatePopupMenus()
#endif
SetMenuStatus(&o.conn[i], o.conn[i].state);
+ CreateNetworkFlyoutEntry(&o.conn[i], i);
}
}
@@ -152,6 +267,7 @@ DestroyPopupMenus()
DestroyMenu(hMenuService);
DestroyMenu(hMenu);
+ ClearNetworkFlyout();
}
@@ -218,6 +334,7 @@ void
OnDestroyTray()
{
DestroyMenu(hMenu);
+ ClearNetworkFlyout();
Shell_NotifyIcon(NIM_DELETE, &ni);
}
@@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
* 2010 Heiko Hund <heikoh@users.sf.net>
+ * Portions Copyright (C) 2018 Microsoft Corporation
*
* 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
@@ -51,5 +52,6 @@ void SetMenuStatus(connection_t *, conn_state_t);
void SetServiceMenuStatus();
void ShowTrayBalloon(TCHAR *, TCHAR *);
void CheckAndSetTrayIcon();
+void ClearNetworkFlyout();
#endif
Add a DLL to be wired in as a custom dialer, which introduces new build dependencies Add copyright notices as required where Microsoft has contributed code Signed-off-by: Kevin Kane <kkane@microsoft.com> --- .gitignore | 3 + BUILD.rst | 1 + Makefile.am | 10 ++- configure.ac | 4 ++ dialer.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ tray.c | 117 +++++++++++++++++++++++++++++++++++ tray.h | 2 + 7 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 dialer.c