MacDNSHelper.mm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  4. *
  5. * (c) ZeroTier, Inc.
  6. * https://www.zerotier.com/
  7. */
  8. #include "MacDNSHelper.hpp"
  9. #include <stdio.h>
  10. #include <SystemConfiguration/SystemConfiguration.h>
  11. namespace ZeroTier {
  12. static void printKeys (const void* key, const void* value, void* context) {
  13. CFShow(key);
  14. CFShow(value);
  15. }
  16. void MacDNSHelper::setDNS(uint64_t nwid, const char *domain, const std::vector<InetAddress> &servers)
  17. {
  18. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  19. CFStringRef *s = new CFStringRef[4];
  20. for (unsigned int i = 0; i < servers.size(); ++i) {
  21. char buf[64];
  22. ZeroTier::InetAddress a = servers[i];
  23. const char *ipStr = a.toIpString(buf);
  24. s[i] = CFStringCreateWithCString(NULL, ipStr, kCFStringEncodingUTF8);
  25. }
  26. CFArrayRef serverArray = CFArrayCreate(NULL, (const void**)s, servers.size(), &kCFTypeArrayCallBacks);
  27. CFStringRef keys[3];
  28. keys[0] = CFSTR("SupplementalMatchDomains");
  29. keys[1] = CFSTR("ServerAddresses");
  30. keys[2] = CFSTR("SearchDomains");
  31. CFStringRef cfdomain = CFStringCreateWithCString(NULL, domain, kCFStringEncodingUTF8);
  32. CFStringRef cfdomain2 = CFStringCreateWithCString(NULL, domain, kCFStringEncodingUTF8);
  33. CFArrayRef domainArray = CFArrayCreate(NULL, (const void**)&cfdomain, 1, &kCFTypeArrayCallBacks);
  34. CFArrayRef domainArray2 = CFArrayCreate(NULL, (const void**)&cfdomain2, 1, &kCFTypeArrayCallBacks);
  35. CFTypeRef values[3];
  36. values[0] = domainArray;
  37. values[1] = serverArray;
  38. values[2] = domainArray2;
  39. CFDictionaryRef dict = CFDictionaryCreate(NULL,
  40. (const void**)keys, (const void**)values, 3, &kCFCopyStringDictionaryKeyCallBacks,
  41. &kCFTypeDictionaryValueCallBacks);
  42. char buf[256] = {0};
  43. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/DNS", nwid);
  44. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  45. CFArrayRef list = SCDynamicStoreCopyKeyList(ds, key);
  46. CFIndex i = 0, j = CFArrayGetCount(list);
  47. bool dnsServersChanged = true;
  48. CFPropertyListRef oldDNSServers = NULL;
  49. if (j > 0) {
  50. oldDNSServers = SCDynamicStoreCopyValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i));
  51. dnsServersChanged = !CFEqual(oldDNSServers,dict);
  52. }
  53. if (dnsServersChanged) {
  54. bool ret = TRUE;
  55. if (j <= 0) {
  56. ret &= SCDynamicStoreAddValue(ds, key, dict);
  57. } else {
  58. ret &= SCDynamicStoreSetValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i), dict);
  59. }
  60. if (!ret) {
  61. fprintf(stderr, "Error writing DNS configuration\n");
  62. }
  63. }
  64. if (oldDNSServers != NULL) {
  65. CFRelease(oldDNSServers);
  66. }
  67. CFRelease(list);
  68. CFRelease(key);
  69. CFRelease(dict);
  70. CFRelease(domainArray);
  71. CFRelease(domainArray2);
  72. CFRelease(cfdomain);
  73. CFRelease(cfdomain2);
  74. CFRelease(serverArray);
  75. for (int i = 0; i < servers.size(); ++i) {
  76. CFRelease(s[i]);
  77. }
  78. delete[] s;
  79. CFRelease(ds);
  80. }
  81. void MacDNSHelper::removeDNS(uint64_t nwid)
  82. {
  83. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  84. char buf[256] = {0};
  85. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/DNS", nwid);
  86. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  87. SCDynamicStoreRemoveValue(ds, key);
  88. CFRelease(key);
  89. CFRelease(ds);
  90. }
  91. // Make macOS believe we do in fact have ipv6 connectivity and that it should resolve dns names
  92. // over ipv6 if we ask for them.
  93. // Originally I planned to put all the v6 ip addresses from the network into the config.
  94. // But only the link local address is necessary and sufficient. Added other v6 addresses
  95. // doesn't do anything.
  96. //
  97. // As of Monterey we need IPv4 set up too.
  98. bool MacDNSHelper::addIps4(uint64_t nwid, const MAC mac, const char *dev, const std::vector<InetAddress>& addrs)
  99. {
  100. const char* ipStr = {0};
  101. char buf2[256] = {0};
  102. bool hasV4 = false;
  103. for (unsigned int i = 0; i < addrs.size(); ++i) {
  104. if (addrs[i].isV4()) {
  105. hasV4 = true;
  106. ipStr = addrs[i].toIpString(buf2);
  107. break;
  108. }
  109. }
  110. if (!hasV4) {
  111. MacDNSHelper::removeIps4(nwid);
  112. return true;
  113. }
  114. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  115. char buf[256] = { 0 };
  116. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/IPv4", nwid);
  117. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  118. CFStringRef cfaddr = CFStringCreateWithCString(NULL, ipStr, kCFStringEncodingUTF8);
  119. CFArrayRef addrArray = CFArrayCreate(NULL, (const void**)&cfaddr, 1, &kCFTypeArrayCallBacks);
  120. CFStringRef cfdev = CFStringCreateWithCString(NULL, dev, kCFStringEncodingUTF8);
  121. CFStringRef cfserver = CFStringCreateWithCString(NULL, "127.0.0.1", kCFStringEncodingUTF8);
  122. // using the ip from the zerotier network breaks routing on the mac
  123. CFStringRef cfrouter = CFStringCreateWithCString(NULL, "127.0.0.1", kCFStringEncodingUTF8);
  124. const int SIZE = 4;
  125. CFStringRef keys[SIZE];
  126. keys[0] = CFSTR("Addresses");
  127. keys[1] = CFSTR("InterfaceName");
  128. keys[2] = CFSTR("ServerAddress");
  129. keys[3] = CFSTR("Router");
  130. CFTypeRef values[SIZE];
  131. values[0] = addrArray;
  132. values[1] = cfdev;
  133. values[2] = cfserver;
  134. values[3] = cfrouter;
  135. CFDictionaryRef dict = CFDictionaryCreate(NULL,
  136. (const void**)keys, (const void**)values, SIZE, &kCFCopyStringDictionaryKeyCallBacks,
  137. &kCFTypeDictionaryValueCallBacks);
  138. // CFDictionaryApplyFunction(dict, printKeys, NULL);
  139. CFArrayRef list = SCDynamicStoreCopyKeyList(ds, key);
  140. CFIndex i = 0, j = CFArrayGetCount(list);
  141. bool addrsChanged = true;
  142. CFPropertyListRef oldAddrs = NULL;
  143. bool ret = TRUE;
  144. if (j > 0) {
  145. oldAddrs = SCDynamicStoreCopyValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i));
  146. addrsChanged = !CFEqual(oldAddrs,dict);
  147. }
  148. if (addrsChanged) {
  149. if (j <= 0) {
  150. ret &= SCDynamicStoreAddValue(ds, key, dict);
  151. } else {
  152. ret &= SCDynamicStoreSetValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i), dict);
  153. }
  154. if (!ret) {
  155. fprintf(stderr, "Error writing IPv6 configuration\n");
  156. }
  157. }
  158. if (oldAddrs != NULL) {
  159. CFRelease(oldAddrs);
  160. }
  161. CFRelease(cfaddr);
  162. CFRelease(addrArray);
  163. CFRelease(cfdev);
  164. CFRelease(cfserver);
  165. CFRelease(cfrouter);
  166. CFRelease(ds);
  167. CFRelease(key);
  168. // for (unsigned int i = 0; i < SIZE; ++i) {
  169. // values[i] = NULL;
  170. // }
  171. CFRelease(list);
  172. CFRelease(dict);
  173. return ret;
  174. }
  175. bool MacDNSHelper::addIps6(uint64_t nwid, const MAC mac, const char *dev, const std::vector<InetAddress>& addrs)
  176. {
  177. bool hasV6 = false;
  178. for (unsigned int i = 0; i < addrs.size(); ++i) {
  179. if (addrs[i].isV6()) {
  180. hasV6 = true;
  181. break;
  182. }
  183. }
  184. if (!hasV6) {
  185. MacDNSHelper::removeIps6(nwid);
  186. return true;
  187. }
  188. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  189. char buf[256] = { 0 };
  190. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/IPv6", nwid);
  191. InetAddress ll = InetAddress::makeIpv6LinkLocal(mac);
  192. char buf2[256] = {0};
  193. const char* llStr = ll.toIpString(buf2);
  194. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  195. CFStringRef cfaddr = CFStringCreateWithCString(NULL, llStr, kCFStringEncodingUTF8);
  196. CFStringRef cfprefixes = CFStringCreateWithCString(NULL, "64", kCFStringEncodingUTF8);
  197. CFStringRef cfdestaddrs = CFStringCreateWithCString(NULL, "::ffff:ffff:ffff:ffff:0:0", kCFStringEncodingUTF8);
  198. CFStringRef cfflags = CFStringCreateWithCString(NULL, "0", kCFStringEncodingUTF8);
  199. CFArrayRef addrArray = CFArrayCreate(NULL, (const void**)&cfaddr, 1, &kCFTypeArrayCallBacks);
  200. CFArrayRef prefixArray = CFArrayCreate(NULL, (const void**)&cfprefixes, 1, &kCFTypeArrayCallBacks);
  201. CFArrayRef destArray = CFArrayCreate(NULL, (const void**)&cfdestaddrs, 1, &kCFTypeArrayCallBacks);
  202. CFArrayRef flagsArray = CFArrayCreate(NULL, (const void**)&cfflags, 1, &kCFTypeArrayCallBacks);
  203. CFStringRef cfdev = CFStringCreateWithCString(NULL, dev, kCFStringEncodingUTF8);
  204. const int SIZE = 5;
  205. CFStringRef keys[SIZE];
  206. keys[0] = CFSTR("Addresses");
  207. keys[1] = CFSTR("DestAddresses");
  208. keys[2] = CFSTR("Flags");
  209. keys[3] = CFSTR("InterfaceName");
  210. keys[4] = CFSTR("PrefixLength");
  211. CFTypeRef values[SIZE];
  212. values[0] = addrArray;
  213. values[1] = destArray;
  214. values[2] = flagsArray;
  215. // values[3] = devArray;
  216. values[3] = cfdev;
  217. values[4] = prefixArray;
  218. CFDictionaryRef dict = CFDictionaryCreate(NULL,
  219. (const void**)keys, (const void**)values, SIZE, &kCFCopyStringDictionaryKeyCallBacks,
  220. &kCFTypeDictionaryValueCallBacks);
  221. // CFDictionaryApplyFunction(dict, printKeys, NULL);
  222. CFArrayRef list = SCDynamicStoreCopyKeyList(ds, key);
  223. CFIndex i = 0, j = CFArrayGetCount(list);
  224. bool addrsChanged = true;
  225. CFPropertyListRef oldAddrs = NULL;
  226. bool ret = TRUE;
  227. if (j > 0) {
  228. oldAddrs = SCDynamicStoreCopyValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i));
  229. addrsChanged = !CFEqual(oldAddrs,dict);
  230. }
  231. if (addrsChanged) {
  232. if (j <= 0) {
  233. ret &= SCDynamicStoreAddValue(ds, key, dict);
  234. } else {
  235. ret &= SCDynamicStoreSetValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i), dict);
  236. }
  237. if (!ret) {
  238. fprintf(stderr, "Error writing IPv6 configuration\n");
  239. }
  240. }
  241. if (oldAddrs != NULL) {
  242. CFRelease(oldAddrs);
  243. }
  244. CFRelease(cfaddr);
  245. CFRelease(cfprefixes);
  246. CFRelease(cfdestaddrs);
  247. CFRelease(cfflags);
  248. CFRelease(addrArray);
  249. CFRelease(prefixArray);
  250. CFRelease(destArray);
  251. CFRelease(flagsArray);
  252. CFRelease(cfdev);
  253. CFRelease(ds);
  254. CFRelease(key);
  255. // for (unsigned int i = 0; i < SIZE; ++i) {
  256. // values[i] = NULL;
  257. // }
  258. CFRelease(list);
  259. CFRelease(dict);
  260. return ret;
  261. }
  262. bool MacDNSHelper::removeIps6(uint64_t nwid)
  263. {
  264. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  265. char buf[256] = {0};
  266. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/IPv6", nwid);
  267. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  268. bool res = SCDynamicStoreRemoveValue(ds, key);
  269. CFRelease(key);
  270. CFRelease(ds);
  271. return res;
  272. }
  273. bool MacDNSHelper::removeIps4(uint64_t nwid)
  274. {
  275. SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
  276. char buf[256] = {0};
  277. snprintf(buf, sizeof(buf), "State:/Network/Service/%.16llx/IPv4", nwid);
  278. CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
  279. bool res = SCDynamicStoreRemoveValue(ds, key);
  280. CFRelease(key);
  281. CFRelease(ds);
  282. return res;
  283. }
  284. }