From 302ac8fefe67284bcdcc81c50fe3ed2419e96b78 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 5 Aug 2020 14:26:11 -0700 Subject: [PATCH] DNS config support on macOS --- make-mac.mk | 8 +++-- osdep/MacDNSHelper.hpp | 20 +++++++++++ osdep/MacDNSHelper.mm | 72 ++++++++++++++++++++++++++++++++++++++++ osdep/MacEthernetTap.cpp | 62 ++++++++++++++++++++++++++++++++++ osdep/MacEthernetTap.hpp | 3 ++ service/OneService.cpp | 3 ++ 6 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 osdep/MacDNSHelper.hpp create mode 100644 osdep/MacDNSHelper.mm diff --git a/make-mac.mk b/make-mac.mk index ada65ff77..acf17dd42 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -25,10 +25,10 @@ TIMESTAMP=$(shell date +"%Y%m%d%H%M") DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE) include objects.mk -ONE_OBJS+=osdep/MacEthernetTap.o osdep/MacKextEthernetTap.o ext/http-parser/http_parser.o +ONE_OBJS+=osdep/MacEthernetTap.o osdep/MacKextEthernetTap.o osdep/MacDNSHelper.o ext/http-parser/http_parser.o ifeq ($(ZT_CONTROLLER),1) - LIBS+=-L/usr/local/opt/libpq/lib -lpq ext/redis-plus-plus-1.1.1/install/macos/lib/libredis++.a ext/hiredis-0.14.1/lib/macos/libhiredis.a + LIBS+=-L/usr/local/opt/libpq/lib -lpq ext/redis-plus-plus-1.1.1/install/macos/lib/libredis++.a ext/hiredis-0.14.1/lib/macos/libhiredis.a -framework SystemConfiguration -framework CoreFoundation DEFS+=-DZT_CONTROLLER_USE_LIBPQ -DZT_CONTROLLER_USE_REDIS -DZT_CONTROLLER INCLUDES+=-I/usr/local/opt/libpq/include -Iext/hiredis-0.14.1/include/ -Iext/redis-plus-plus-1.1.1/install/macos/include/sw/ @@ -97,7 +97,11 @@ mac-agent: FORCE $(CC) -Ofast -o MacEthernetTapAgent osdep/MacEthernetTapAgent.c $(CODESIGN) -f -s $(CODESIGN_APP_CERT) MacEthernetTapAgent +osdep/MacDNSHelper.o: osdep/MacDNSHelper.mm + $(CXX) $(CXXFLAGS) -c osdep/MacDNSHelper.mm -o osdep/MacDNSHelper.o + one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent + $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS) # $(STRIP) zerotier-one ln -sf zerotier-one zerotier-idtool diff --git a/osdep/MacDNSHelper.hpp b/osdep/MacDNSHelper.hpp new file mode 100644 index 000000000..de45a8507 --- /dev/null +++ b/osdep/MacDNSHelper.hpp @@ -0,0 +1,20 @@ +#ifndef MAC_DNS_HELPER +#define MAC_DNS_HELPER + +#include +#include "../node/InetAddress.hpp" + +namespace ZeroTier { + +class MacDNSHelper +{ +public: + static void doTheThing(); + + static void setDNS(uint64_t nwid, const char *domain, const std::vector &servers); + static void removeDNS(uint64_t nwid); +}; + +} + +#endif diff --git a/osdep/MacDNSHelper.mm b/osdep/MacDNSHelper.mm new file mode 100644 index 000000000..75b5fd560 --- /dev/null +++ b/osdep/MacDNSHelper.mm @@ -0,0 +1,72 @@ +#include "MacDNSHelper.hpp" + +#include + +#include + +namespace ZeroTier { + +void MacDNSHelper::doTheThing() +{ + fprintf(stderr, "\n\nDOING THE THING!!\n\n"); +} + +void MacDNSHelper::setDNS(uint64_t nwid, const char *domain, const std::vector &servers) +{ + SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL); + + CFStringRef *s = new CFStringRef[4]; + for (unsigned int i = 0; i < servers.size(); ++i) { + char buf[64]; + ZeroTier::InetAddress a = servers[i]; + const char *ipStr = a.toIpString(buf); + s[i] = CFStringCreateWithCString(NULL, ipStr, kCFStringEncodingUTF8); + } + + CFArrayRef serverArray = CFArrayCreate(NULL, (const void**)s, servers.size(), &kCFTypeArrayCallBacks); + + CFStringRef keys[2]; + keys[0] = CFSTR("SupplementalMatchDomains"); + keys[1] = CFSTR("ServerAddresses"); + + CFStringRef cfdomain = CFStringCreateWithCString(NULL, domain, kCFStringEncodingUTF8); + CFArrayRef domainArray = CFArrayCreate(NULL, (const void**)&cfdomain, 1, &kCFTypeArrayCallBacks); + + CFTypeRef values[2]; + values[0] = domainArray; + values[1] = serverArray; + + CFDictionaryRef dict = CFDictionaryCreate(NULL, + (const void**)keys, (const void**)values, 2, &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFShow(dict); + + char buf[256] = {0}; + sprintf(buf, "State:/Network/Service/%.16llx/DNS", nwid); + CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); + CFArrayRef list = SCDynamicStoreCopyKeyList(ds, key); + + CFIndex i = 0, j = CFArrayGetCount(list); + bool ret = TRUE; + if (j <= 0) { + fprintf(stderr, "Key '%s' does not exist. Creating.\n", buf); + ret &= SCDynamicStoreAddValue(ds, key, dict); + } else { + fprintf(stderr, "Key '%s' already exists. Updating DNS config.\n", buf); + ret &= SCDynamicStoreSetValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i), dict); + } + if (ret) { + fprintf(stderr, "DNS written successfully\n"); + } else { + fprintf(stderr, "Error writing DNS configuration\n"); + } + + delete[] s; +} + +void MacDNSHelper::removeDNS(uint64_t nwid) +{ + +} + +} \ No newline at end of file diff --git a/osdep/MacEthernetTap.cpp b/osdep/MacEthernetTap.cpp index 3db0b3ff7..a48de6758 100644 --- a/osdep/MacEthernetTap.cpp +++ b/osdep/MacEthernetTap.cpp @@ -21,6 +21,7 @@ #include "OSUtils.hpp" #include "MacEthernetTap.hpp" #include "MacEthernetTapAgent.h" +#include "MacDNSHelper.hpp" #include #include @@ -54,6 +55,7 @@ #include #include #include +#include static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); @@ -454,7 +456,67 @@ void MacEthernetTap::threadMain() void MacEthernetTap::setDns(const char *domain, const std::vector &servers) { + MacDNSHelper::doTheThing(); + MacDNSHelper::setDNS(this->_nwid, domain, servers); + // _removeDnsConfig(domain, servers); + // _addDnsConfig(domain, servers); +} +void MacEthernetTap::_removeDnsConfig(const char *domain, const std::vector &servers) +{ + std::string tmpfile = std::tmpnam(nullptr); + std::FILE *remf = std::fopen(tmpfile.c_str(), "w"); + char buf[4096] = {0}; + sprintf(buf, "remove State:/Network/Service/%.16llx/DNS\n", _nwid); + std::fputs(buf, remf); + std::fflush(remf); + std::fclose(remf); + fprintf(stderr, "wrote tmpfile %s\n", tmpfile.c_str()); + long cpid = (long)vfork(); + if (cpid == 0) { + char cmd[1024] = {0}; + sprintf(cmd, "/usr/sbin/scutil < %s", tmpfile.c_str()); + ::execl("/bin/sh", "-c", cmd); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid, &exitcode,0); + if (exitcode) { + throw std::runtime_error("scutil dns config remove error"); + } + } +} + +void MacEthernetTap::_addDnsConfig(const char *domain, const std::vector &servers) +{ + std::string tmpfile = std::tmpnam(nullptr); + std::FILE *addf = std::fopen(tmpfile.c_str(), "w"); + char buf[4096] = {0}; + sprintf(buf, "d.init\n"); + sprintf(buf, "d.add ServerAddresses *"); + for (auto it = servers.begin(); it != servers.end(); ++it) { + char ipbuf[128] = {0}; + sprintf(buf, " %s", it->toIpString(buf)); + } + sprintf(buf, "\n"); + sprintf(buf, "d.add SupplementalMatchDomains * %s\n", domain); + sprintf(buf, "set State:/Network/Service/%.16llx/DNS", _nwid); + std::fputs(buf, addf); + std::fflush(addf); + std::fclose(addf); + fprintf(stderr, "wrote add tmpfile %s\n", tmpfile.c_str()); + long cpid = (long)vfork(); + if (cpid == 0) { + char cmd[1024]; + sprintf(cmd, "'/usr/bin/scutil < %s'", tmpfile.c_str()); + fprintf(stderr, "%s\n", cmd); + ::execl("/bin/sh", "-c", cmd); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid, &exitcode, 0); + if (exitcode) { + throw std::runtime_error("scutil dns config add error"); + } + } } } // namespace ZeroTier diff --git a/osdep/MacEthernetTap.hpp b/osdep/MacEthernetTap.hpp index 7945bb408..9ffbdee56 100644 --- a/osdep/MacEthernetTap.hpp +++ b/osdep/MacEthernetTap.hpp @@ -62,6 +62,9 @@ public: throw(); private: + void _removeDnsConfig(const char *domain, const std::vector &servers); + void _addDnsConfig(const char *domain, const std::vector &servers); + void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); void *_arg; uint64_t _nwid; diff --git a/service/OneService.cpp b/service/OneService.cpp index f48e90640..8b09d236a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2011,12 +2011,15 @@ public: for (int i = 0; i < n.config.dnsCount; ++i) { if (strlen(n.config.dns[i].domain) != 0) { fprintf(stderr, "Syncing DNS for domain: %s\n", n.config.dns[i].domain); + std::vector servers; for (int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) { InetAddress a(n.config.dns[i].server_addr[j]); if (a.isV4() || a.isV6()) { fprintf(stderr, "\t Server %d: %s\n", j+1, a.toIpString(buf)); + servers.push_back(a); } } + n.tap->setDns(n.config.dns[i].domain, servers); } } }