Browse code

Set IPv6 DNS servers using interactive service

- 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>

Selva Nair authored on 2016/11/24 12:35:27
Showing 7 changed files
... ...
@@ -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