Skip to content

Commit

Permalink
Set IPv6 DNS servers using interactive service
Browse files Browse the repository at this point in the history
- Any existing addresses are deleted before adding
- On close_tun all addresses are deleted (only if any were added)

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1479958527-29491-1-git-send-email-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg13222.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
  • Loading branch information
selvanair authored and cron2 committed Nov 24, 2016
1 parent e2ffdb7 commit c098016
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 7 deletions.
2 changes: 1 addition & 1 deletion doc/openvpn.8
Expand Up @@ -5696,7 +5696,7 @@ this option to set secondary DNS server addresses.
Set primary domain name server IPv6 address. Repeat
this option to set secondary DNS server IPv6 addresses.

Note: currently this is handled using netsh and requires admin rights (the
Note: currently this is handled using netsh (the
existing DHCP code can only do IPv4 DHCP, and that protocol only
permits IPv4 addresses anywhere). The option will be put into the
environment, so an
Expand Down
7 changes: 3 additions & 4 deletions include/openvpn-msg.h
Expand Up @@ -79,10 +79,9 @@ typedef struct {
message_header_t header;
interface_t iface;
char domains[512];
struct in_addr primary_ipv4;
struct in_addr secondary_ipv4;
struct in_addr6 primary_ipv6;
struct in_addr6 secondary_ipv6;
short family;
int addr_len;
inet_address_t addr[4]; /* support up to 4 dns addresses */
} dns_cfg_message_t;

typedef struct {
Expand Down
72 changes: 71 additions & 1 deletion src/openvpn/tun.c
Expand Up @@ -135,6 +135,74 @@ do_address_service (const bool add, const short family, const struct tuntap *tt)
return ret;
}

static bool
do_dns6_service (bool add, const struct tuntap *tt)
{
DWORD len;
bool ret = false;
ack_message_t ack;
struct gc_arena gc = gc_new ();
HANDLE pipe = tt->options.msg_channel;
int addr_len = add ? tt->options.dns6_len : 0;

if (addr_len == 0 && add) /* no addresses to add */
return true;

dns_cfg_message_t dns = {
.header = {
(add ? msg_add_dns_cfg : msg_del_dns_cfg),
sizeof (dns_cfg_message_t),
0 },
.iface = { .index = tt->adapter_index, .name = "" },
.domains = "",
.family = AF_INET6,
.addr_len = addr_len
};

/* interface name is required */
strncpy (dns.iface.name, tt->actual_name, sizeof (dns.iface.name));
dns.iface.name[sizeof (dns.iface.name) - 1] = '\0';

if (addr_len > _countof(dns.addr))
{
addr_len = _countof(dns.addr);
dns.addr_len = addr_len;
msg(M_WARN, "Number of IPv6 DNS addresses sent to service truncated to %d",
addr_len);
}

for (int i = 0; i < addr_len; ++i)
{
dns.addr[i].ipv6 = tt->options.dns6[i];
}

msg (D_LOW, "%s IPv6 dns servers on '%s' (if_index = %d) using service",
(add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index);

if (!WriteFile (pipe, &dns, sizeof (dns), &len, NULL) ||
!ReadFile (pipe, &ack, sizeof (ack), &len, NULL))
{
msg (M_WARN, "TUN: could not talk to service: %s [%lu]",
strerror_win32 (GetLastError (), &gc), GetLastError ());
goto out;
}

if (ack.error_number != NO_ERROR)
{
msg (M_WARN, "TUN: %s IPv6 dns failed using service: %s [status=%u if_name=%s]",
(add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc),
ack.error_number, dns.iface.name);
goto out;
}

msg (M_INFO, "IPv6 dns servers %s using service", (add ? "set" : "deleted"));
ret = true;

out:
gc_free (&gc);
return ret;
}

#endif

#ifdef TARGET_SOLARIS
Expand Down Expand Up @@ -1384,7 +1452,7 @@ do_ifconfig (struct tuntap *tt,
else if (tt->options.msg_channel)
{
do_address_service (true, AF_INET6, tt);
/* TODO: do_dns6_service() */
do_dns6_service (true, tt);
}
else
{
Expand Down Expand Up @@ -5596,6 +5664,8 @@ close_tun (struct tuntap *tt)
if (tt->options.msg_channel)
{
do_address_service (false, AF_INET6, tt);
if (tt->options.dns6_len > 0)
do_dns6_service (false, tt);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/openvpnserv/Makefile.am
Expand Up @@ -26,7 +26,7 @@ openvpnserv_CFLAGS = \
-municode -D_UNICODE \
-UNTDDI_VERSION -U_WIN32_WINNT \
-D_WIN32_WINNT=_WIN32_WINNT_VISTA
openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32
openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 -lntdll
endif

openvpnserv_SOURCES = \
Expand Down
12 changes: 12 additions & 0 deletions src/openvpnserv/common.c
Expand Up @@ -216,3 +216,15 @@ MsgToEventLog (DWORD flags, LPCTSTR format, ...)

return error;
}

/* Convert a utf8 string to utf16. Caller should free the result */
wchar_t *
utf8to16 (const char *utf8)
{
int n = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0);
wchar_t *utf16 = malloc (n * sizeof (wchar_t));
if (!utf16)
return NULL;
MultiByteToWideChar (CP_UTF8, 0, utf8, -1, utf16, n);
return utf16;
}
172 changes: 172 additions & 0 deletions src/openvpnserv/interactive.c
Expand Up @@ -35,6 +35,12 @@
#include <sddl.h>
#include <shellapi.h>

#ifdef HAVE_VERSIONHELPERS_H
#include <versionhelpers.h>
#else
#include "compat-versionhelpers.h"
#endif

#include "openvpn-msg.h"
#include "validate.h"
#include "block_dns.h"
Expand Down Expand Up @@ -82,6 +88,8 @@ typedef enum {
address,
route,
block_dns,
undo_dns4,
undo_dns6,
_undo_type_max
} undo_type_t;
typedef list_item_t* undo_lists_t[_undo_type_max];
Expand Down Expand Up @@ -962,6 +970,156 @@ HandleRegisterDNSMessage (void)
return err;
}

/**
* Run the command: netsh interface $proto $action dns $if_name $addr [validate=no]
* @param action "delete" or "add"
* @param proto "ipv6" or "ip"
* @param if_name "name_of_interface"
* @param addr IPv4 (for proto = ip) or IPv6 address as a string
*
* If addr is null and action = "delete" all addresses are deleted.
*/
static DWORD
netsh_dns_cmd (const wchar_t *action, const wchar_t *proto, const wchar_t *if_name, const wchar_t *addr)
{
DWORD err = 0;
int timeout = 30000; /* in msec */
wchar_t argv0[MAX_PATH];

if (!addr)
{
if (wcscmp(action, L"delete") == 0)
addr = L"all";
else /* nothing to do -- return success*/
goto out;
}

/* Path of netsh */
int n = GetSystemDirectory (argv0, MAX_PATH);
if (n > 0 && n < MAX_PATH) /* got system directory */
{
wcsncat(argv0, L"\\netsh.exe", MAX_PATH - n - 1);
}
else
{
wcsncpy(argv0, L"C:\\Windows\\system32\\netsh.exe", MAX_PATH);
}

/* cmd template:
* netsh interface $proto $action dns $if_name $addr [validate=no]
*/
const wchar_t *fmt = L"netsh interface %s %s dns \"%s\" %s";

/* max cmdline length in wchars -- include room for worst case and some */
int ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1;
wchar_t *cmdline = malloc(ncmdline*sizeof(wchar_t));
if (!cmdline)
{
err = ERROR_OUTOFMEMORY;
goto out;
}

openvpn_sntprintf (cmdline, ncmdline, fmt, proto, action, if_name, addr);

if (IsWindows7OrGreater())
{
wcsncat(cmdline, L" validate=no", ncmdline - wcslen(cmdline) - 1);
}
err = ExecCommand (argv0, cmdline, timeout);

out:
free (cmdline);
return err;
}

/* Delete all IPv4 or IPv6 dns servers for an interface */
static DWORD
DeleteDNS(short family, wchar_t *if_name)
{
wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
return netsh_dns_cmd (L"delete", proto, if_name, NULL);
}

/* Add an IPv4 or IPv6 dns server to an interface */
static DWORD
AddDNS(short family, wchar_t *if_name, wchar_t *addr)
{
wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip";
return netsh_dns_cmd (L"add", proto, if_name, addr);
}

static BOOL
CmpWString (LPVOID item, LPVOID str)
{
return (wcscmp (item, str) == 0) ? TRUE : FALSE;
}

static DWORD
HandleDNSConfigMessage (const dns_cfg_message_t *msg, undo_lists_t *lists)
{
DWORD err = 0;
wchar_t addr[46]; /* large enough to hold string representation of an ipv4 / ipv6 address */
undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns4 : undo_dns6;
int addr_len = msg->addr_len;

/* sanity check */
if (addr_len > _countof(msg->addr))
addr_len = _countof(msg->addr);

if (!msg->iface.name[0]) /* interface name is required */
return ERROR_MESSAGE_DATA;

wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
if (!wide_name)
return ERROR_OUTOFMEMORY;

/* We delete all current addresses before adding any
* OR if the message type is del_dns_cfg
*/
if (addr_len > 0 || msg->header.type == msg_del_dns_cfg)
{
err = DeleteDNS(msg->family, wide_name);
if (err)
goto out;
free (RemoveListItem (&(*lists)[undo_type], CmpWString, wide_name));
}

if (msg->header.type == msg_del_dns_cfg) /* job done */
goto out;

for (int i = 0; i < addr_len; ++i)
{
if (msg->family == AF_INET6)
RtlIpv6AddressToStringW (&msg->addr[i].ipv6, addr);
else
RtlIpv4AddressToStringW (&msg->addr[i].ipv4, addr);
err = AddDNS(msg->family, wide_name, addr);
if (i == 0 && err)
goto out;
/* We do not check for duplicate addresses, so any error in adding
* additional addresses is ignored.
*/
}

if (msg->addr_len > 0)
{
wchar_t *tmp_name = wcsdup(wide_name);
if (!tmp_name || AddListItem(&(*lists)[undo_type], tmp_name))
{
free(tmp_name);
DeleteDNS(msg->family, wide_name);
err = ERROR_OUTOFMEMORY;
goto out;
}
}

err = 0;

out:
free(wide_name);
return err;
}

static VOID
HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
{
Expand All @@ -972,6 +1130,7 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list
route_message_t route;
flush_neighbors_message_t flush_neighbors;
block_dns_message_t block_dns;
dns_cfg_message_t dns;
} msg;
ack_message_t ack = {
.header = {
Expand Down Expand Up @@ -1017,6 +1176,11 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list
ack.error_number = HandleRegisterDNSMessage ();
break;

case msg_add_dns_cfg:
case msg_del_dns_cfg:
ack.error_number = HandleDNSConfigMessage (&msg.dns, lists);
break;

default:
ack.error_number = ERROR_MESSAGE_TYPE;
MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
Expand Down Expand Up @@ -1048,6 +1212,14 @@ Undo (undo_lists_t *lists)
DeleteRoute (item->data);
break;

case undo_dns4:
DeleteDNS(AF_INET, item->data);
break;

case undo_dns6:
DeleteDNS(AF_INET6, item->data);
break;

case block_dns:
delete_block_dns_filters (item->data);
item->data = NULL;
Expand Down
3 changes: 3 additions & 0 deletions src/openvpnserv/service.h
Expand Up @@ -89,4 +89,7 @@ BOOL ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
LPCTSTR GetLastErrorText ();
DWORD MsgToEventLog (DWORD flags, LPCTSTR lpszMsg, ...);

/* Convert a utf8 string to utf16. Caller should free the result */
wchar_t *utf8to16 (const char *utf8);

#endif

0 comments on commit c098016

Please sign in to comment.