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