diff --git a/.gitignore b/.gitignore
index ac37772..e36e0a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@
*.log
*.tar.gz
*.bak
+*.lo
+*.la

.deps
Makefile
@@ -30,3 +32,4 @@ depcomp
stamp-h1
install-sh
missing
+libtool
diff --git a/BUILD.rst b/BUILD.rst
index 9354784..3c70b52 100644
--- a/BUILD.rst
+++ b/BUILD.rst
@@ -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
diff --git a/Makefile.am b/Makefile.am
index 8301087..d8435ed 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/configure.ac b/configure.ac
index 9a2ba75..4fe6b81 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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(
diff --git a/dialer.c b/dialer.c
new file mode 100644
index 0000000..86a0f39
--- /dev/null
+++ b/dialer.c
@@ -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;
+}
diff --git a/tray.c b/tray.c
index 1e1dc2e..9ce27a0 100644
--- a/tray.c
+++ b/tray.c
@@ -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);
}

diff --git a/tray.h b/tray.h
index 1ce5e07..f5a9ff4 100644
--- a/tray.h
+++ b/tray.h
@@ -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
