38#include <versionhelpers.h>
44#define IO_TIMEOUT 2000
46#define ERROR_OPENVPN_STARTUP 0x20000000
47#define ERROR_STARTUP_DATA 0x20000001
48#define ERROR_MESSAGE_DATA 0x20000002
49#define ERROR_MESSAGE_TYPE 0x20000003
52static SERVICE_STATUS
status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
56#define RDNS_TIMEOUT 600
58#define TUN_IOCTL_REGISTER_RINGS \
59 CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
138 if (new_item == NULL)
140 return ERROR_OUTOFMEMORY;
143 new_item->
next = *pfirst;
158 for (pnext = pfirst; *pnext; pnext = &(*pnext)->
next)
161 if (!match(item->
data, ctx))
179 if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
181 CloseHandle(*handle);
182 *handle = INVALID_HANDLE_VALUE;
184 return INVALID_HANDLE_VALUE;
190 ZeroMemory(overlapped,
sizeof(OVERLAPPED));
191 overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
192 return overlapped->hEvent;
198 HANDLE io_event = overlapped->hEvent;
199 if (!ResetEvent(io_event))
203 ZeroMemory(overlapped,
sizeof(OVERLAPPED));
204 overlapped->hEvent = io_event;
223 DWORD res, bytes = 0;
224 OVERLAPPED overlapped;
225 LPHANDLE handles = NULL;
233 handles = malloc((count + 1) *
sizeof(HANDLE));
241 success = WriteFile(pipe,
buffer, size, NULL, &overlapped);
245 success = ReadFile(pipe,
buffer, size, NULL, &overlapped);
247 if (!success && GetLastError() != ERROR_IO_PENDING && GetLastError() != ERROR_MORE_DATA)
252 handles[0] = io_event;
253 for (i = 0; i < count; i++)
255 handles[i + 1] = events[i];
258 res = WaitForMultipleObjects(count + 1, handles, FALSE, op ==
peek ? INFINITE :
IO_TIMEOUT);
259 if (res != WAIT_OBJECT_0)
267 PeekNamedPipe(pipe, NULL, 0, NULL, &bytes, NULL);
271 GetOverlappedResult(pipe, &overlapped, &bytes, TRUE);
307 const WCHAR
msg[] = L
"Process ID";
308 WCHAR buf[22 + _countof(
msg)];
314 swprintf(buf, _countof(buf), L
"0x%08x\n0x%08lx\n%ls", 0, pid,
msg);
316 WritePipeAsync(pipe, buf, (DWORD)(wcslen(buf) * 2), count, events);
320ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
323 LPWSTR result = L
"0xffffffff\nFormatMessage failed\nCould not return result";
324 DWORD_PTR args[] = { (DWORD_PTR)error, (DWORD_PTR)func, (DWORD_PTR)
"" };
328 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
329 | FORMAT_MESSAGE_IGNORE_INSERTS,
330 0, error, 0, (LPWSTR)&args[2], 0, NULL);
333 result_len = FormatMessageW(
334 FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
335 L
"0x%1!08x!\n%2!s!\n%3!s!", 0, 0, (LPWSTR)&result, 0, (va_list *)args);
337 WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
342 LocalFree((LPVOID)args[2]);
365ValidateOptions(HANDLE pipe,
const WCHAR *workdir,
const WCHAR *cmdline, WCHAR *errmsg,
372 const WCHAR *
msg1 = L
"You have specified a config file location (%ls relative to %ls)"
373 L
" that requires admin approval. This error may be avoided"
374 L
" by adding your account to the \"%ls\" group";
376 const WCHAR *
msg2 = L
"You have specified an option (%ls) that may be used"
377 L
" only with admin approval. This error may be avoided"
378 L
" by adding your account to the \"%ls\" group";
383 argv = CommandLineToArgvW(cmdline, &argc);
387 swprintf(errmsg, capacity,
388 L
"Cannot validate options: CommandLineToArgvW failed with error = 0x%08lx",
402 if (argc == first + 1)
404 WCHAR *argv_tmp[2] = { L
"--config",
argv[first] };
413 for (i = first; i < argc; ++i)
422 if (wcscmp(L
"--config",
argv[i]) == 0 && argc - i > 1)
460 size = bytes /
sizeof(*data);
461 if ((size == 0) || (size > 4096))
468 data = malloc(bytes);
484 if (
data[size - 1] != 0)
502 len = wcslen(sud->
options) + 1;
531 SOCKADDR_INET sa_inet;
532 ZeroMemory(&sa_inet,
sizeof(sa_inet));
533 sa_inet.si_family = family;
534 if (family == AF_INET)
536 sa_inet.Ipv4.sin_addr = addr->
ipv4;
538 else if (family == AF_INET6)
540 sa_inet.Ipv6.sin6_addr = addr->
ipv6;
548 NETIO_STATUS convert_status;
549 LPWSTR wide_name =
utf8to16(iface_name);
553 convert_status = ConvertInterfaceAliasToLuid(wide_name, luid);
558 convert_status = ERROR_OUTOFMEMORY;
560 return convert_status;
566 return memcmp(item,
address,
sizeof(MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE;
572 return DeleteUnicastIpAddressEntry(addr_row);
579 PMIB_UNICASTIPADDRESS_ROW addr_row;
583 msg->iface.name[_countof(
msg->iface.name) - 1] =
'\0';
585 addr_row = malloc(
sizeof(*addr_row));
586 if (addr_row == NULL)
588 return ERROR_OUTOFMEMORY;
591 InitializeUnicastIpAddressEntry(addr_row);
593 addr_row->OnLinkPrefixLength = (UINT8)
msg->prefix_len;
597 addr_row->InterfaceIndex =
msg->iface.index;
607 addr_row->InterfaceLuid = luid;
612 err = CreateUnicastIpAddressEntry(addr_row);
647 return memcmp(item,
route,
sizeof(MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE;
653 return DeleteIpForwardEntry2(fwd_row);
660 PMIB_IPFORWARD_ROW2 fwd_row;
664 msg->iface.name[_countof(
msg->iface.name) - 1] =
'\0';
666 fwd_row = malloc(
sizeof(*fwd_row));
669 return ERROR_OUTOFMEMORY;
672 ZeroMemory(fwd_row,
sizeof(*fwd_row));
673 fwd_row->ValidLifetime = 0xffffffff;
674 fwd_row->PreferredLifetime = 0xffffffff;
675 fwd_row->Protocol = MIB_IPPROTO_NETMGMT;
676 fwd_row->Metric =
msg->metric;
678 fwd_row->DestinationPrefix.PrefixLength = (UINT8)
msg->prefix_len;
683 fwd_row->InterfaceIndex =
msg->iface.index;
685 else if (strlen(
msg->iface.name))
693 fwd_row->InterfaceLuid = luid;
698 err = CreateIpForwardEntry2(fwd_row);
734 if (
msg->family == AF_INET)
736 return FlushIpNetTable(
msg->iface.index);
739 return FlushIpNetTable2(
msg->family,
msg->iface.index);
753 err_str = L
"Unknown Win32 Error";
755 if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
756 NULL, err, 0, buf, _countof(buf), NULL))
803 HANDLE engine = NULL;
816 err = ERROR_OUTOFMEMORY;
819 block_data->
engine = engine;
820 block_data->
index =
msg->iface.index;
884ExecCommand(
const WCHAR *argv0,
const WCHAR *cmdline, DWORD timeout)
888 PROCESS_INFORMATION pi;
889 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
890 WCHAR *cmdline_dup = NULL;
892 ZeroMemory(&si,
sizeof(si));
893 ZeroMemory(&pi,
sizeof(pi));
898 cmdline_dup = _wcsdup(cmdline);
900 && CreateProcessW(argv0, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
902 WaitForSingleObject(pi.hProcess, timeout ? timeout : INFINITE);
903 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
906 exit_code = GetLastError();
908 else if (exit_code == STILL_ACTIVE)
910 exit_code = WAIT_TIMEOUT;
913 TerminateProcess(pi.hProcess, exit_code);
914 MsgToEventLog(
M_ERR, L
"ExecCommand: \"%ls %ls\" killed after timeout", argv0, cmdline);
926 CloseHandle(pi.hProcess);
927 CloseHandle(pi.hThread);
931 exit_code = GetLastError();
949 WCHAR ipcfg[MAX_PATH];
957 { ipcfg, L
"ipconfig /flushdns", timeout },
958 { ipcfg, L
"ipconfig /registerdns", timeout },
963 swprintf(ipcfg, MAX_PATH, L
"%ls\\%ls",
get_win_sys_path(), L
"ipconfig.exe");
965 if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
968 for (
size_t i = 0; i < _countof(cmds); ++i)
970 ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
982 err = ERROR_SEM_TIMEOUT;
991 HANDLE thread = NULL;
994 thread = CreateThread(NULL, 0,
RegisterDNS, NULL, 0, NULL);
1003 CloseHandle(thread);
1007 err = GetLastError();
1026 int timeout = 30000;
1027 wchar_t argv0[MAX_PATH];
1028 wchar_t *cmdline = NULL;
1029 const wchar_t *addr_static = (wcscmp(action, L
"set") == 0) ? L
"static" : L
"";
1033 if (wcscmp(action, L
"delete") == 0)
1044 swprintf(argv0, _countof(argv0), L
"%ls\\%ls",
get_win_sys_path(), L
"netsh.exe");
1049 const wchar_t *fmt = L
"netsh interface ip %ls wins %lu %ls %ls";
1052 size_t ncmdline = wcslen(fmt) + 11 + wcslen(action) + wcslen(addr)
1053 + wcslen(addr_static) + 32 + 1;
1054 cmdline = malloc(ncmdline *
sizeof(
wchar_t));
1057 err = ERROR_OUTOFMEMORY;
1061 swprintf(cmdline, ncmdline, fmt, action, if_index, addr_static, addr);
1079 typedef NTSTATUS(__stdcall * publish_fn_t)(DWORD StateNameLo, DWORD StateNameHi, DWORD TypeId,
1080 DWORD Buffer, DWORD Length, DWORD ExplicitScope);
1081 publish_fn_t RtlPublishWnfStateData;
1082 const DWORD WNF_GPOL_SYSTEM_CHANGES_HI = 0x0D891E2A;
1083 const DWORD WNF_GPOL_SYSTEM_CHANGES_LO = 0xA3BC0875;
1086 HMODULE ntdll = LoadLibraryA(
"ntdll.dll");
1092 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll,
"RtlPublishWnfStateData");
1093 if (RtlPublishWnfStateData == NULL)
1098 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES_LO, WNF_GPOL_SYSTEM_CHANGES_HI, 0, 0, 0, 0)
1119 typedef NTSTATUS (*publish_fn_t)(INT64 StateName, INT64 TypeId, INT64 Buffer,
1120 unsigned int Length, INT64 ExplicitScope);
1121 publish_fn_t RtlPublishWnfStateData;
1122 const INT64 WNF_GPOL_SYSTEM_CHANGES = 0x0D891E2AA3BC0875;
1125 HMODULE ntdll = LoadLibraryA(
"ntdll.dll");
1131 RtlPublishWnfStateData = (publish_fn_t)GetProcAddress(ntdll,
"RtlPublishWnfStateData");
1132 if (RtlPublishWnfStateData == NULL)
1137 if (RtlPublishWnfStateData(WNF_GPOL_SYSTEM_CHANGES, 0, 0, 0, 0) != ERROR_SUCCESS)
1158 const BOOL win_32bit = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL;
1173 SC_HANDLE scm = NULL;
1174 SC_HANDLE dnssvc = NULL;
1181 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1184 MsgToEventLog(
M_ERR, L
"%S: OpenSCManager call failed (%lu)", __func__, GetLastError());
1188 dnssvc = OpenServiceA(scm,
"Dnscache", SERVICE_PAUSE_CONTINUE);
1191 MsgToEventLog(
M_ERR, L
"%S: OpenService call failed (%lu)", __func__, GetLastError());
1195 SERVICE_STATUS control_status;
1196 if (ControlService(dnssvc, SERVICE_CONTROL_PARAMCHANGE, &control_status) == 0)
1198 MsgToEventLog(
M_ERR, L
"%S: ControlService call failed (%lu)", __func__, GetLastError());
1207 CloseServiceHandle(dnssvc);
1211 CloseServiceHandle(scm);
1231 PWSTR iid_str = NULL;
1236 PWSTR wide_name =
utf8to16(itf_name);
1241 err = ConvertInterfaceLuidToGuid(&luid, &guid);
1244 PWSTR wide_name =
utf8to16(itf_name);
1250 if (StringFromIID(&guid, &iid_str) != S_OK)
1252 PWSTR wide_name =
utf8to16(itf_name);
1255 err = ERROR_OUTOFMEMORY;
1258 if (wcslen(iid_str) + 1 > len)
1260 err = ERROR_INVALID_PARAMETER;
1264 wcsncpy(str, iid_str, len);
1269 CoTaskMemFree(iid_str);
1291 DWORD size =
sizeof(
data);
1292 LSTATUS err = RegGetValueA(
key, NULL,
"SearchList", RRF_RT_REG_SZ, NULL, (PBYTE)
data, &size);
1293 if (!err || err == ERROR_MORE_DATA)
1296 for (
size_t i = 0; i < strlen(
data); ++i)
1298 if (isalnum(
data[i]) ||
data[i] ==
'-' ||
data[i] ==
'.')
1332 err = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient",
1333 0, KEY_ALL_ACCESS,
key);
1346 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\TCPIP\\Parameters",
1347 0, KEY_ALL_ACCESS,
key);
1366 RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1367 "System\\CurrentControlSet\\Services\\TCPIP\\Parameters\\Interfaces",
1368 0, KEY_ALL_ACCESS, &itfs);
1371 err = RegOpenKeyExW(itfs, iid, 0, KEY_ALL_ACCESS,
key);
1381 *
key = INVALID_HANDLE_VALUE;
1397 err = RegGetValueA(
key, NULL,
"InitialSearchList", RRF_RT_REG_SZ, NULL, NULL, NULL);
1400 if (err == ERROR_FILE_NOT_FOUND)
1417 size_t length = (wcslen(
string) + 1) *
sizeof(
wchar_t);
1418 if (length > UINT_MAX)
1422 return (DWORD)length;
1438 if (!list || wcslen(list) == 0)
1451 LSTATUS err = RegSetValueExW(
key, L
"InitialSearchList", 0, REG_SZ, (PBYTE)list, size);
1454 MsgToEventLog(
M_ERR, L
"%S: failed to set InitialSearchList value (%lu)", __func__, err);
1478 size_t list_len = wcslen(list);
1479 size_t add_len = wcslen(add);
1485 size_t sep_len = (list_len > 0) ? 1 : 0;
1486 if (list_len + sep_len + add_len + 1 > list_cap)
1493 list[list_len++] = L
',';
1495 wmemcpy(list + list_len, add, add_len + 1);
1526 PCWSTR domain = remove;
1529 PCWSTR comma = wcschr(domain, L
',');
1530 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
1535 PWSTR match_end = NULL;
1536 for (PWSTR p = list; *p;)
1538 PWSTR tok_end = wcschr(p, L
',');
1539 size_t tok_len = tok_end ? (size_t)(tok_end - p) : wcslen(p);
1540 if (tok_len == domain_len && wcsncmp(p, domain, domain_len) == 0)
1543 match_end = tok_end;
1555 PWSTR cut_start, cut_end;
1559 cut_end = match_end ? match_end + 1 : match + domain_len;
1563 cut_start = match - 1;
1564 cut_end = match + domain_len;
1566 wmemmove(cut_start, cut_end, wcslen(cut_end) + 1);
1598 WCHAR list[2048] = { 0 };
1602 DWORD size =
sizeof(list);
1604 RegGetValueW(
key, NULL, L
"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1625 LSTATUS err = RegSetValueExW(
key, L
"SearchList", 0, REG_SZ, (PBYTE)list, size);
1652 DWORD size =
sizeof(list);
1654 err = RegGetValueW(
key, NULL, L
"InitialSearchList", RRF_RT_REG_SZ, NULL, list, &size);
1657 if (err != ERROR_FILE_NOT_FOUND)
1666 err = RegSetValueExW(
key, L
"SearchList", 0, REG_SZ, (PBYTE)list, size);
1673 RegDeleteValueA(
key,
"InitialSearchList");
1698 DWORD size =
sizeof(list);
1699 LSTATUS err = RegGetValueW(
key, NULL, L
"SearchList", RRF_RT_REG_SZ, NULL, list, &size);
1712 if (list[0] != L
'\0')
1717 WCHAR initial[2048];
1718 size =
sizeof(initial);
1719 err = RegGetValueW(
key, NULL, L
"InitialSearchList", RRF_RT_REG_SZ, NULL, initial, &size);
1720 if (!err && wcscmp(list, initial) == 0)
1725 if (err && err != ERROR_FILE_NOT_FOUND)
1734 err = RegSetValueExW(
key, L
"SearchList", 0, REG_SZ, (PBYTE)list, size);
1750 HKEY dns_searchlist_key;
1752 if (dns_searchlist_key != INVALID_HANDLE_VALUE)
1755 RegCloseKey(dns_searchlist_key);
1787 DWORD err = ERROR_OUTOFMEMORY;
1791 if (list_key == INVALID_HANDLE_VALUE)
1794 return ERROR_FILE_NOT_FOUND;
1808 if (domains && *domains)
1810 wchar_t *wide_domains =
utf8to16(domains);
1816 undo_data = malloc(
sizeof(*undo_data));
1820 wide_domains = NULL;
1823 strncpy_s(undo_data->
itf_name,
sizeof(undo_data->
itf_name), itf_name, _TRUNCATE);
1824 undo_data->
domains = wide_domains;
1840 RegCloseKey(list_key);
1854 PCSTR itfs_key = family == AF_INET6
1855 ?
"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces"
1856 :
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
1858 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, itfs_key, 0, KEY_ALL_ACCESS,
key);
1861 *
key = INVALID_HANDLE_VALUE;
1863 __func__, family, err);
1866 return err ? FALSE : TRUE;
1886 return ERROR_FILE_NOT_FOUND;
1889 HKEY itf = INVALID_HANDLE_VALUE;
1890 err = RegOpenKeyExW(itfs, itf_id, 0, KEY_ALL_ACCESS, &itf);
1894 __func__, itf_id, family, err);
1898 err = RegSetValueExA(itf,
"NameServer", 0, REG_SZ, (PBYTE)value, (DWORD)strlen(value) + 1);
1902 __func__, value, itf_id, family, err);
1906 if (itf != INVALID_HANDLE_VALUE)
1910 if (itfs != INVALID_HANDLE_VALUE)
1951 unsigned int addr_len =
msg->addr_len;
1954 const unsigned int max_addrs = _countof(
msg->addr);
1955 if (addr_len > max_addrs)
1957 addr_len = max_addrs;
1960 if (!
msg->iface.name[0])
1969 msgptr->
iface.
name[_countof(
msg->iface.name) - 1] =
'\0';
1970 msgptr->
domains[_countof(
msg->domains) - 1] =
'\0';
1996 if (
msg->domains[0])
2010 CHAR addrs[_countof(
msg->addr) * 64];
2012 for (
unsigned int i = 0; i < addr_len; ++i)
2016 addrs[offset++] =
',';
2018 if (
msg->family == AF_INET6)
2020 RtlIpv6AddressToStringA(&
msg->addr[i].ipv6, addrs + offset);
2024 RtlIpv4AddressToStringA(&
msg->addr[i].ipv4, addrs + offset);
2026 offset = strlen(addrs);
2035 wchar_t *tmp_iid = _wcsdup(iid);
2036 if (!tmp_iid ||
AddListItem(&(*lists)[undo_type], tmp_iid))
2040 return ERROR_OUTOFMEMORY;
2045 if (
msg->domains[0])
2066 DWORD size =
sizeof(
dhcp);
2069 err = RegGetValueA(
key, NULL,
"EnableDHCP", RRF_RT_REG_DWORD, NULL, (PBYTE)&
dhcp, &size);
2070 if (err != NO_ERROR)
2076 return dhcp ? TRUE : FALSE;
2090 const short families[] = { AF_INET, AF_INET6 };
2091 for (
size_t i = 0; i < _countof(families); i++)
2093 short family = families[i];
2100 if ((family == AF_INET6 && strchr(addresses[j],
':') == NULL)
2101 || (family == AF_INET && strchr(addresses[j],
':') != NULL))
2108 addr_list[offset++] =
',';
2110 strcpy(addr_list + offset, addresses[j]);
2111 offset += strlen(addresses[j]);
2143 addrs[*size - 1] =
'\0';
2147 err = RegGetValueA(itf_key, NULL,
"NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2148 if (err && err != ERROR_FILE_NOT_FOUND)
2158 err = RegGetValueA(itf_key, NULL,
"DhcpNameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2166 if (strchr(addrs,
'.'))
2173 return ERROR_FILE_NOT_FOUND;
2188 addrs[*size - 1] =
'\0';
2192 err = RegGetValueA(itf_key, NULL,
"NameServer", RRF_RT_REG_SZ, NULL, (PBYTE)addrs, &s);
2193 if (err && err != ERROR_FILE_NOT_FOUND)
2202 IN6_ADDR in_addrs[8];
2203 DWORD in_addrs_size =
sizeof(in_addrs);
2204 err = RegGetValueA(itf_key, NULL,
"Dhcpv6DNSServers", RRF_RT_REG_BINARY, NULL,
2205 (PBYTE)in_addrs, &in_addrs_size);
2214 size_t in_addrs_read = in_addrs_size /
sizeof(IN6_ADDR);
2215 for (
size_t i = 0; i < in_addrs_read; ++i)
2224 if (inet_ntop(AF_INET6, &in_addrs[i],
pos, s) == NULL)
2227 return ERROR_MORE_DATA;
2230 size_t addr_len = strlen(
pos);
2232 s -= (DWORD)addr_len;
2234 s = (DWORD)strlen(addrs) + 1;
2237 if (strchr(addrs,
':'))
2244 return ERROR_FILE_NOT_FOUND;
2259 PCWSTR entry = list;
2260 while (entry && *entry)
2262 PCWSTR comma = wcschr(entry, L
',');
2263 size_t entry_len = comma ? (size_t)(comma - entry) : wcslen(entry);
2264 if (entry_len == len && wcsncmp(entry, domain, len) == 0)
2303 const size_t glyph_size =
sizeof(*domains);
2304 const size_t max_len = (size_t)capacity / glyph_size;
2307 const size_t dot_len = 1;
2308 const size_t term_len = 2;
2310 LSTATUS ret = NO_ERROR;
2312 WCHAR *tmp = malloc(capacity);
2315 ret = ERROR_OUTOFMEMORY;
2319 PWCHAR tmp_pos = tmp;
2320 PCWCHAR domain = domains;
2322 while (domain && *domain)
2324 PWCHAR comma = wcschr(domain, L
',');
2325 size_t domain_len = comma ? (size_t)(comma - domain) : wcslen(domain);
2330 domain = comma ? comma + 1 : domain + domain_len;
2335 if (tmp_len + dot_len + domain_len + term_len > max_len)
2343 ret = ERROR_MORE_DATA;
2349 wcsncpy(tmp_pos, domain, domain_len);
2350 tmp_pos += domain_len;
2352 tmp_len += dot_len + domain_len + 1;
2354 domain = comma ? comma + 1 : domain + domain_len;
2359 ret = ERROR_FILE_NOT_FOUND;
2370 wmemcpy(domains, tmp, tmp_len);
2373 *size = (DWORD)(tmp_len * glyph_size);
2401 if (domains == NULL || size == NULL || *size == 0)
2403 return ERROR_INVALID_PARAMETER;
2406 LSTATUS err = ERROR_FILE_NOT_FOUND;
2407 const DWORD buf_size = *size;
2408 const DWORD glyph_size =
sizeof(*domains);
2409 PWSTR values[] = { L
"SearchList", L
"Domain", L
"DhcpDomainSearchList", L
"DhcpDomain", NULL };
2411 for (
int i = 0; values[i]; i++)
2414 err = RegGetValueW(itf, NULL, values[i], RRF_RT_REG_SZ, NULL, (PBYTE)domains, size);
2415 if (!err && *size > glyph_size && domains[(*size / glyph_size) - 1] ==
'\0' && wcschr(domains,
'.'))
2438 MIB_IF_ROW2 itf_row;
2441 if (IIDFromString(iid_str, &iid) != S_OK)
2449 if (ConvertInterfaceGuidToLuid(&iid, &itf_row.InterfaceLuid) != NO_ERROR)
2455 if (GetIfEntry2(&itf_row) != NO_ERROR)
2461 if (itf_row.MediaConnectState == MediaConnectStateConnected
2462 && itf_row.OperStatus == IfOperStatusUp)
2483 HKEY v4_itfs = INVALID_HANDLE_VALUE;
2484 HKEY v6_itfs = INVALID_HANDLE_VALUE;
2492 DWORD enum_index = 0;
2493 while (i < data_size)
2495 WCHAR itf_guid[MAX_PATH];
2496 DWORD itf_guid_len = _countof(itf_guid);
2498 RegEnumKeyExW(v4_itfs, enum_index++, itf_guid, &itf_guid_len, NULL, NULL, NULL, NULL);
2501 if (err != ERROR_NO_MORE_ITEMS)
2515 if (RegOpenKeyExW(v4_itfs, itf_guid, 0, KEY_READ, &v4_itf) != NO_ERROR)
2523 data[i].domains_size =
sizeof(
data[0].domains);
2524 memset(
data[i].domains, 0,
data[i].domains_size);
2528 if (err != ERROR_FILE_NOT_FOUND)
2537 DWORD v4_addrs_size =
sizeof(
data[0].addresses);
2539 if (err && err != ERROR_FILE_NOT_FOUND)
2542 __func__, itf_guid, err);
2547 PSTR v6_addrs =
data[i].addresses + v4_addrs_size;
2548 DWORD v6_addrs_size =
sizeof(
data[0].addresses) - v4_addrs_size;
2552 if (RegOpenKeyExW(v6_itfs, itf_guid, 0, KEY_READ, &v6_itf) != NO_ERROR)
2555 __func__, itf_guid);
2559 RegCloseKey(v6_itf);
2560 if (err && err != ERROR_FILE_NOT_FOUND)
2563 __func__, itf_guid, err);
2568 if (v4_addrs_size || v6_addrs_size)
2571 for (
size_t j = 0; j <
sizeof(
data[0].addresses) &&
data[i].addresses[j]; j++)
2573 if (
data[i].addresses[j] ==
',' ||
data[i].addresses[j] ==
' ')
2575 data[i].addresses[j] =
';';
2582 RegCloseKey(v4_itf);
2586 RegCloseKey(v6_itfs);
2587 RegCloseKey(v4_itfs);
2607 DWORD err = NO_ERROR;
2609 err = RegCreateKeyExW(nrpt_key, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &rule_key, NULL);
2616 err = RegSetValueExW(rule_key, L
"Name", 0, REG_MULTI_SZ, (PBYTE)domains, dom_size);
2623 err = RegSetValueExA(rule_key,
"GenericDNSServers", 0, REG_SZ, (PBYTE)
address,
2635 err = RegSetValueExA(rule_key,
"DNSSECValidationRequired", 0, REG_DWORD, (PBYTE)®_val,
2643 err = RegSetValueExA(rule_key,
"DNSSECQueryIPSECRequired", 0, REG_DWORD, (PBYTE)®_val,
2651 err = RegSetValueExA(rule_key,
"DNSSECQueryIPSECEncryption", 0, REG_DWORD, (PBYTE)®_val,
2660 reg_val = dnssec ? 0x0000000A : 0x00000008;
2661 err = RegSetValueExA(rule_key,
"ConfigOptions", 0, REG_DWORD, (
const PBYTE)®_val,
2670 err = RegSetValueExA(rule_key,
"Version", 0, REG_DWORD, (
const PBYTE)®_val,
sizeof(reg_val));
2679 RegDeleteKeyW(nrpt_key, subkey);
2681 RegCloseKey(rule_key);
2702 for (
size_t i = 0; i < _countof(
data); ++i)
2712 swprintf(subkey, _countof(subkey), L
"OpenVPNDNSRoutingX-%02x-%lu", ++n, ovpn_pid);
2735 const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
2737 DWORD err = NO_ERROR;
2738 PWSTR wide_domains = L
".\0";
2744 size_t domains_len = strlen(domains);
2745 dom_size = (DWORD)domains_len + 2;
2750 return ERROR_OUTOFMEMORY;
2752 domains_len = wcslen(wide_domains);
2753 dom_size = (DWORD)(domains_len + 2) *
sizeof(*wide_domains);
2756 for (
size_t i = 0; i < domains_len; ++i)
2758 if (wide_domains[i] ==
',')
2760 wide_domains[i] = 0;
2766 PWSTR wide_search_domains;
2767 wide_search_domains =
utf8to16(search_domains);
2768 if (!wide_search_domains)
2770 return ERROR_OUTOFMEMORY;
2773 free(wide_search_domains);
2776 if (addresses[0][0])
2780 PSTR
pos = addr_list;
2787 strcpy(
pos, addresses[i]);
2791 WCHAR subkey[MAX_PATH];
2792 swprintf(subkey, _countof(subkey), L
"OpenVPNDNSRouting-%lu", ovpn_pid);
2793 err =
SetNrptRule(nrpt_key, subkey, addr_list, wide_domains, dom_size, dnssec);
2824 static PCSTR gpol_key =
"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
2825 static PCSTR sys_key =
2826 "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
2830 LSTATUS err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, gpol_key, 0, KEY_ALL_ACCESS, &nrpt);
2831 if (err == ERROR_FILE_NOT_FOUND)
2834 err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sys_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &nrpt,
2838 nrpt = INVALID_HANDLE_VALUE;
2872 swprintf(pid_str, _countof(pid_str), L
"-%lu", pid);
2873 pidlen = wcslen(pid_str);
2877 DWORD enum_index = 0;
2880 WCHAR name[MAX_PATH];
2881 DWORD namelen = _countof(name);
2882 err = RegEnumKeyExW(
key, enum_index++, name, &namelen, NULL, NULL, NULL, NULL);
2885 if (err != ERROR_NO_MORE_ITEMS)
2893 if (wcsncmp(name, L
"OpenVPNDNSRouting", 17) != 0
2894 || (pid && wcsncmp(name + namelen - pidlen, pid_str, pidlen) != 0))
2899 if (RegDeleteKeyW(
key, name) == NO_ERROR)
2907 return deleted ? TRUE : FALSE;
2945 msgptr->
iface.
name[_countof(
msg->iface.name) - 1] =
'\0';
2950 msgptr->
addresses[i][_countof(
msg->addresses[0]) - 1] =
'\0';
2955 if (
msg->iface.name[0] == 0)
2964 if (
msg->addresses[0][0] == 0)
2969 const char *rdom =
msg->resolve_domains;
2970 size_t rdom_size =
sizeof(
msg->resolve_domains);
2971 size_t rdom_len = strlen(rdom);
2972 if (rdom_len && (rdom_len + 1 >= rdom_size || rdom[rdom_len + 1] != 0))
2978 BOOL gpol_nrpt = FALSE;
2979 BOOL gpol_list = FALSE;
2992 if (*undo_pid != ovpn_pid)
2995 L
"%S: PID stored for undo doesn't match: %lu vs %lu. "
2996 "This is likely an error. Cleaning up anyway.",
2997 __func__, *undo_pid, ovpn_pid);
3021 PDWORD pid = malloc(
sizeof(ovpn_pid));
3024 err = ERROR_OUTOFMEMORY;
3030 err = ERROR_OUTOFMEMORY;
3050 if (
msg->search_domains[0] || !
msg->resolve_domains[0])
3060 if (
msg->search_domains[0])
3074 DWORD err = NO_ERROR;
3076 unsigned int addr_len =
msg->addr_len;
3079 if (addr_len > _countof(
msg->addr))
3081 addr_len = _countof(
msg->addr);
3084 if (!
msg->iface.index)
3107 for (
unsigned int i = 0; i < addr_len; ++i)
3109 RtlIpv4AddressToStringW(&
msg->addr[i].ipv4, addr);
3120 PDWORD if_index = malloc(
sizeof(
msg->iface.index));
3123 *if_index =
msg->iface.index;
3130 err = ERROR_OUTOFMEMORY;
3144 DWORD timeout = 5000;
3145 wchar_t argv0[MAX_PATH];
3148 swprintf(argv0, _countof(argv0), L
"%ls\\%ls",
get_win_sys_path(), L
"netsh.exe");
3153 const wchar_t *fmt = L
"netsh interface ipv4 set address name=\"%lu\" source=dhcp";
3158 size_t ncmdline = wcslen(fmt) + 10 + 1;
3159 wchar_t *cmdline = malloc(ncmdline *
sizeof(
wchar_t));
3162 err = ERROR_OUTOFMEMORY;
3166 swprintf(cmdline, ncmdline, fmt,
dhcp->iface.index);
3182 MIB_IPINTERFACE_ROW ipiface;
3183 InitializeIpInterfaceEntry(&ipiface);
3184 ipiface.Family = mtu->
family;
3186 err = GetIpInterfaceEntry(&ipiface);
3187 if (err != NO_ERROR)
3191 if (mtu->
family == AF_INET)
3193 ipiface.SitePrefixLength = 0;
3195 ipiface.NlMtu = mtu->
mtu;
3197 err = SetIpInterfaceEntry(&ipiface);
3213 switch (
msg->adapter_type)
3220 hwid = L
"root\\tap0901";
3224 return ERROR_INVALID_PARAMETER;
3227 WCHAR cmd[MAX_PATH];
3228 WCHAR args[MAX_PATH];
3230 if (swprintf_s(cmd, _countof(cmd), L
"%s\\tapctl.exe",
settings.
bin_dir) < 0)
3232 return ERROR_BUFFER_OVERFLOW;
3235 if (swprintf_s(args, _countof(args), L
"tapctl create --hwid %s", hwid) < 0)
3237 return ERROR_BUFFER_OVERFLOW;
3244HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count,
3261 switch (
msg.header.type)
3265 if (
msg.header.size ==
sizeof(
msg.address))
3273 if (
msg.header.size ==
sizeof(
msg.route))
3280 if (
msg.header.size ==
sizeof(
msg.flush_neighbors))
3288 if (
msg.header.size ==
sizeof(
msg.wfp_block))
3300 if (
msg.header.size ==
sizeof(
msg.dns))
3308 if (
msg.header.size ==
sizeof(
msg.nrpt_dns))
3310 DWORD ovpn_pid = proc_info->dwProcessId;
3317 if (
msg.header.size ==
sizeof(
msg.wins))
3324 if (
msg.header.size ==
sizeof(
msg.dhcp))
3331 if (
msg.header.size ==
sizeof(
msg.mtu))
3338 if (
msg.header.size ==
sizeof(
msg.create_adapter))
3417 *pnext = item->
next;
3434 static PCWSTR user_vars[] = { L
"APPDATA=", L
"LOCALAPPDATA=", L
"TEMP=", L
"TMP=",
3435 L
"USERDOMAIN=", L
"USERNAME=", L
"USERPROFILE=" };
3437 for (
size_t i = 0; i < _countof(user_vars); ++i)
3439 if (wcsncmp(var, user_vars[i], wcslen(user_vars[i])) == 0)
3460 var += wcslen(var) + 1;
3462 return ((
size_t)(var - env) + 1) *
sizeof(*env);
3489 for (PCWSTR var = sys_env; *var; var += wcslen(var) + 1)
3493 size_t len = wcslen(var) + 1;
3494 wmemcpy(
pos, var, len);
3498 for (PCWSTR var = usr_env; *var; var += wcslen(var) + 1)
3502 size_t len = wcslen(var) + 1;
3503 wmemcpy(
pos, var, len);
3527 LPVOID sys_env = NULL;
3528 LPVOID usr_env = NULL;
3531 if (!CreateEnvironmentBlock(&sys_env, NULL, FALSE)
3532 || !CreateEnvironmentBlock(&usr_env, token, FALSE))
3540 SetLastError(ERROR_OUTOFMEMORY);
3546 DestroyEnvironmentBlock(sys_env);
3547 DestroyEnvironmentBlock(usr_env);
3555 HANDLE ovpn_pipe = NULL, svc_pipe = NULL;
3556 PTOKEN_USER svc_user = NULL, ovpn_user = NULL;
3557 HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL;
3558 HANDLE stdin_read = NULL, stdin_write = NULL;
3559 HANDLE stdout_write = NULL;
3560 DWORD pipe_mode, len, exit_code = 0;
3562 STARTUPINFOW startup_info;
3563 PROCESS_INFORMATION proc_info;
3564 PWSTR ovpn_env = NULL;
3565 WCHAR ovpn_pipe_name[256];
3568 WCHAR *cmdline = NULL;
3569 size_t cmdline_size;
3571 WCHAR errmsg[512] = L
"";
3572 BOOL flush_pipe = TRUE;
3574 SECURITY_ATTRIBUTES inheritable = { .nLength =
sizeof(inheritable),
3575 .lpSecurityDescriptor = NULL,
3576 .bInheritHandle = TRUE };
3578 PACL ovpn_dacl = NULL;
3579 PSECURITY_DESCRIPTOR pSD = NULL;
3580 EXPLICIT_ACCESS ea[2];
3581 SECURITY_DESCRIPTOR ovpn_sd;
3582 SECURITY_ATTRIBUTES ovpn_sa = { .nLength =
sizeof(ovpn_sa),
3583 .lpSecurityDescriptor = &ovpn_sd,
3584 .bInheritHandle = FALSE };
3586 ZeroMemory(&ea,
sizeof(ea));
3587 ZeroMemory(&startup_info,
sizeof(startup_info));
3588 ZeroMemory(&undo_lists,
sizeof(undo_lists));
3589 ZeroMemory(&proc_info,
sizeof(proc_info));
3597 if (!InitializeSecurityDescriptor(&ovpn_sd, SECURITY_DESCRIPTOR_REVISION))
3604 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &svc_token))
3610 while (!GetTokenInformation(svc_token, TokenUser, svc_user, len, &len))
3612 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3618 svc_user = malloc(len);
3619 if (svc_user == NULL)
3625 if (!IsValidSid(svc_user->User.Sid))
3631 if (!ImpersonateNamedPipeClient(pipe))
3636 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &imp_token))
3642 while (!GetTokenInformation(imp_token, TokenUser, ovpn_user, len, &len))
3644 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3650 ovpn_user = malloc(len);
3651 if (ovpn_user == NULL)
3657 if (!IsValidSid(ovpn_user->User.Sid))
3664 ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
3665 ea[0].grfAccessMode = SET_ACCESS;
3666 ea[0].grfInheritance = NO_INHERITANCE;
3667 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3668 ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3669 ea[0].Trustee.ptstrName = (LPWSTR)svc_user->User.Sid;
3670 ea[1].grfAccessPermissions = READ_CONTROL | PROCESS_VM_READ | SYNCHRONIZE
3671 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION;
3672 ea[1].grfAccessMode = SET_ACCESS;
3673 ea[1].grfInheritance = NO_INHERITANCE;
3674 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
3675 ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
3676 ea[1].Trustee.ptstrName = (LPWSTR)ovpn_user->User.Sid;
3679 if (!SetSecurityDescriptorOwner(&ovpn_sd, svc_user->User.Sid, FALSE))
3684 if (SetEntriesInAcl(2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS)
3689 if (!SetSecurityDescriptorDacl(&ovpn_sd, TRUE, ovpn_dacl, FALSE))
3701 BYTE medium_sid[SECURITY_MAX_SID_SIZE];
3702 DWORD medium_sid_size =
sizeof(medium_sid);
3703 DWORD sacl_buf[(
sizeof(ACL) +
sizeof(SYSTEM_MANDATORY_LABEL_ACE) + SECURITY_MAX_SID_SIZE) /
sizeof(DWORD) + 1];
3704 PACL sacl = (PACL)sacl_buf;
3705 if (!CreateWellKnownSid(WinMediumLabelSid, NULL, medium_sid, &medium_sid_size)
3706 || !InitializeAcl(sacl, (DWORD)
sizeof(sacl_buf), ACL_REVISION)
3707 || !AddMandatoryAce(sacl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, medium_sid)
3708 || !SetSecurityDescriptorSacl(&ovpn_sd, TRUE, sacl, FALSE))
3715 if (!DuplicateTokenEx(imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token))
3726 BYTE high_sid[SECURITY_MAX_SID_SIZE];
3727 DWORD high_sid_size =
sizeof(high_sid);
3728 if (!RevertToSelf())
3733 if (!CreateWellKnownSid(WinHighLabelSid, NULL, high_sid, &high_sid_size))
3738 TOKEN_MANDATORY_LABEL label = { .Label = { .Sid = high_sid, .Attributes = SE_GROUP_INTEGRITY } };
3739 DWORD label_size =
sizeof(label) + GetLengthSid(high_sid);
3740 if (!SetTokenInformation(pri_token, TokenIntegrityLevel, &label, label_size))
3745 if (!SetThreadToken(NULL, imp_token))
3752 stdout_write = CreateFile(
_L(
"NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, &inheritable,
3753 OPEN_EXISTING, 0, NULL);
3754 if (stdout_write == INVALID_HANDLE_VALUE)
3760 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
3761 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
3768 RPC_STATUS rpc_stat = UuidCreate(&pipe_uuid);
3769 if (rpc_stat != RPC_S_OK)
3775 RPC_WSTR pipe_uuid_str = NULL;
3776 rpc_stat = UuidToStringW(&pipe_uuid, &pipe_uuid_str);
3777 if (rpc_stat != RPC_S_OK)
3782 swprintf(ovpn_pipe_name, _countof(ovpn_pipe_name),
3785 GetCurrentThreadId(), pipe_uuid_str);
3786 RpcStringFreeW(&pipe_uuid_str);
3792 SECURITY_ATTRIBUTES sa;
3793 LPCWSTR szSDDL = L
"D:(A;;GA;;;SY)(A;;GA;;;OW)";
3794 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
3795 szSDDL, SDDL_REVISION_1, &pSD, NULL))
3800 sa.nLength =
sizeof(sa);
3801 sa.lpSecurityDescriptor = pSD;
3802 sa.bInheritHandle = FALSE;
3804 ovpn_pipe = CreateNamedPipe(
3805 ovpn_pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
3806 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 128, 128, 0, &sa);
3807 if (ovpn_pipe == INVALID_HANDLE_VALUE)
3813 svc_pipe = CreateFile(ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, &inheritable,
3814 OPEN_EXISTING, 0, NULL);
3815 if (svc_pipe == INVALID_HANDLE_VALUE)
3821 pipe_mode = PIPE_READMODE_MESSAGE;
3822 if (!SetNamedPipeHandleState(svc_pipe, &pipe_mode, NULL, NULL))
3828 cmdline_size = wcslen(sud.
options) + 128;
3829 cmdline = malloc(cmdline_size *
sizeof(*cmdline));
3830 if (cmdline == NULL)
3838 swprintf(cmdline, cmdline_size, L
"openvpn --msg-channel %" PRIuPTR L
" %ls",
3839 (uintptr_t)svc_pipe, sud.
options);
3862 startup_info.cb =
sizeof(startup_info);
3863 startup_info.dwFlags = STARTF_USESTDHANDLES;
3864 startup_info.hStdInput = stdin_read;
3865 startup_info.hStdOutput = stdout_write;
3866 startup_info.hStdError = stdout_write;
3871 if (!CreateProcessAsUserW(pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE,
3873 ovpn_env, sud.
directory, &startup_info, &proc_info))
3879 if (!RevertToSelf())
3881 TerminateProcess(proc_info.hProcess, 1);
3892 DWORD input_size = WideCharToMultiByte(CP_UTF8, 0, sud.
std_input, -1, NULL, 0, NULL, NULL);
3894 if (input_size && (input = malloc(input_size)))
3897 WideCharToMultiByte(CP_UTF8, 0, sud.
std_input, -1, input, input_size, NULL, NULL);
3898 WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
3915 L
"OpenVPN process sent too large payload length to the pipe (%lu bytes), it will be terminated",
3923 WaitForSingleObject(proc_info.hProcess,
IO_TIMEOUT);
3924 GetExitCodeProcess(proc_info.hProcess, &exit_code);
3925 if (exit_code == STILL_ACTIVE)
3927 TerminateProcess(proc_info.hProcess, 1);
3929 else if (exit_code != 0)
3932 swprintf(buf, _countof(buf), L
"OpenVPN exited with error: exit code = %lu", exit_code);
3940 FlushFileBuffers(pipe);
3942 DisconnectNamedPipe(pipe);
3948 LocalFree(ovpn_dacl);
3970 SERVICE_STATUS *svc_status = ctx;
3973 case SERVICE_CONTROL_STOP:
3974 svc_status->dwCurrentState = SERVICE_STOP_PENDING;
3982 case SERVICE_CONTROL_INTERROGATE:
3986 return ERROR_CALL_NOT_IMPLEMENTED;
4000 const WCHAR *sddlString =
4001 L
"D:(A;OICI;GA;;;S-1-5-18)(D;OICI;0x4;;;S-1-1-0)(A;OICI;GRGW;;;S-1-5-11)(D;;GA;;;S-1-5-7)";
4003 PSECURITY_DESCRIPTOR sd = NULL;
4004 if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, SDDL_REVISION_1, &sd,
4008 return INVALID_HANDLE_VALUE;
4012 SECURITY_ATTRIBUTES sa = { 0 };
4013 sa.nLength =
sizeof(SECURITY_ATTRIBUTES);
4014 sa.lpSecurityDescriptor = sd;
4015 sa.bInheritHandle = FALSE;
4017 DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED;
4019 static BOOL first = TRUE;
4022 flags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
4026 WCHAR pipe_name[256];
4028 swprintf(pipe_name, _countof(pipe_name), L
"\\\\.\\pipe\\" _L(
PACKAGE) L
"%ls\\service",
4030 HANDLE pipe = CreateNamedPipe(
4031 pipe_name, flags, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
4032 PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, &sa);
4036 if (pipe == INVALID_HANDLE_VALUE)
4039 return INVALID_HANDLE_VALUE;
4050 static DWORD size = 10;
4051 static LPHANDLE handles = NULL;
4054 if (handles == NULL)
4056 handles = malloc(size *
sizeof(HANDLE));
4057 *handles_ptr = handles;
4058 if (handles == NULL)
4060 return ERROR_OUTOFMEMORY;
4064 handles[
pos++] = io_event;
4077 tmp = realloc(handles, size *
sizeof(HANDLE));
4082 return ERROR_OUTOFMEMORY;
4085 *handles_ptr = handles;
4087 handles[
pos++] = threads->
data;
4088 threads = threads->
next;
4112 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
4124 BOOL changed = FALSE;
4134 if (
key != INVALID_HANDLE_VALUE)
4152 HANDLE pipe, io_event = NULL;
4153 OVERLAPPED overlapped;
4154 DWORD error = NO_ERROR;
4156 PHANDLE handles = NULL;
4166 status.dwCurrentState = SERVICE_START_PENDING;
4167 status.dwServiceSpecificExitCode = NO_ERROR;
4168 status.dwWin32ExitCode = NO_ERROR;
4169 status.dwWaitHint = 3000;
4177 if (error != ERROR_SUCCESS)
4183 exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
4198 if (error != NO_ERROR)
4204 if (pipe == INVALID_HANDLE_VALUE)
4209 status.dwCurrentState = SERVICE_RUNNING;
4215 if (!ConnectNamedPipe(pipe, &overlapped))
4217 DWORD connect_error = GetLastError();
4218 if (connect_error == ERROR_NO_DATA)
4225 DisconnectNamedPipe(pipe);
4229 else if (connect_error == ERROR_PIPE_CONNECTED)
4232 SetEvent(overlapped.hEvent);
4234 else if (connect_error != ERROR_IO_PENDING)
4241 error = WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
4242 if (error == WAIT_OBJECT_0)
4248 if (handle_count + 1 > MAXIMUM_WAIT_OBJECTS)
4257 HANDLE thread = CreateThread(NULL, 0,
RunOpenvpn, pipe, CREATE_SUSPENDED, NULL);
4268 ReturnError(pipe, error, L
"Insufficient resources to service new clients", 1,
4273 TerminateThread(thread, 1);
4279 ResumeThread(thread);
4293 if (error == WAIT_FAILED)
4323 status.dwCurrentState = SERVICE_STOPPED;
4324 status.dwWin32ExitCode = error;
wchar_t * utf8to16_size(const char *utf8, int size)
Convert a UTF-8 string to UTF-16.
DWORD MsgToEventLog(DWORD flags, LPCWSTR format,...)
DWORD GetOpenvpnSettings(settings_t *s)
static LSTATUS GetItfDnsServersV4(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv4 addresses of an interface.
static LSTATUS SetNameServerAddresses(PWSTR itf_id, const nrpt_address_t *addresses)
Set name servers from a NRPT address list.
static BOOL ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *cmdline, WCHAR *errmsg, DWORD capacity)
static VOID ReturnLastError(HANDLE pipe, LPCWSTR func)
static BOOL GetInterfacesKey(short family, PHKEY key)
Return the interfaces registry key for the specified address family.
static DWORD ReadPipeAsync(HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static void UndoNrptRules(DWORD ovpn_pid)
Delete a process' NRPT rules and apply the reduced set of rules.
static BOOL ApplyGpolSettings(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings.
static VOID ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
static BOOL GetDnsSearchListKey(PCSTR itf_name, PBOOL gpol, PHKEY key)
Find the registry key for storing the DNS domains for the VPN interface.
static DWORD HandleWINSConfigMessage(const wins_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAddress(LPVOID item, LPVOID address)
static LSTATUS GetItfDnsDomains(HKEY itf, PCWSTR search_domains, PWSTR domains, PDWORD size)
Return interface specific domain suffix(es)
static DWORD PeekNamedPipeAsyncTimed(HANDLE pipe, DWORD count, LPHANDLE events)
static DWORD PeekNamedPipeAsync(HANDLE pipe, DWORD count, LPHANDLE events)
static BOOL ResetOverlapped(LPOVERLAPPED overlapped)
static DWORD SetNameServers(PCWSTR itf_id, short family, PCSTR addrs)
Set the DNS name servers in a registry interface configuration.
static void SetNrptExcludeRules(HKEY nrpt_key, DWORD ovpn_pid, PCWSTR search_domains)
Set NRPT exclude rules to accompany a catch all rule.
static DWORD ExecCommand(const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
static DWORD HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp)
static BOOL ResetDnsSearchDomains(HKEY key)
Reset the DNS search list to its original value.
static DWORD AddWfpBlock(const wfp_block_message_t *msg, undo_lists_t *lists)
static HANDLE CreateClientPipeInstance(VOID)
static DWORD DeleteWfpBlock(undo_lists_t *lists)
static void GetNrptExcludeData(PCWSTR search_domains, nrpt_exclude_data_t *data, size_t data_size)
Collect interface DNS settings to be used in excluding NRPT rules.
static DWORD SetNameServersValue(PCWSTR itf_id, short family, PCSTR value)
Set the DNS name servers in a registry interface configuration.
static BOOL GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
static BOOL DeleteNrptRules(DWORD pid, PBOOL gpol)
Delete OpenVPN NRPT rules from the registry.
static VOID Undo(undo_lists_t *lists)
static BOOL ApplyDnsSettings(BOOL apply_gpol)
Signal the DNS resolver to reload its settings.
#define ERROR_STARTUP_DATA
static DWORD WINAPI RunOpenvpn(LPVOID p)
static settings_t settings
VOID WINAPI ServiceStartInteractive(DWORD dwArgc, LPWSTR *lpszArgv)
static BOOL IsUserEnvVar(PCWSTR var)
Check if an environment variable is user-specific.
static PWSTR MergeOpenvpnEnvironment(PCWSTR sys_env, PCWSTR usr_env)
Merge the environment for the openvpn process.
static DWORD DeleteRoute(PMIB_IPFORWARD_ROW2 fwd_row)
static SERVICE_STATUS status
static DWORD HandleDNSConfigNrptMessage(const nrpt_dns_cfg_message_t *msg, DWORD ovpn_pid, undo_lists_t *lists)
Add Name Resolution Policy Table (NRPT) rules as documented in https://msdn.microsoft....
static DWORD SetDnsSearchDomains(PCSTR itf_name, PCSTR domains, PBOOL gpol, undo_lists_t *lists)
Add or remove DNS search domains.
static void CleanupRegistry(void)
Clean up remains of previous sessions in registry.
static DWORD netsh_wins_cmd(const wchar_t *action, DWORD if_index, const wchar_t *addr)
Run the command: netsh interface ip $action wins $if_index [static] $addr.
#define ERROR_MESSAGE_TYPE
static LPVOID RemoveListItem(list_item_t **pfirst, match_fn_t match, LPVOID ctx)
static BOOL CmpHandle(LPVOID item, LPVOID hnd)
static BOOL ApplyGpolSettings64(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 64 bit ...
static DWORD HandleAddressMessage(address_message_t *msg, undo_lists_t *lists)
static VOID ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events)
static DWORD AddListItem(list_item_t **pfirst, LPVOID data)
static void BlockDNSErrHandler(DWORD err, const char *msg)
static DWORD ResetNameServers(PCWSTR itf_id, short family)
Delete all DNS name servers from a registry interface configuration.
static LSTATUS OpenNrptBaseKey(PHKEY key, PBOOL gpol)
Return the registry key where NRPT rules are stored.
static BOOL HasValidSearchList(HKEY key)
Check for a valid search list in a certain key of the registry.
static DWORD HandleRouteMessage(route_message_t *msg, undo_lists_t *lists)
static DWORD WINAPI RegisterDNS(LPVOID unused)
static HANDLE InitOverlapped(LPOVERLAPPED overlapped)
BOOL(* match_fn_t)(LPVOID item, LPVOID ctx)
static HANDLE CloseHandleEx(LPHANDLE handle)
static DWORD WINAPI ServiceCtrlInteractive(DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx)
static BOOL StoreInitialDnsSearchList(HKEY key, PCWSTR list)
Prepare DNS domain "SearchList" registry value, so additional VPN domains can be added and its origin...
struct _list_item list_item_t
static DWORD RegWStringSize(PCWSTR string)
Return correct size for registry value to set for string.
static DWORD DeleteAddress(PMIB_UNICASTIPADDRESS_ROW addr_row)
static BOOL IsInterfaceConnected(PWSTR iid_str)
Check if an interface is connected and up.
#define ERROR_OPENVPN_STARTUP
static DWORD SetNrptRules(HKEY nrpt_key, const nrpt_address_t *addresses, const char *domains, const char *search_domains, BOOL dnssec, DWORD ovpn_pid)
Set NRPT rules for a openvpn process.
static LSTATUS GetItfDnsServersV6(HKEY itf_key, PSTR addrs, PDWORD size)
Get DNS server IPv6 addresses of an interface.
static size_t EnvBlockSize(PCWSTR env)
Return the size of an environment block.
static BOOL AppendSearchList(PWSTR list, size_t list_cap, PCWSTR add)
Append a comma-separated list of domains to another comma-separated list, in place.
static DWORD SetNrptRule(HKEY nrpt_key, PCWSTR subkey, PCSTR address, PCWSTR domains, DWORD dom_size, BOOL dnssec)
Set a NRPT rule (subkey) and its values in the registry.
static BOOL AddDnsSearchDomains(HKEY key, BOOL have_list, PCWSTR domains)
Append domain suffixes to an existing search list.
static VOID FreeWaitHandles(LPHANDLE h)
openvpn_service_t interactive_service
VOID WINAPI ServiceStartInteractiveOwn(DWORD dwArgc, LPWSTR *lpszArgv)
static size_t RemoveSearchListTokens(PWSTR list, PCWSTR remove)
Remove tokens from a comma-separated search list with multiset semantics: for each comma-separated to...
static DWORD AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
static BOOL ListContainsDomain(PCWSTR list, PCWSTR domain, size_t len)
Check if a domain is contained in a comma separated list of domains.
static BOOL IsDhcpEnabled(HKEY key)
Checks if DHCP is enabled for an interface.
static BOOL CreateOpenvpnEnvironment(HANDLE token, PWSTR *env)
Create the environment for the openvpn process.
static DWORD HandleFlushNeighborsMessage(flush_neighbors_message_t *msg)
static BOOL ApplyGpolSettings32(void)
Signal the DNS resolver (and others potentially) to reload the group policy (DNS) settings on 32 bit ...
static DWORD HandleMTUMessage(const set_mtu_message_t *mtu)
list_item_t * undo_lists_t[_undo_type_max]
static VOID HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
static DWORD HandleRegisterDNSMessage(void)
static void RemoveDnsSearchDomains(HKEY key, PCWSTR domains)
Remove domain suffixes from an existing search list.
static BOOL InitialSearchListExists(HKEY key)
Check if a initial list had already been created.
#define ERROR_MESSAGE_DATA
static VOID FreeStartupData(STARTUP_DATA *sud)
static DWORD HandleWfpBlockMessage(const wfp_block_message_t *msg, undo_lists_t *lists)
static HANDLE rdns_semaphore
static DWORD UpdateWaitHandles(LPHANDLE *handles_ptr, LPDWORD count, HANDLE io_event, const list_item_t *threads)
static DWORD InterfaceLuid(const char *iface_name, PNET_LUID luid)
static LSTATUS ConvertItfDnsDomains(PCWSTR search_domains, PWSTR domains, PDWORD size, const DWORD capacity)
Convert interface specific domain suffix(es) from comma-separated string to MULTI_SZ string.
static BOOL CmpRoute(LPVOID item, LPVOID route)
static DWORD HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
static BOOL CmpAny(LPVOID item, LPVOID any)
static DWORD HandleCreateAdapterMessage(const create_adapter_message_t *msg)
Creates a VPN adapter of the specified type by invoking tapctl.exe.
static DWORD InterfaceIdString(PCSTR itf_name, PWSTR str, size_t len)
Get the string interface UUID (with braces) for an interface alias name.
static SERVICE_STATUS_HANDLE service
static DWORD WritePipeAsync(HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events)
static SOCKADDR_INET sockaddr_inet(short family, const inet_address_t *addr)
static void UndoDnsSearchDomains(dns_domains_undo_data_t *undo_data)
Removes DNS domains from a search list they were previously added to.
#define TUN_ADAPTER_INDEX_INVALID
char nrpt_address_t[NRPT_ADDR_SIZE]
BOOL ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
#define SERVICE_DEPENDENCIES
static wchar_t * utf8to16(const char *utf8)
Convert a zero terminated UTF-8 string to UTF-16.
Wrapper structure for dynamically allocated memory.
Container for unidirectional cipher and HMAC key material.
char resolve_domains[512]
nrpt_address_t addresses[NRPT_ADDR_NUM]
CHAR addresses[NRPT_ADDR_NUM *NRPT_ADDR_SIZE]
WCHAR ovpn_admin_group[MAX_NAME]
WCHAR ovpn_service_user[MAX_NAME]
static int cleanup(void **state)
address_message_t address
flush_neighbors_message_t flush_neighbors
wfp_block_message_t wfp_block
enable_dhcp_message_t dhcp
nrpt_dns_cfg_message_t nrpt_dns
create_adapter_message_t create_adapter
BOOL IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group, const WCHAR *ovpn_service_user)
BOOL CheckOption(const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s)
static BOOL IsOption(const WCHAR *o)
int get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
Return interface metric value for the specified interface index.
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
DWORD delete_wfp_block_filters(HANDLE engine_handle)
DWORD add_wfp_block_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, wfp_block_msg_handler_t msg_handler, BOOL dns_only)
#define WFP_BLOCK_IFACE_METRIC
char * get_win_sys_path(void)