WinDNSHelper.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #include "WinDNSHelper.hpp"
  2. #include <comdef.h>
  3. #include <WbemIdl.h>
  4. #include <vector>
  5. #include <string>
  6. #include <sstream>
  7. #include <strsafe.h>
  8. #define MAX_KEY_LENGTH 255
  9. #define MAX_VALUE_NAME 16383
  10. namespace ZeroTier
  11. {
  12. BOOL RegDelnodeRecurse(HKEY hKeyRoot, LPTSTR lpSubKey)
  13. {
  14. LPTSTR lpEnd;
  15. LONG lResult;
  16. DWORD dwSize;
  17. TCHAR szName[MAX_PATH];
  18. HKEY hKey;
  19. FILETIME ftWrite;
  20. // First, see if we can delete the key without having
  21. // to recurse.
  22. lResult = RegDeleteKey(hKeyRoot, lpSubKey);
  23. if (lResult == ERROR_SUCCESS)
  24. return TRUE;
  25. lResult = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
  26. if (lResult != ERROR_SUCCESS)
  27. {
  28. if (lResult == ERROR_FILE_NOT_FOUND) {
  29. return TRUE;
  30. }
  31. else {
  32. return FALSE;
  33. }
  34. }
  35. // Check for an ending slash and add one if it is missing.
  36. lpEnd = lpSubKey + lstrlen(lpSubKey);
  37. if (*(lpEnd - 1) != TEXT('\\'))
  38. {
  39. *lpEnd = TEXT('\\');
  40. lpEnd++;
  41. *lpEnd = TEXT('\0');
  42. }
  43. // Enumerate the keys
  44. dwSize = MAX_PATH;
  45. lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
  46. NULL, NULL, &ftWrite);
  47. if (lResult == ERROR_SUCCESS)
  48. {
  49. do {
  50. *lpEnd = TEXT('\0');
  51. StringCchCat(lpSubKey, MAX_PATH * 2, szName);
  52. if (!RegDelnodeRecurse(hKeyRoot, lpSubKey)) {
  53. break;
  54. }
  55. dwSize = MAX_PATH;
  56. lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
  57. NULL, NULL, &ftWrite);
  58. } while (lResult == ERROR_SUCCESS);
  59. }
  60. lpEnd--;
  61. *lpEnd = TEXT('\0');
  62. RegCloseKey(hKey);
  63. // Try again to delete the key.
  64. lResult = RegDeleteKey(hKeyRoot, lpSubKey);
  65. if (lResult == ERROR_SUCCESS)
  66. return TRUE;
  67. return FALSE;
  68. }
  69. //*************************************************************
  70. //
  71. // RegDelnode()
  72. //
  73. // Purpose: Deletes a registry key and all its subkeys / values.
  74. //
  75. // Parameters: hKeyRoot - Root key
  76. // lpSubKey - SubKey to delete
  77. //
  78. // Return: TRUE if successful.
  79. // FALSE if an error occurs.
  80. //
  81. //*************************************************************
  82. BOOL RegDelnode(HKEY hKeyRoot, LPCTSTR lpSubKey)
  83. {
  84. TCHAR szDelKey[MAX_PATH * 2];
  85. StringCchCopy(szDelKey, MAX_PATH * 2, lpSubKey);
  86. return RegDelnodeRecurse(hKeyRoot, szDelKey);
  87. }
  88. std::vector<std::string> getSubKeys(const char* key)
  89. {
  90. std::vector<std::string> subkeys;
  91. HKEY hKey;
  92. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
  93. key,
  94. 0,
  95. KEY_READ,
  96. &hKey) == ERROR_SUCCESS) {
  97. TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
  98. DWORD cbName; // size of name string
  99. TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
  100. DWORD cchClassName = MAX_PATH; // size of class string
  101. DWORD cSubKeys = 0; // number of subkeys
  102. DWORD cbMaxSubKey; // longest subkey size
  103. DWORD cchMaxClass; // longest class string
  104. DWORD cValues; // number of values for key
  105. DWORD cchMaxValue; // longest value name
  106. DWORD cbMaxValueData; // longest value data
  107. DWORD cbSecurityDescriptor; // size of security descriptor
  108. FILETIME ftLastWriteTime; // last write time
  109. DWORD i, retCode;
  110. TCHAR achValue[MAX_VALUE_NAME];
  111. DWORD cchValue = MAX_VALUE_NAME;
  112. retCode = RegQueryInfoKey(
  113. hKey, // key handle
  114. achClass, // buffer for class name
  115. &cchClassName, // size of class string
  116. NULL, // reserved
  117. &cSubKeys, // number of subkeys
  118. &cbMaxSubKey, // longest subkey size
  119. &cchMaxClass, // longest class string
  120. &cValues, // number of values for this key
  121. &cchMaxValue, // longest value name
  122. &cbMaxValueData, // longest value data
  123. &cbSecurityDescriptor, // security descriptor
  124. &ftLastWriteTime); // last write time
  125. for (i = 0; i < cSubKeys; ++i) {
  126. cbName = MAX_KEY_LENGTH;
  127. retCode = RegEnumKeyEx(
  128. hKey,
  129. i,
  130. achKey,
  131. &cbName,
  132. NULL,
  133. NULL,
  134. NULL,
  135. &ftLastWriteTime);
  136. if (retCode == ERROR_SUCCESS) {
  137. subkeys.push_back(achKey);
  138. }
  139. }
  140. }
  141. RegCloseKey(hKey);
  142. return subkeys;
  143. }
  144. std::vector<std::string> getValueList(const char* key) {
  145. std::vector<std::string> values;
  146. HKEY hKey;
  147. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
  148. key,
  149. 0,
  150. KEY_READ,
  151. &hKey) == ERROR_SUCCESS) {
  152. TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
  153. DWORD cbName; // size of name string
  154. TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
  155. DWORD cchClassName = MAX_PATH; // size of class string
  156. DWORD cSubKeys = 0; // number of subkeys
  157. DWORD cbMaxSubKey; // longest subkey size
  158. DWORD cchMaxClass; // longest class string
  159. DWORD cValues; // number of values for key
  160. DWORD cchMaxValue; // longest value name
  161. DWORD cbMaxValueData; // longest value data
  162. DWORD cbSecurityDescriptor; // size of security descriptor
  163. FILETIME ftLastWriteTime; // last write time
  164. DWORD i, retCode;
  165. TCHAR achValue[MAX_VALUE_NAME];
  166. DWORD cchValue = MAX_VALUE_NAME;
  167. retCode = RegQueryInfoKey(
  168. hKey, // key handle
  169. achClass, // buffer for class name
  170. &cchClassName, // size of class string
  171. NULL, // reserved
  172. &cSubKeys, // number of subkeys
  173. &cbMaxSubKey, // longest subkey size
  174. &cchMaxClass, // longest class string
  175. &cValues, // number of values for this key
  176. &cchMaxValue, // longest value name
  177. &cbMaxValueData, // longest value data
  178. &cbSecurityDescriptor, // security descriptor
  179. &ftLastWriteTime); // last write time
  180. for (i = 0, retCode = ERROR_SUCCESS; i < cValues; ++i) {
  181. cchValue = MAX_VALUE_NAME;
  182. achValue[0] = '\0';
  183. retCode = RegEnumValue(
  184. hKey,
  185. i,
  186. achValue,
  187. &cchValue,
  188. NULL,
  189. NULL,
  190. NULL,
  191. NULL);
  192. if (retCode == ERROR_SUCCESS) {
  193. values.push_back(achValue);
  194. }
  195. }
  196. }
  197. RegCloseKey(hKey);
  198. return values;
  199. }
  200. std::pair<bool, std::string> WinDNSHelper::hasDNSConfig(uint64_t nwid)
  201. {
  202. char networkStr[20] = { 0 };
  203. sprintf(networkStr, "%.16llx", nwid);
  204. const char* baseKey = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
  205. auto subkeys = getSubKeys(baseKey);
  206. for (auto it = subkeys.begin(); it != subkeys.end(); ++it) {
  207. char sub[MAX_KEY_LENGTH] = { 0 };
  208. sprintf(sub, "%s\\%s", baseKey, it->c_str());
  209. auto dnsRecords = getValueList(sub);
  210. for (auto it2 = dnsRecords.begin(); it2 != dnsRecords.end(); ++it2) {
  211. if ((*it2) == "Comment") {
  212. HKEY hKey;
  213. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
  214. sub,
  215. 0,
  216. KEY_READ,
  217. &hKey) == ERROR_SUCCESS) {
  218. char buf[16384] = { 0 };
  219. DWORD size = sizeof(buf);
  220. DWORD retCode = RegGetValueA(
  221. HKEY_LOCAL_MACHINE,
  222. sub,
  223. it2->c_str(),
  224. RRF_RT_REG_SZ,
  225. NULL,
  226. &buf,
  227. &size);
  228. if (retCode == ERROR_SUCCESS) {
  229. if (std::string(networkStr) == std::string(buf)) {
  230. RegCloseKey(hKey);
  231. return std::make_pair(true, std::string(sub));
  232. }
  233. }
  234. else {
  235. }
  236. }
  237. RegCloseKey(hKey);
  238. }
  239. }
  240. }
  241. return std::make_pair(false, std::string());
  242. }
  243. void WinDNSHelper::setDNS(uint64_t nwid, const char* domain, const std::vector<InetAddress>& servers)
  244. {
  245. auto hasConfig = hasDNSConfig(nwid);
  246. std::stringstream ss;
  247. for (auto it = servers.begin(); it != servers.end(); ++it) {
  248. char ipaddr[256] = { 0 };
  249. ss << it->toIpString(ipaddr);
  250. if ((it + 1) != servers.end()) {
  251. ss << ";";
  252. }
  253. }
  254. std::string serverValue = ss.str();
  255. if (hasConfig.first) {
  256. // update existing config
  257. HKEY dnsKey;
  258. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, hasConfig.second.c_str(), 0, KEY_READ | KEY_WRITE, &dnsKey) == ERROR_SUCCESS) {
  259. auto retCode = RegSetKeyValueA(dnsKey, NULL, "GenericDNSServers", REG_SZ, serverValue.data(), (DWORD)serverValue.length());
  260. if (retCode != ERROR_SUCCESS) {
  261. fprintf(stderr, "Error writing dns servers: %d\n", retCode);
  262. }
  263. }
  264. } else {
  265. // add new config
  266. const char* baseKey = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
  267. GUID guid;
  268. CoCreateGuid(&guid);
  269. wchar_t guidTmp[128] = { 0 };
  270. char guidStr[128] = { 0 };
  271. StringFromGUID2(guid, guidTmp, 128);
  272. wcstombs(guidStr, guidTmp, 128);
  273. char fullKey[MAX_KEY_LENGTH] = { 0 };
  274. sprintf(fullKey, "%s\\%s", baseKey, guidStr);
  275. HKEY dnsKey;
  276. RegCreateKeyA(HKEY_LOCAL_MACHINE, fullKey, &dnsKey);
  277. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, fullKey, 0, KEY_READ | KEY_WRITE, &dnsKey) == ERROR_SUCCESS) {
  278. char nwString[32] = { 0 };
  279. sprintf(nwString, "%.16llx", nwid);
  280. RegSetKeyValueA(dnsKey, NULL, "Comment", REG_SZ, nwString, strlen(nwString));
  281. DWORD configOpts = 8;
  282. RegSetKeyValueA(dnsKey, NULL, "ConfigOptions", REG_DWORD, &configOpts, sizeof(DWORD));
  283. RegSetKeyValueA(dnsKey, NULL, "DisplayName", REG_SZ, "", 0);
  284. RegSetKeyValueA(dnsKey, NULL, "GenericDNSServers", REG_SZ, serverValue.data(), serverValue.length());
  285. RegSetKeyValueA(dnsKey, NULL, "IPSECCARestriction", REG_SZ, "", 0);
  286. std::string d = "." + std::string(domain);
  287. RegSetKeyValueA(dnsKey, NULL, "Name", REG_MULTI_SZ, d.data(), d.length());
  288. DWORD version = 2;
  289. RegSetKeyValueA(dnsKey, NULL, "Version", REG_DWORD, &version, sizeof(DWORD));
  290. }
  291. }
  292. }
  293. void WinDNSHelper::removeDNS(uint64_t nwid)
  294. {
  295. auto hasConfig = hasDNSConfig(nwid);
  296. if (hasConfig.first) {
  297. RegDelnode(HKEY_LOCAL_MACHINE, hasConfig.second.c_str());
  298. }
  299. }
  300. }