- 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>
| ... | ... |
@@ -5696,7 +5696,7 @@ this option to set secondary DNS server addresses. |
| 5696 | 5696 |
Set primary domain name server IPv6 address. Repeat |
| 5697 | 5697 |
this option to set secondary DNS server IPv6 addresses. |
| 5698 | 5698 |
|
| 5699 |
-Note: currently this is handled using netsh and requires admin rights (the |
|
| 5699 |
+Note: currently this is handled using netsh (the |
|
| 5700 | 5700 |
existing DHCP code can only do IPv4 DHCP, and that protocol only |
| 5701 | 5701 |
permits IPv4 addresses anywhere). The option will be put into the |
| 5702 | 5702 |
environment, so an |
| ... | ... |
@@ -79,10 +79,9 @@ typedef struct {
|
| 79 | 79 |
message_header_t header; |
| 80 | 80 |
interface_t iface; |
| 81 | 81 |
char domains[512]; |
| 82 |
- struct in_addr primary_ipv4; |
|
| 83 |
- struct in_addr secondary_ipv4; |
|
| 84 |
- struct in_addr6 primary_ipv6; |
|
| 85 |
- struct in_addr6 secondary_ipv6; |
|
| 82 |
+ short family; |
|
| 83 |
+ int addr_len; |
|
| 84 |
+ inet_address_t addr[4]; /* support up to 4 dns addresses */ |
|
| 86 | 85 |
} dns_cfg_message_t; |
| 87 | 86 |
|
| 88 | 87 |
typedef struct {
|
| ... | ... |
@@ -135,6 +135,74 @@ out: |
| 135 | 135 |
return ret; |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
+static bool |
|
| 139 |
+do_dns6_service (bool add, const struct tuntap *tt) |
|
| 140 |
+{
|
|
| 141 |
+ DWORD len; |
|
| 142 |
+ bool ret = false; |
|
| 143 |
+ ack_message_t ack; |
|
| 144 |
+ struct gc_arena gc = gc_new (); |
|
| 145 |
+ HANDLE pipe = tt->options.msg_channel; |
|
| 146 |
+ int addr_len = add ? tt->options.dns6_len : 0; |
|
| 147 |
+ |
|
| 148 |
+ if (addr_len == 0 && add) /* no addresses to add */ |
|
| 149 |
+ return true; |
|
| 150 |
+ |
|
| 151 |
+ dns_cfg_message_t dns = {
|
|
| 152 |
+ .header = {
|
|
| 153 |
+ (add ? msg_add_dns_cfg : msg_del_dns_cfg), |
|
| 154 |
+ sizeof (dns_cfg_message_t), |
|
| 155 |
+ 0 }, |
|
| 156 |
+ .iface = { .index = tt->adapter_index, .name = "" },
|
|
| 157 |
+ .domains = "", |
|
| 158 |
+ .family = AF_INET6, |
|
| 159 |
+ .addr_len = addr_len |
|
| 160 |
+ }; |
|
| 161 |
+ |
|
| 162 |
+ /* interface name is required */ |
|
| 163 |
+ strncpy (dns.iface.name, tt->actual_name, sizeof (dns.iface.name)); |
|
| 164 |
+ dns.iface.name[sizeof (dns.iface.name) - 1] = '\0'; |
|
| 165 |
+ |
|
| 166 |
+ if (addr_len > _countof(dns.addr)) |
|
| 167 |
+ {
|
|
| 168 |
+ addr_len = _countof(dns.addr); |
|
| 169 |
+ dns.addr_len = addr_len; |
|
| 170 |
+ msg(M_WARN, "Number of IPv6 DNS addresses sent to service truncated to %d", |
|
| 171 |
+ addr_len); |
|
| 172 |
+ } |
|
| 173 |
+ |
|
| 174 |
+ for (int i = 0; i < addr_len; ++i) |
|
| 175 |
+ {
|
|
| 176 |
+ dns.addr[i].ipv6 = tt->options.dns6[i]; |
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ msg (D_LOW, "%s IPv6 dns servers on '%s' (if_index = %d) using service", |
|
| 180 |
+ (add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index); |
|
| 181 |
+ |
|
| 182 |
+ if (!WriteFile (pipe, &dns, sizeof (dns), &len, NULL) || |
|
| 183 |
+ !ReadFile (pipe, &ack, sizeof (ack), &len, NULL)) |
|
| 184 |
+ {
|
|
| 185 |
+ msg (M_WARN, "TUN: could not talk to service: %s [%lu]", |
|
| 186 |
+ strerror_win32 (GetLastError (), &gc), GetLastError ()); |
|
| 187 |
+ goto out; |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ if (ack.error_number != NO_ERROR) |
|
| 191 |
+ {
|
|
| 192 |
+ msg (M_WARN, "TUN: %s IPv6 dns failed using service: %s [status=%u if_name=%s]", |
|
| 193 |
+ (add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc), |
|
| 194 |
+ ack.error_number, dns.iface.name); |
|
| 195 |
+ goto out; |
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ msg (M_INFO, "IPv6 dns servers %s using service", (add ? "set" : "deleted")); |
|
| 199 |
+ ret = true; |
|
| 200 |
+ |
|
| 201 |
+out: |
|
| 202 |
+ gc_free (&gc); |
|
| 203 |
+ return ret; |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 138 | 206 |
#endif |
| 139 | 207 |
|
| 140 | 208 |
#ifdef TARGET_SOLARIS |
| ... | ... |
@@ -1384,7 +1452,7 @@ do_ifconfig (struct tuntap *tt, |
| 1384 | 1384 |
else if (tt->options.msg_channel) |
| 1385 | 1385 |
{
|
| 1386 | 1386 |
do_address_service (true, AF_INET6, tt); |
| 1387 |
- /* TODO: do_dns6_service() */ |
|
| 1387 |
+ do_dns6_service (true, tt); |
|
| 1388 | 1388 |
} |
| 1389 | 1389 |
else |
| 1390 | 1390 |
{
|
| ... | ... |
@@ -5596,6 +5664,8 @@ close_tun (struct tuntap *tt) |
| 5596 | 5596 |
if (tt->options.msg_channel) |
| 5597 | 5597 |
{
|
| 5598 | 5598 |
do_address_service (false, AF_INET6, tt); |
| 5599 |
+ if (tt->options.dns6_len > 0) |
|
| 5600 |
+ do_dns6_service (false, tt); |
|
| 5599 | 5601 |
} |
| 5600 | 5602 |
else |
| 5601 | 5603 |
{
|
| ... | ... |
@@ -26,7 +26,7 @@ openvpnserv_CFLAGS = \ |
| 26 | 26 |
-municode -D_UNICODE \ |
| 27 | 27 |
-UNTDDI_VERSION -U_WIN32_WINNT \ |
| 28 | 28 |
-D_WIN32_WINNT=_WIN32_WINNT_VISTA |
| 29 |
-openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 |
|
| 29 |
+openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 -lntdll |
|
| 30 | 30 |
endif |
| 31 | 31 |
|
| 32 | 32 |
openvpnserv_SOURCES = \ |
| ... | ... |
@@ -216,3 +216,15 @@ MsgToEventLog (DWORD flags, LPCTSTR format, ...) |
| 216 | 216 |
|
| 217 | 217 |
return error; |
| 218 | 218 |
} |
| 219 |
+ |
|
| 220 |
+/* Convert a utf8 string to utf16. Caller should free the result */ |
|
| 221 |
+wchar_t * |
|
| 222 |
+utf8to16 (const char *utf8) |
|
| 223 |
+{
|
|
| 224 |
+ int n = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0); |
|
| 225 |
+ wchar_t *utf16 = malloc (n * sizeof (wchar_t)); |
|
| 226 |
+ if (!utf16) |
|
| 227 |
+ return NULL; |
|
| 228 |
+ MultiByteToWideChar (CP_UTF8, 0, utf8, -1, utf16, n); |
|
| 229 |
+ return utf16; |
|
| 230 |
+} |
| ... | ... |
@@ -35,6 +35,12 @@ |
| 35 | 35 |
#include <sddl.h> |
| 36 | 36 |
#include <shellapi.h> |
| 37 | 37 |
|
| 38 |
+#ifdef HAVE_VERSIONHELPERS_H |
|
| 39 |
+#include <versionhelpers.h> |
|
| 40 |
+#else |
|
| 41 |
+#include "compat-versionhelpers.h" |
|
| 42 |
+#endif |
|
| 43 |
+ |
|
| 38 | 44 |
#include "openvpn-msg.h" |
| 39 | 45 |
#include "validate.h" |
| 40 | 46 |
#include "block_dns.h" |
| ... | ... |
@@ -82,6 +88,8 @@ typedef enum {
|
| 82 | 82 |
address, |
| 83 | 83 |
route, |
| 84 | 84 |
block_dns, |
| 85 |
+ undo_dns4, |
|
| 86 |
+ undo_dns6, |
|
| 85 | 87 |
_undo_type_max |
| 86 | 88 |
} undo_type_t; |
| 87 | 89 |
typedef list_item_t* undo_lists_t[_undo_type_max]; |
| ... | ... |
@@ -962,6 +970,156 @@ HandleRegisterDNSMessage (void) |
| 962 | 962 |
return err; |
| 963 | 963 |
} |
| 964 | 964 |
|
| 965 |
+/** |
|
| 966 |
+ * Run the command: netsh interface $proto $action dns $if_name $addr [validate=no] |
|
| 967 |
+ * @param action "delete" or "add" |
|
| 968 |
+ * @param proto "ipv6" or "ip" |
|
| 969 |
+ * @param if_name "name_of_interface" |
|
| 970 |
+ * @param addr IPv4 (for proto = ip) or IPv6 address as a string |
|
| 971 |
+ * |
|
| 972 |
+ * If addr is null and action = "delete" all addresses are deleted. |
|
| 973 |
+ */ |
|
| 974 |
+static DWORD |
|
| 975 |
+netsh_dns_cmd (const wchar_t *action, const wchar_t *proto, const wchar_t *if_name, const wchar_t *addr) |
|
| 976 |
+{
|
|
| 977 |
+ DWORD err = 0; |
|
| 978 |
+ int timeout = 30000; /* in msec */ |
|
| 979 |
+ wchar_t argv0[MAX_PATH]; |
|
| 980 |
+ |
|
| 981 |
+ if (!addr) |
|
| 982 |
+ {
|
|
| 983 |
+ if (wcscmp(action, L"delete") == 0) |
|
| 984 |
+ addr = L"all"; |
|
| 985 |
+ else /* nothing to do -- return success*/ |
|
| 986 |
+ goto out; |
|
| 987 |
+ } |
|
| 988 |
+ |
|
| 989 |
+ /* Path of netsh */ |
|
| 990 |
+ int n = GetSystemDirectory (argv0, MAX_PATH); |
|
| 991 |
+ if (n > 0 && n < MAX_PATH) /* got system directory */ |
|
| 992 |
+ {
|
|
| 993 |
+ wcsncat(argv0, L"\\netsh.exe", MAX_PATH - n - 1); |
|
| 994 |
+ } |
|
| 995 |
+ else |
|
| 996 |
+ {
|
|
| 997 |
+ wcsncpy(argv0, L"C:\\Windows\\system32\\netsh.exe", MAX_PATH); |
|
| 998 |
+ } |
|
| 999 |
+ |
|
| 1000 |
+ /* cmd template: |
|
| 1001 |
+ * netsh interface $proto $action dns $if_name $addr [validate=no] |
|
| 1002 |
+ */ |
|
| 1003 |
+ const wchar_t *fmt = L"netsh interface %s %s dns \"%s\" %s"; |
|
| 1004 |
+ |
|
| 1005 |
+ /* max cmdline length in wchars -- include room for worst case and some */ |
|
| 1006 |
+ int ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1; |
|
| 1007 |
+ wchar_t *cmdline = malloc(ncmdline*sizeof(wchar_t)); |
|
| 1008 |
+ if (!cmdline) |
|
| 1009 |
+ {
|
|
| 1010 |
+ err = ERROR_OUTOFMEMORY; |
|
| 1011 |
+ goto out; |
|
| 1012 |
+ } |
|
| 1013 |
+ |
|
| 1014 |
+ openvpn_sntprintf (cmdline, ncmdline, fmt, proto, action, if_name, addr); |
|
| 1015 |
+ |
|
| 1016 |
+ if (IsWindows7OrGreater()) |
|
| 1017 |
+ {
|
|
| 1018 |
+ wcsncat(cmdline, L" validate=no", ncmdline - wcslen(cmdline) - 1); |
|
| 1019 |
+ } |
|
| 1020 |
+ err = ExecCommand (argv0, cmdline, timeout); |
|
| 1021 |
+ |
|
| 1022 |
+out: |
|
| 1023 |
+ free (cmdline); |
|
| 1024 |
+ return err; |
|
| 1025 |
+} |
|
| 1026 |
+ |
|
| 1027 |
+/* Delete all IPv4 or IPv6 dns servers for an interface */ |
|
| 1028 |
+static DWORD |
|
| 1029 |
+DeleteDNS(short family, wchar_t *if_name) |
|
| 1030 |
+{
|
|
| 1031 |
+ wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip"; |
|
| 1032 |
+ return netsh_dns_cmd (L"delete", proto, if_name, NULL); |
|
| 1033 |
+} |
|
| 1034 |
+ |
|
| 1035 |
+/* Add an IPv4 or IPv6 dns server to an interface */ |
|
| 1036 |
+static DWORD |
|
| 1037 |
+AddDNS(short family, wchar_t *if_name, wchar_t *addr) |
|
| 1038 |
+{
|
|
| 1039 |
+ wchar_t *proto = (family == AF_INET6) ? L"ipv6" : L"ip"; |
|
| 1040 |
+ return netsh_dns_cmd (L"add", proto, if_name, addr); |
|
| 1041 |
+} |
|
| 1042 |
+ |
|
| 1043 |
+static BOOL |
|
| 1044 |
+CmpWString (LPVOID item, LPVOID str) |
|
| 1045 |
+{
|
|
| 1046 |
+ return (wcscmp (item, str) == 0) ? TRUE : FALSE; |
|
| 1047 |
+} |
|
| 1048 |
+ |
|
| 1049 |
+static DWORD |
|
| 1050 |
+HandleDNSConfigMessage (const dns_cfg_message_t *msg, undo_lists_t *lists) |
|
| 1051 |
+{
|
|
| 1052 |
+ DWORD err = 0; |
|
| 1053 |
+ wchar_t addr[46]; /* large enough to hold string representation of an ipv4 / ipv6 address */ |
|
| 1054 |
+ undo_type_t undo_type = (msg->family == AF_INET6) ? undo_dns4 : undo_dns6; |
|
| 1055 |
+ int addr_len = msg->addr_len; |
|
| 1056 |
+ |
|
| 1057 |
+ /* sanity check */ |
|
| 1058 |
+ if (addr_len > _countof(msg->addr)) |
|
| 1059 |
+ addr_len = _countof(msg->addr); |
|
| 1060 |
+ |
|
| 1061 |
+ if (!msg->iface.name[0]) /* interface name is required */ |
|
| 1062 |
+ return ERROR_MESSAGE_DATA; |
|
| 1063 |
+ |
|
| 1064 |
+ wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */ |
|
| 1065 |
+ if (!wide_name) |
|
| 1066 |
+ return ERROR_OUTOFMEMORY; |
|
| 1067 |
+ |
|
| 1068 |
+ /* We delete all current addresses before adding any |
|
| 1069 |
+ * OR if the message type is del_dns_cfg |
|
| 1070 |
+ */ |
|
| 1071 |
+ if (addr_len > 0 || msg->header.type == msg_del_dns_cfg) |
|
| 1072 |
+ {
|
|
| 1073 |
+ err = DeleteDNS(msg->family, wide_name); |
|
| 1074 |
+ if (err) |
|
| 1075 |
+ goto out; |
|
| 1076 |
+ free (RemoveListItem (&(*lists)[undo_type], CmpWString, wide_name)); |
|
| 1077 |
+ } |
|
| 1078 |
+ |
|
| 1079 |
+ if (msg->header.type == msg_del_dns_cfg) /* job done */ |
|
| 1080 |
+ goto out; |
|
| 1081 |
+ |
|
| 1082 |
+ for (int i = 0; i < addr_len; ++i) |
|
| 1083 |
+ {
|
|
| 1084 |
+ if (msg->family == AF_INET6) |
|
| 1085 |
+ RtlIpv6AddressToStringW (&msg->addr[i].ipv6, addr); |
|
| 1086 |
+ else |
|
| 1087 |
+ RtlIpv4AddressToStringW (&msg->addr[i].ipv4, addr); |
|
| 1088 |
+ err = AddDNS(msg->family, wide_name, addr); |
|
| 1089 |
+ if (i == 0 && err) |
|
| 1090 |
+ goto out; |
|
| 1091 |
+ /* We do not check for duplicate addresses, so any error in adding |
|
| 1092 |
+ * additional addresses is ignored. |
|
| 1093 |
+ */ |
|
| 1094 |
+ } |
|
| 1095 |
+ |
|
| 1096 |
+ if (msg->addr_len > 0) |
|
| 1097 |
+ {
|
|
| 1098 |
+ wchar_t *tmp_name = wcsdup(wide_name); |
|
| 1099 |
+ if (!tmp_name || AddListItem(&(*lists)[undo_type], tmp_name)) |
|
| 1100 |
+ {
|
|
| 1101 |
+ free(tmp_name); |
|
| 1102 |
+ DeleteDNS(msg->family, wide_name); |
|
| 1103 |
+ err = ERROR_OUTOFMEMORY; |
|
| 1104 |
+ goto out; |
|
| 1105 |
+ } |
|
| 1106 |
+ } |
|
| 1107 |
+ |
|
| 1108 |
+ err = 0; |
|
| 1109 |
+ |
|
| 1110 |
+out: |
|
| 1111 |
+ free(wide_name); |
|
| 1112 |
+ return err; |
|
| 1113 |
+} |
|
| 1114 |
+ |
|
| 965 | 1115 |
static VOID |
| 966 | 1116 |
HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists) |
| 967 | 1117 |
{
|
| ... | ... |
@@ -972,6 +1130,7 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list |
| 972 | 972 |
route_message_t route; |
| 973 | 973 |
flush_neighbors_message_t flush_neighbors; |
| 974 | 974 |
block_dns_message_t block_dns; |
| 975 |
+ dns_cfg_message_t dns; |
|
| 975 | 976 |
} msg; |
| 976 | 977 |
ack_message_t ack = {
|
| 977 | 978 |
.header = {
|
| ... | ... |
@@ -1017,6 +1176,11 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list |
| 1017 | 1017 |
ack.error_number = HandleRegisterDNSMessage (); |
| 1018 | 1018 |
break; |
| 1019 | 1019 |
|
| 1020 |
+ case msg_add_dns_cfg: |
|
| 1021 |
+ case msg_del_dns_cfg: |
|
| 1022 |
+ ack.error_number = HandleDNSConfigMessage (&msg.dns, lists); |
|
| 1023 |
+ break; |
|
| 1024 |
+ |
|
| 1020 | 1025 |
default: |
| 1021 | 1026 |
ack.error_number = ERROR_MESSAGE_TYPE; |
| 1022 | 1027 |
MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
|
| ... | ... |
@@ -1048,6 +1212,14 @@ Undo (undo_lists_t *lists) |
| 1048 | 1048 |
DeleteRoute (item->data); |
| 1049 | 1049 |
break; |
| 1050 | 1050 |
|
| 1051 |
+ case undo_dns4: |
|
| 1052 |
+ DeleteDNS(AF_INET, item->data); |
|
| 1053 |
+ break; |
|
| 1054 |
+ |
|
| 1055 |
+ case undo_dns6: |
|
| 1056 |
+ DeleteDNS(AF_INET6, item->data); |
|
| 1057 |
+ break; |
|
| 1058 |
+ |
|
| 1051 | 1059 |
case block_dns: |
| 1052 | 1060 |
delete_block_dns_filters (item->data); |
| 1053 | 1061 |
item->data = NULL; |
| ... | ... |
@@ -89,4 +89,7 @@ BOOL ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status) |
| 89 | 89 |
LPCTSTR GetLastErrorText (); |
| 90 | 90 |
DWORD MsgToEventLog (DWORD flags, LPCTSTR lpszMsg, ...); |
| 91 | 91 |
|
| 92 |
+/* Convert a utf8 string to utf16. Caller should free the result */ |
|
| 93 |
+wchar_t *utf8to16 (const char *utf8); |
|
| 94 |
+ |
|
| 92 | 95 |
#endif |