Browse Source

Merge branch 'dns' into dev

Grant Limberg 5 years ago
parent
commit
8d0a3563e4

+ 1 - 0
.gitignore

@@ -121,3 +121,4 @@ __pycache__
 attic/world/*.c25519
 attic/world/*.c25519
 attic/world/mkworld
 attic/world/mkworld
 workspace/
 workspace/
+workspace2/

+ 2 - 0
controller/DB.cpp

@@ -48,6 +48,8 @@ void DB::initNetwork(nlohmann::json &network)
 			{ "type","ACTION_ACCEPT" }
 			{ "type","ACTION_ACCEPT" }
 		}};
 		}};
 	}
 	}
+	if (!network.count("dns")) network["dns"] = nlohmann::json::array();
+
 	network["objtype"] = "network";
 	network["objtype"] = "network";
 }
 }
 
 

+ 41 - 1
controller/EmbeddedNetworkController.cpp

@@ -585,7 +585,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
 							responseBody.reserve((members.size() + 2) * 32);
 							responseBody.reserve((members.size() + 2) * 32);
 							std::string mid;
 							std::string mid;
 							for(auto member=members.begin();member!=members.end();++member) {
 							for(auto member=members.begin();member!=members.end();++member) {
-								mid = (*member)["id"];
+								mid = OSUtils::jsonString((*member)["id"], "");
 								char tmp[128];
 								char tmp[128];
 								OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s\"%s\":%llu",(responseBody.length() > 1) ? "," : "",mid.c_str(),(unsigned long long)OSUtils::jsonInt((*member)["revision"],0));
 								OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s\"%s\":%llu",(responseBody.length() > 1) ? "," : "",mid.c_str(),(unsigned long long)OSUtils::jsonInt((*member)["revision"],0));
 								responseBody.append(tmp);
 								responseBody.append(tmp);
@@ -1029,6 +1029,30 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 						}
 						}
 					}
 					}
 
 
+					if (b.count("dns")) {
+						json &dns = b["dns"];
+						if (dns.is_array()) {
+							json nda = json::array();
+							for(unsigned int i=0;i<dns.size();++i) {
+								json &d = dns[i];
+								if (d.is_object()) {
+									json nd = json::object();
+									nd["domain"] = d["domain"];
+									json &srv = d["servers"];
+									if (srv.is_array()) {
+										json ns = json::array();
+										for(unsigned int j=0;j<srv.size();++j) {
+											ns.push_back(srv[i]);
+										}
+										nd["servers"] = ns;
+									}
+									nda.push_back(nd);
+								}
+							}
+							network["dns"] = nda;
+						}
+					}
+
 				} catch ( ... ) {
 				} catch ( ... ) {
 					responseBody = "{ \"message\": \"exception occurred while parsing body variables\" }";
 					responseBody = "{ \"message\": \"exception occurred while parsing body variables\" }";
 					responseContentType = "application/json";
 					responseContentType = "application/json";
@@ -1366,6 +1390,7 @@ void EmbeddedNetworkController::_request(
 	nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
 	nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
 	nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
 	nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
 
 
+	
 	std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
 	std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
 	if (rtt.length() == 10) {
 	if (rtt.length() == 10) {
 		nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
 		nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
@@ -1392,6 +1417,7 @@ void EmbeddedNetworkController::_request(
 	json &tags = network["tags"];
 	json &tags = network["tags"];
 	json &memberCapabilities = member["capabilities"];
 	json &memberCapabilities = member["capabilities"];
 	json &memberTags = member["tags"];
 	json &memberTags = member["tags"];
+	json &dns = network["dns"];
 
 
 	if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) {
 	if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) {
 		// Old versions with no rules engine support get an allow everything rule.
 		// Old versions with no rules engine support get an allow everything rule.
@@ -1684,6 +1710,20 @@ void EmbeddedNetworkController::_request(
 			}
 			}
 		}
 		}
 	}
 	}
+	
+	if(dns.is_object()) {
+		std::string domain = OSUtils::jsonString(dns["domain"],"");
+		memcpy(nc->dns.domain, domain.c_str(), domain.size());
+		json &addrArray = dns["servers"];
+		if (addrArray.is_array()) {
+			for(unsigned int j = 0; j < addrArray.size() && j < ZT_MAX_DNS_SERVERS; ++j) {
+				json &addr = addrArray[j];
+				nc->dns.server_addr[j] = InetAddress(OSUtils::jsonString(addr,"").c_str());
+			}
+		}
+	} else {
+		dns = json::object();
+	}
 
 
 	// Issue a certificate of ownership for all static IPs
 	// Issue a certificate of ownership for all static IPs
 	if (nc->staticIpCount) {
 	if (nc->staticIpCount) {

+ 70 - 0
controller/PostgreSQL.cpp

@@ -430,6 +430,44 @@ void PostgreSQL::initializeNetworks(PGconn *conn)
 				config["routes"].push_back(route);
 				config["routes"].push_back(route);
 			}
 			}
 
 
+			r2 = PQexecParams(conn,
+				"SELECT domain, servers FROM ztc_network_dns WHERE network_id = $1",
+				1,
+				NULL,
+				nwidparam,
+				NULL,
+				NULL,
+				0);
+			
+			if (PQresultStatus(r2) != PGRES_TUPLES_OK) {
+				fprintf(stderr, "ERROR: Error retrieving DNS settings for network: %s\n", PQresultErrorMessage(r2));
+				PQclear(r2);
+				PQclear(res);
+				exit(1);
+			}
+
+			n = PQntuples(r2);
+			if (n > 1) {
+				fprintf(stderr, "ERROR: invalid number of DNS configurations for network %s.  Must be 0 or 1\n", nwid.c_str());
+			} else if (n == 1) {
+				json obj;
+				std::string domain = PQgetvalue(r2, 0, 0);
+				std::string serverList = PQgetvalue(r2, 0, 1);
+				auto servers = json::array();
+				if (serverList.rfind("{",0) != std::string::npos) {
+					serverList = serverList.substr(1, serverList.size()-2);
+					std::stringstream ss(serverList);
+					while(ss.good()) {
+						std::string server;
+						std::getline(ss, server, ',');
+						servers.push_back(server);
+					}
+				}
+				obj["domain"] = domain;
+				obj["servers"] = servers;
+				config["dns"] = obj;
+			}
+
 			PQclear(r2);
 			PQclear(r2);
 
 
 			_networkChanged(empty, config, false);
 			_networkChanged(empty, config, false);
@@ -1423,6 +1461,38 @@ void PostgreSQL::commitThread()
 						config = nullptr;
 						config = nullptr;
 						continue;
 						continue;
 					}
 					}
+					auto dns = (*config)["dns"];
+					std::string domain = dns["domain"];
+					std::stringstream servers;
+					servers << "{";
+					for (auto j = dns["servers"].begin(); j < dns["servers"].end(); ++j) {
+						servers << *j;
+						if ( (j+1) != dns["servers"].end()) {
+							servers << ",";
+						}
+					}
+					servers << "}";
+
+					const char *p[3] = {
+						id.c_str(),
+						domain.c_str(),
+						servers.str().c_str()
+					};
+
+					res = PQexecParams(conn, "INSERT INTO ztc_network_dns (network_id, domain, servers) VALUES ($1, $2, $3) ON CONFLICT (network_id) DO UPDATE SET domain = EXCLUDED.domain, servers = EXCLUDED.servers",
+						3,
+						NULL,
+						p,
+						NULL,
+						NULL,
+						0);
+					if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+						fprintf(stderr, "ERROR: Error updating DNS: %s\n", PQresultErrorMessage(res));
+						PQclear(res);
+						err = true;
+						break;
+					}
+					PQclear(res);
 
 
 					res = PQexec(conn, "COMMIT");
 					res = PQexec(conn, "COMMIT");
 					if (PQresultStatus(res) != PGRES_COMMAND_OK) {
 					if (PQresultStatus(res) != PGRES_COMMAND_OK) {

+ 19 - 0
include/ZeroTierOne.h

@@ -195,6 +195,11 @@ extern "C" {
  */
  */
 #define ZT_PATH_LINK_QUALITY_MAX 0xff
 #define ZT_PATH_LINK_QUALITY_MAX 0xff
 
 
+/**
+ * Maximum number of DNS servers per domain
+ */
+#define ZT_MAX_DNS_SERVERS 4
+
 /**
 /**
  * Packet characteristics flag: packet direction, 1 if inbound 0 if outbound
  * Packet characteristics flag: packet direction, 1 if inbound 0 if outbound
  */
  */
@@ -1106,6 +1111,15 @@ typedef struct
 	uint16_t metric;
 	uint16_t metric;
 } ZT_VirtualNetworkRoute;
 } ZT_VirtualNetworkRoute;
 
 
+/**
+ * DNS configuration to be pushed on a virtual network
+ */
+typedef struct
+{
+	char domain[128];
+	struct sockaddr_storage server_addr[ZT_MAX_DNS_SERVERS];
+} ZT_VirtualNetworkDNS;
+
 /**
 /**
  * An Ethernet multicast group
  * An Ethernet multicast group
  */
  */
@@ -1320,6 +1334,11 @@ typedef struct
 		uint64_t mac; /* MAC in lower 48 bits */
 		uint64_t mac; /* MAC in lower 48 bits */
 		uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP groups */
 		uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP groups */
 	} multicastSubscriptions[ZT_MAX_MULTICAST_SUBSCRIPTIONS];
 	} multicastSubscriptions[ZT_MAX_MULTICAST_SUBSCRIPTIONS];
+	
+	/**
+	 * Network specific DNS configuration
+	 */
+	ZT_VirtualNetworkDNS dns;
 } ZT_VirtualNetworkConfig;
 } ZT_VirtualNetworkConfig;
 
 
 /**
 /**

+ 6 - 2
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)
 DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE)
 
 
 include objects.mk
 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)
 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 
 	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/
 	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
 	$(CC) -Ofast -o MacEthernetTapAgent osdep/MacEthernetTapAgent.c
 	$(CODESIGN) -f -s $(CODESIGN_APP_CERT) MacEthernetTapAgent
 	$(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
 one:	$(CORE_OBJS) $(ONE_OBJS) one.o mac-agent
+	 
 	$(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
 	$(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
 	# $(STRIP) zerotier-one
 	# $(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
 	ln -sf zerotier-one zerotier-idtool

+ 56 - 0
node/DNS.hpp

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c)2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2023-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#ifndef ZT_DNS_HPP
+#define ZT_DNS_HPP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Buffer.hpp"
+#include "InetAddress.hpp"
+#include "../include/ZeroTierOne.h"
+
+namespace ZeroTier {
+
+/**
+ * DNS data serealization methods
+ */
+class DNS {
+public:
+    template<unsigned int C>
+    static inline void serializeDNS(Buffer<C> &b, const ZT_VirtualNetworkDNS *dns)
+    {
+        b.append(dns->domain, 128);
+        for(unsigned int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) {
+            InetAddress tmp(dns->server_addr[j]);
+            tmp.serialize(b);
+        }
+    }
+
+    template<unsigned int C>
+    static inline void deserializeDNS(const Buffer<C> &b, unsigned int &p, ZT_VirtualNetworkDNS *dns)
+    {
+        char *d = (char*)b.data()+p;
+        memset(dns, 0, sizeof(ZT_VirtualNetworkDNS));
+        memcpy(dns->domain, d, 128);
+        p += 128;
+        for (unsigned int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) {
+            p += reinterpret_cast<InetAddress *>(&(dns->server_addr[j]))->deserialize(b, p);
+        }
+    }
+};
+
+}
+
+#endif // ZT_DNS_HPP

+ 3 - 0
node/Network.cpp

@@ -586,6 +586,7 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u
 
 
 	if (!_portInitialized) {
 	if (!_portInitialized) {
 		ZT_VirtualNetworkConfig ctmp;
 		ZT_VirtualNetworkConfig ctmp;
+		memset(&ctmp, 0, sizeof(ZT_VirtualNetworkConfig));
 		_externalConfig(&ctmp);
 		_externalConfig(&ctmp);
 		_portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
 		_portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
 		_portInitialized = true;
 		_portInitialized = true;
@@ -1426,6 +1427,8 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
 		ec->multicastSubscriptions[i].mac = _myMulticastGroups[i].mac().toInt();
 		ec->multicastSubscriptions[i].mac = _myMulticastGroups[i].mac().toInt();
 		ec->multicastSubscriptions[i].adi = _myMulticastGroups[i].adi();
 		ec->multicastSubscriptions[i].adi = _myMulticastGroups[i].adi();
 	}
 	}
+
+	memcpy(&ec->dns, &_config.dns, sizeof(ZT_VirtualNetworkDNS));
 }
 }
 
 
 void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
 void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)

+ 11 - 0
node/NetworkConfig.cpp

@@ -176,6 +176,12 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
 			}
 			}
 		}
 		}
 
 
+		tmp->clear();
+		DNS::serializeDNS(*tmp, &dns);
+		if (tmp->size()) {
+			if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_DNS,*tmp)) return false;
+		}
+
 		delete tmp;
 		delete tmp;
 	} catch ( ... ) {
 	} catch ( ... ) {
 		delete tmp;
 		delete tmp;
@@ -354,6 +360,11 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
 				unsigned int p = 0;
 				unsigned int p = 0;
 				Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
 				Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
 			}
 			}
+
+			if (d.get(ZT_NETWORKCONFIG_DICT_KEY_DNS, *tmp)) {
+				unsigned int p = 0;
+				DNS::deserializeDNS(*tmp, p, &dns);
+			}
 		}
 		}
 
 
 		//printf("~~~\n%s\n~~~\n",d.data());
 		//printf("~~~\n%s\n~~~\n",d.data());

+ 16 - 1
node/NetworkConfig.hpp

@@ -26,6 +26,7 @@
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "Buffer.hpp"
 #include "Buffer.hpp"
+#include "DNS.hpp"
 #include "InetAddress.hpp"
 #include "InetAddress.hpp"
 #include "MulticastGroup.hpp"
 #include "MulticastGroup.hpp"
 #include "Address.hpp"
 #include "Address.hpp"
@@ -175,6 +176,8 @@ namespace ZeroTier {
 #define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
 #define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
 // tags (binary blobs)
 // tags (binary blobs)
 #define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
 #define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
+// dns (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_DNS "DNS"
 
 
 // Legacy fields -- these are obsoleted but are included when older clients query
 // Legacy fields -- these are obsoleted but are included when older clients query
 
 
@@ -229,13 +232,15 @@ public:
 		capabilities(),
 		capabilities(),
 		tags(),
 		tags(),
 		certificatesOfOwnership(),
 		certificatesOfOwnership(),
-		type(ZT_NETWORK_TYPE_PRIVATE)
+		type(ZT_NETWORK_TYPE_PRIVATE),
+		dnsCount(0)
 	{
 	{
 		name[0] = 0;
 		name[0] = 0;
 		memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS);
 		memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS);
 		memset(routes, 0, sizeof(ZT_VirtualNetworkRoute)*ZT_MAX_NETWORK_ROUTES);
 		memset(routes, 0, sizeof(ZT_VirtualNetworkRoute)*ZT_MAX_NETWORK_ROUTES);
 		memset(staticIps, 0, sizeof(InetAddress)*ZT_MAX_ZT_ASSIGNED_ADDRESSES);
 		memset(staticIps, 0, sizeof(InetAddress)*ZT_MAX_ZT_ASSIGNED_ADDRESSES);
 		memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES);
 		memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES);
+		memset(&dns, 0, sizeof(ZT_VirtualNetworkDNS));
 	}
 	}
 
 
 	/**
 	/**
@@ -589,6 +594,16 @@ public:
 	 * Certificate of membership (for private networks)
 	 * Certificate of membership (for private networks)
 	 */
 	 */
 	CertificateOfMembership com;
 	CertificateOfMembership com;
+
+	/**
+	 * Number of ZT-pushed DNS configurations
+	 */
+	unsigned int dnsCount;
+
+	/**
+	 * ZT pushed DNS configuration
+	 */
+	ZT_VirtualNetworkDNS dns;
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 1 - 0
osdep/BSDEthernetTap.hpp

@@ -54,6 +54,7 @@ public:
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void setMtu(unsigned int mtu);
 	virtual void setMtu(unsigned int mtu);
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers) {}
 
 
 	void threadMain()
 	void threadMain()
 		throw();
 		throw();

+ 1 - 0
osdep/EthernetTap.hpp

@@ -53,6 +53,7 @@ public:
 	virtual void setFriendlyName(const char *friendlyName) = 0;
 	virtual void setFriendlyName(const char *friendlyName) = 0;
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) = 0;
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) = 0;
 	virtual void setMtu(unsigned int mtu) = 0;
 	virtual void setMtu(unsigned int mtu) = 0;
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers) = 0;
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 1 - 0
osdep/LinuxEthernetTap.hpp

@@ -54,6 +54,7 @@ public:
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void setMtu(unsigned int mtu);
 	virtual void setMtu(unsigned int mtu);
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers) {}
 
 
 	void threadMain()
 	void threadMain()
 		throw();
 		throw();

+ 18 - 0
osdep/MacDNSHelper.hpp

@@ -0,0 +1,18 @@
+#ifndef MAC_DNS_HELPER
+#define MAC_DNS_HELPER
+
+#include <vector>
+#include "../node/InetAddress.hpp"
+
+namespace ZeroTier {
+
+class MacDNSHelper
+{
+public:
+    static void setDNS(uint64_t nwid, const char *domain, const std::vector<InetAddress> &servers);
+    static void removeDNS(uint64_t nwid);
+};
+
+}
+
+#endif

+ 79 - 0
osdep/MacDNSHelper.mm

@@ -0,0 +1,79 @@
+#include "MacDNSHelper.hpp"
+
+#include <stdio.h>
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+namespace ZeroTier {
+
+void MacDNSHelper::setDNS(uint64_t nwid, const char *domain, const std::vector<InetAddress> &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);
+
+    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) {
+        ret &= SCDynamicStoreAddValue(ds, key, dict);
+    } else {
+        ret &= SCDynamicStoreSetValue(ds, (CFStringRef)CFArrayGetValueAtIndex(list, i), dict);
+    }
+    if (!ret) {
+        fprintf(stderr, "Error writing DNS configuration\n");
+    }
+
+    CFRelease(list);
+    CFRelease(key);
+    CFRelease(dict);
+    CFRelease(domainArray);
+    CFRelease(cfdomain);
+    CFRelease(serverArray);
+    for (int i = 0; i < servers.size(); ++i) {
+        CFRelease(s[i]);
+    }
+    delete[] s;
+    CFRelease(ds);
+}
+    
+void MacDNSHelper::removeDNS(uint64_t nwid) 
+{
+    SCDynamicStoreRef ds = SCDynamicStoreCreate(NULL, CFSTR("zerotier"), NULL, NULL);
+
+    char buf[256] = {0};
+    sprintf(buf, "State:/Network/Service/%.16llx/DNS", nwid);
+    CFStringRef key = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
+    SCDynamicStoreRemoveValue(ds, key);
+    CFRelease(key);
+    CFRelease(ds);
+}
+
+}

+ 9 - 0
osdep/MacEthernetTap.cpp

@@ -21,6 +21,7 @@
 #include "OSUtils.hpp"
 #include "OSUtils.hpp"
 #include "MacEthernetTap.hpp"
 #include "MacEthernetTap.hpp"
 #include "MacEthernetTapAgent.h"
 #include "MacEthernetTapAgent.h"
+#include "MacDNSHelper.hpp"
 
 
 #include <stdint.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdio.h>
@@ -54,6 +55,7 @@
 #include <map>
 #include <map>
 #include <set>
 #include <set>
 #include <algorithm>
 #include <algorithm>
+#include <filesystem>
 
 
 static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
 static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
 
 
@@ -200,6 +202,8 @@ MacEthernetTap::MacEthernetTap(
 
 
 MacEthernetTap::~MacEthernetTap()
 MacEthernetTap::~MacEthernetTap()
 {
 {
+	MacDNSHelper::removeDNS(_nwid);
+	
 	Mutex::Lock _gl(globalTapCreateLock);
 	Mutex::Lock _gl(globalTapCreateLock);
 	::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
 	::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
 	Thread::join(_thread);
 	Thread::join(_thread);
@@ -452,6 +456,11 @@ void MacEthernetTap::threadMain()
 	}
 	}
 }
 }
 
 
+void MacEthernetTap::setDns(const char *domain, const std::vector<InetAddress> &servers)
+{
+	MacDNSHelper::setDNS(this->_nwid, domain, servers);
+}
+
 } // namespace ZeroTier
 } // namespace ZeroTier
 
 
 #endif // __APPLE__
 #endif // __APPLE__

+ 1 - 0
osdep/MacEthernetTap.hpp

@@ -56,6 +56,7 @@ public:
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void setMtu(unsigned int mtu);
 	virtual void setMtu(unsigned int mtu);
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers);
 
 
 	void threadMain()
 	void threadMain()
 		throw();
 		throw();

+ 9 - 0
osdep/MacKextEthernetTap.cpp

@@ -43,6 +43,8 @@
 #include <netinet/in_var.h>
 #include <netinet/in_var.h>
 #include <netinet/icmp6.h>
 #include <netinet/icmp6.h>
 
 
+#include "MacDNSHelper.hpp"
+
 // OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
 // OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
 struct prf_ra {
 struct prf_ra {
 	u_char onlink : 1;
 	u_char onlink : 1;
@@ -441,6 +443,8 @@ MacKextEthernetTap::MacKextEthernetTap(
 
 
 MacKextEthernetTap::~MacKextEthernetTap()
 MacKextEthernetTap::~MacKextEthernetTap()
 {
 {
+	MacDNSHelper::removeDNS(_nwid);
+
 	::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
 	::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
 	Thread::join(_thread);
 	Thread::join(_thread);
 
 
@@ -687,4 +691,9 @@ void MacKextEthernetTap::threadMain()
 	}
 	}
 }
 }
 
 
+void MacKextEthernetTap::setDns(const char *domain, const std::vector<InetAddress> &servers)
+{
+	MacDNSHelper::setDNS(_nwid, domain, servers);
+}
+
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 2 - 0
osdep/MacKextEthernetTap.hpp

@@ -56,6 +56,8 @@ public:
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void setMtu(unsigned int mtu);
 	virtual void setMtu(unsigned int mtu);
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers);
+
 
 
 	void threadMain()
 	void threadMain()
 		throw();
 		throw();

+ 1 - 0
osdep/NetBSDEthernetTap.hpp

@@ -53,6 +53,7 @@ public:
 	virtual std::string deviceName() const;
 	virtual std::string deviceName() const;
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+	virtual void setDns(const char *domain, const std::vector<InetAddress> &servers) {}
 
 
 	void threadMain()
 	void threadMain()
 		throw();
 		throw();

+ 353 - 0
osdep/WinDNSHelper.cpp

@@ -0,0 +1,353 @@
+#include "WinDNSHelper.hpp"
+
+#include <comdef.h>
+#include <WbemIdl.h>
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <strsafe.h>
+
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_NAME 16383
+
+namespace ZeroTier
+{
+
+BOOL RegDelnodeRecurse(HKEY hKeyRoot, LPTSTR lpSubKey)
+{
+	LPTSTR lpEnd;
+	LONG lResult;
+	DWORD dwSize;
+	TCHAR szName[MAX_PATH];
+	HKEY hKey;
+	FILETIME ftWrite;
+
+	// First, see if we can delete the key without having
+	// to recurse.
+
+	lResult = RegDeleteKey(hKeyRoot, lpSubKey);
+
+	if (lResult == ERROR_SUCCESS)
+		return TRUE;
+
+	lResult = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
+
+	if (lResult != ERROR_SUCCESS)
+	{
+		if (lResult == ERROR_FILE_NOT_FOUND) {
+			return TRUE;
+		}
+		else {
+			return FALSE;
+		}
+	}
+
+	// Check for an ending slash and add one if it is missing.
+
+	lpEnd = lpSubKey + lstrlen(lpSubKey);
+
+	if (*(lpEnd - 1) != TEXT('\\'))
+	{
+		*lpEnd = TEXT('\\');
+		lpEnd++;
+		*lpEnd = TEXT('\0');
+	}
+
+	// Enumerate the keys
+
+	dwSize = MAX_PATH;
+	lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
+		NULL, NULL, &ftWrite);
+
+	if (lResult == ERROR_SUCCESS)
+	{
+		do {
+
+			*lpEnd = TEXT('\0');
+			StringCchCat(lpSubKey, MAX_PATH * 2, szName);
+
+			if (!RegDelnodeRecurse(hKeyRoot, lpSubKey)) {
+				break;
+			}
+
+			dwSize = MAX_PATH;
+
+			lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
+				NULL, NULL, &ftWrite);
+
+		} while (lResult == ERROR_SUCCESS);
+	}
+
+	lpEnd--;
+	*lpEnd = TEXT('\0');
+
+	RegCloseKey(hKey);
+
+	// Try again to delete the key.
+
+	lResult = RegDeleteKey(hKeyRoot, lpSubKey);
+
+	if (lResult == ERROR_SUCCESS)
+		return TRUE;
+
+	return FALSE;
+}
+
+//*************************************************************
+//
+//  RegDelnode()
+//
+//  Purpose:    Deletes a registry key and all its subkeys / values.
+//
+//  Parameters: hKeyRoot    -   Root key
+//              lpSubKey    -   SubKey to delete
+//
+//  Return:     TRUE if successful.
+//              FALSE if an error occurs.
+//
+//*************************************************************
+
+BOOL RegDelnode(HKEY hKeyRoot, LPCTSTR lpSubKey)
+{
+	TCHAR szDelKey[MAX_PATH * 2];
+
+	StringCchCopy(szDelKey, MAX_PATH * 2, lpSubKey);
+	return RegDelnodeRecurse(hKeyRoot, szDelKey);
+
+}
+std::vector<std::string> getSubKeys(const char* key)
+{
+	std::vector<std::string> subkeys;
+	HKEY hKey;
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+		key,
+		0,
+		KEY_READ,
+		&hKey) == ERROR_SUCCESS) {
+
+		TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
+		DWORD    cbName;                   // size of name string 
+		TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
+		DWORD    cchClassName = MAX_PATH;  // size of class string 
+		DWORD    cSubKeys = 0;               // number of subkeys 
+		DWORD    cbMaxSubKey;              // longest subkey size 
+		DWORD    cchMaxClass;              // longest class string 
+		DWORD    cValues;              // number of values for key 
+		DWORD    cchMaxValue;          // longest value name 
+		DWORD    cbMaxValueData;       // longest value data 
+		DWORD    cbSecurityDescriptor; // size of security descriptor 
+		FILETIME ftLastWriteTime;      // last write time 
+
+		DWORD i, retCode;
+
+		TCHAR  achValue[MAX_VALUE_NAME];
+		DWORD cchValue = MAX_VALUE_NAME;
+
+		retCode = RegQueryInfoKey(
+			hKey,                    // key handle 
+			achClass,                // buffer for class name 
+			&cchClassName,           // size of class string 
+			NULL,                    // reserved 
+			&cSubKeys,               // number of subkeys 
+			&cbMaxSubKey,            // longest subkey size 
+			&cchMaxClass,            // longest class string 
+			&cValues,                // number of values for this key 
+			&cchMaxValue,            // longest value name 
+			&cbMaxValueData,         // longest value data 
+			&cbSecurityDescriptor,   // security descriptor 
+			&ftLastWriteTime);       // last write time 
+
+		for (i = 0; i < cSubKeys; ++i) {
+			cbName = MAX_KEY_LENGTH;
+			retCode = RegEnumKeyEx(
+				hKey,
+				i,
+				achKey,
+				&cbName,
+				NULL,
+				NULL,
+				NULL,
+				&ftLastWriteTime);
+			if (retCode == ERROR_SUCCESS) {
+				subkeys.push_back(achKey);
+			}
+		}
+	}
+	RegCloseKey(hKey);
+	return subkeys;
+}
+
+std::vector<std::string> getValueList(const char* key) {
+	std::vector<std::string> values;
+	HKEY hKey;
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+		key,
+		0,
+		KEY_READ,
+		&hKey) == ERROR_SUCCESS) {
+
+		TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
+		DWORD    cbName;                   // size of name string 
+		TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
+		DWORD    cchClassName = MAX_PATH;  // size of class string 
+		DWORD    cSubKeys = 0;               // number of subkeys 
+		DWORD    cbMaxSubKey;              // longest subkey size 
+		DWORD    cchMaxClass;              // longest class string 
+		DWORD    cValues;              // number of values for key 
+		DWORD    cchMaxValue;          // longest value name 
+		DWORD    cbMaxValueData;       // longest value data 
+		DWORD    cbSecurityDescriptor; // size of security descriptor 
+		FILETIME ftLastWriteTime;      // last write time 
+
+		DWORD i, retCode;
+
+		TCHAR  achValue[MAX_VALUE_NAME];
+		DWORD cchValue = MAX_VALUE_NAME;
+
+		retCode = RegQueryInfoKey(
+			hKey,                    // key handle 
+			achClass,                // buffer for class name 
+			&cchClassName,           // size of class string 
+			NULL,                    // reserved 
+			&cSubKeys,               // number of subkeys 
+			&cbMaxSubKey,            // longest subkey size 
+			&cchMaxClass,            // longest class string 
+			&cValues,                // number of values for this key 
+			&cchMaxValue,            // longest value name 
+			&cbMaxValueData,         // longest value data 
+			&cbSecurityDescriptor,   // security descriptor 
+			&ftLastWriteTime);       // last write time 
+		
+		for (i = 0, retCode = ERROR_SUCCESS; i < cValues; ++i) {
+			cchValue = MAX_VALUE_NAME;
+			achValue[0] = '\0';
+			retCode = RegEnumValue(
+				hKey,
+				i,
+				achValue,
+				&cchValue,
+				NULL,
+				NULL,
+				NULL,
+				NULL);
+			if (retCode == ERROR_SUCCESS) {
+				values.push_back(achValue);
+			}
+		}
+	}
+	RegCloseKey(hKey);
+	return values;
+}
+
+std::pair<bool, std::string> WinDNSHelper::hasDNSConfig(uint64_t nwid)
+{
+	char networkStr[20] = { 0 };
+	sprintf(networkStr, "%.16llx", nwid);
+
+	const char* baseKey = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
+	auto subkeys = getSubKeys(baseKey);
+	for (auto it = subkeys.begin(); it != subkeys.end(); ++it) {
+		char sub[MAX_KEY_LENGTH] = { 0 };
+		sprintf(sub, "%s\\%s", baseKey, it->c_str());
+		auto dnsRecords = getValueList(sub);
+		for (auto it2 = dnsRecords.begin(); it2 != dnsRecords.end(); ++it2) {
+			if ((*it2) == "Comment") {
+				HKEY hKey;
+				if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+					sub,
+					0,
+					KEY_READ,
+					&hKey) == ERROR_SUCCESS) {
+
+					char buf[16384] = { 0 };
+					DWORD size = sizeof(buf);
+					DWORD retCode = RegGetValueA(
+						HKEY_LOCAL_MACHINE, 
+						sub, 
+						it2->c_str(), 
+						RRF_RT_REG_SZ,
+						NULL, 
+						&buf, 
+						&size);
+					if (retCode == ERROR_SUCCESS) {
+						if (std::string(networkStr) == std::string(buf)) {
+							RegCloseKey(hKey);
+							return std::make_pair(true, std::string(sub));
+						}
+					}
+					else {
+
+					}
+				}
+				RegCloseKey(hKey);
+			} 
+		}
+	}
+
+	return std::make_pair(false, std::string());
+}
+
+void WinDNSHelper::setDNS(uint64_t nwid, const char* domain, const std::vector<InetAddress>& servers)
+{
+	auto hasConfig = hasDNSConfig(nwid);
+
+	std::stringstream ss;
+	for (auto it = servers.begin(); it != servers.end(); ++it) {
+		char ipaddr[256] = { 0 };
+		ss << it->toIpString(ipaddr);
+		if ((it + 1) != servers.end()) {
+			ss << ";";
+		}
+	}
+	std::string serverValue = ss.str();
+
+	if (hasConfig.first) {
+		// update existing config
+		HKEY dnsKey;
+		if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, hasConfig.second.c_str(), 0, KEY_READ | KEY_WRITE, &dnsKey) == ERROR_SUCCESS) {
+			auto retCode = RegSetKeyValueA(dnsKey, NULL, "GenericDNSServers", REG_SZ, serverValue.data(), (DWORD)serverValue.length());
+			if (retCode != ERROR_SUCCESS) {
+				fprintf(stderr, "Error writing dns servers: %d\n", retCode);
+			}
+		}
+	} else {
+		// add new config
+		const char* baseKey = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig";
+		GUID guid;
+		CoCreateGuid(&guid);
+		wchar_t guidTmp[128] = { 0 };
+		char guidStr[128] = { 0 };
+		StringFromGUID2(guid, guidTmp, 128);
+		wcstombs(guidStr, guidTmp, 128);
+		char fullKey[MAX_KEY_LENGTH] = { 0 };
+		sprintf(fullKey, "%s\\%s", baseKey, guidStr);
+		HKEY dnsKey;
+		RegCreateKeyA(HKEY_LOCAL_MACHINE, fullKey, &dnsKey);
+		if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, fullKey, 0, KEY_READ | KEY_WRITE, &dnsKey) == ERROR_SUCCESS) {
+			char nwString[32] = { 0 };
+			sprintf(nwString, "%.16llx", nwid);
+			RegSetKeyValueA(dnsKey, NULL, "Comment", REG_SZ, nwString, strlen(nwString));
+
+			DWORD configOpts = 8;
+			RegSetKeyValueA(dnsKey, NULL, "ConfigOptions", REG_DWORD, &configOpts, sizeof(DWORD));
+			RegSetKeyValueA(dnsKey, NULL, "DisplayName", REG_SZ, "", 0);
+			RegSetKeyValueA(dnsKey, NULL, "GenericDNSServers", REG_SZ, serverValue.data(), serverValue.length());
+			RegSetKeyValueA(dnsKey, NULL, "IPSECCARestriction", REG_SZ, "", 0);
+			std::string d = "." + std::string(domain);
+			RegSetKeyValueA(dnsKey, NULL, "Name", REG_MULTI_SZ, d.data(), d.length());
+			DWORD version = 2;
+			RegSetKeyValueA(dnsKey, NULL, "Version", REG_DWORD, &version, sizeof(DWORD));
+		}
+	}
+}
+
+void WinDNSHelper::removeDNS(uint64_t nwid)
+{
+	auto hasConfig = hasDNSConfig(nwid);
+	if (hasConfig.first) {
+		RegDelnode(HKEY_LOCAL_MACHINE, hasConfig.second.c_str());
+	}
+}
+
+}

+ 24 - 0
osdep/WinDNSHelper.hpp

@@ -0,0 +1,24 @@
+#ifndef WIN_DNS_HELPER_H_
+#define WIN_DNS_HELPER_H_
+
+#include <vector>
+#include <cstdint>
+#include "../node/InetAddress.hpp"
+
+
+namespace ZeroTier 
+{
+
+class WinDNSHelper
+{
+public:
+	static void setDNS(uint64_t nwid, const char* domain, const std::vector<InetAddress>& servers);
+	static void removeDNS(uint64_t nwid);
+
+private:
+	static std::pair<bool, std::string> hasDNSConfig(uint64_t nwid);
+};
+
+}
+
+#endif

+ 31 - 0
osdep/WindowsEthernetTap.cpp

@@ -44,6 +44,7 @@
 #include "OSUtils.hpp"
 #include "OSUtils.hpp"
 
 
 #include "..\windows\TapDriver6\tap-windows.h"
 #include "..\windows\TapDriver6\tap-windows.h"
+#include "WinDNSHelper.hpp"
 
 
 #include <netcon.h>
 #include <netcon.h>
 
 
@@ -473,6 +474,29 @@ WindowsEthernetTap::WindowsEthernetTap(
 	char data[1024];
 	char data[1024];
 	char tag[24];
 	char tag[24];
 
 
+	// Initialize COM
+	HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+	if (FAILED(hres)) {
+		throw std::runtime_error("WinEthernetTap: COM initialization failed");
+	}
+
+	hres = CoInitializeSecurity(
+		NULL,
+		-1,
+		NULL,
+		NULL,
+		RPC_C_AUTHN_LEVEL_DEFAULT,
+		RPC_C_IMP_LEVEL_IMPERSONATE,
+		NULL,
+		EOAC_NONE,
+		NULL
+	);
+	if (FAILED(hres)) {
+		CoUninitialize();
+		throw std::runtime_error("WinEthernetTap: Failed to initialize security");
+	}
+
+
 	// We "tag" registry entries with the network ID to identify persistent devices
 	// We "tag" registry entries with the network ID to identify persistent devices
 	OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
 	OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
 
 
@@ -646,6 +670,8 @@ WindowsEthernetTap::WindowsEthernetTap(
 
 
 WindowsEthernetTap::~WindowsEthernetTap()
 WindowsEthernetTap::~WindowsEthernetTap()
 {
 {
+	WinDNSHelper::removeDNS(_nwid);
+	CoUninitialize();
 	_run = false;
 	_run = false;
 	ReleaseSemaphore(_injectSemaphore,1,NULL);
 	ReleaseSemaphore(_injectSemaphore,1,NULL);
 	Thread::join(_thread);
 	Thread::join(_thread);
@@ -1290,4 +1316,9 @@ void WindowsEthernetTap::_syncIps()
 	}
 	}
 }
 }
 
 
+void WindowsEthernetTap::setDns(const char* domain, const std::vector<InetAddress>& servers)
+{
+	WinDNSHelper::setDNS(_nwid, domain, servers);
+}
+
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 1 - 0
osdep/WindowsEthernetTap.hpp

@@ -97,6 +97,7 @@ public:
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void setFriendlyName(const char *friendlyName);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
 	virtual void setMtu(unsigned int mtu);
 	virtual void setMtu(unsigned int mtu);
+	virtual void setDns(const char* domain, const std::vector<InetAddress> &servers);
 
 
 	inline const NET_LUID &luid() const { return _deviceLuid; }
 	inline const NET_LUID &luid() const { return _deviceLuid; }
 	inline const GUID &guid() const { return _deviceGuid; }
 	inline const GUID &guid() const { return _deviceGuid; }

+ 32 - 4
service/OneService.cpp

@@ -231,6 +231,20 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
 		mca.push_back(m);
 		mca.push_back(m);
 	}
 	}
 	nj["multicastSubscriptions"] = mca;
 	nj["multicastSubscriptions"] = mca;
+
+	nlohmann::json m;
+	m["domain"] = nc->dns.domain;
+	m["servers"] = nlohmann::json::array();
+	for(int j=0;j<ZT_MAX_DNS_SERVERS;++j) {
+		
+		InetAddress a(nc->dns.server_addr[j]);
+		if (a.isV4() || a.isV6()) {
+			char buf[256];
+			m["servers"].push_back(a.toIpString(buf));
+		}
+	}
+	nj["dns"] = m;
+	
 }
 }
 
 
 static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
 static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
@@ -501,6 +515,7 @@ public:
 			settings.allowManaged = true;
 			settings.allowManaged = true;
 			settings.allowGlobal = false;
 			settings.allowGlobal = false;
 			settings.allowDefault = false;
 			settings.allowDefault = false;
+			memset(&config, 0, sizeof(ZT_VirtualNetworkConfig));
 		}
 		}
 
 
 		std::shared_ptr<EthernetTap> tap;
 		std::shared_ptr<EthernetTap> tap;
@@ -831,7 +846,7 @@ public:
 						Mutex::Lock _l(_nets_m);
 						Mutex::Lock _l(_nets_m);
 						for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
 						for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
 							if (n->second.tap)
 							if (n->second.tap)
-								syncManagedStuff(n->second,false,true);
+								syncManagedStuff(n->second,false,true,false);
 						}
 						}
 					}
 					}
 				}
 				}
@@ -1117,7 +1132,7 @@ public:
 		}
 		}
 
 
 		if (n->second.tap)
 		if (n->second.tap)
-			syncManagedStuff(n->second,true,true);
+			syncManagedStuff(n->second,true,true,true);
 
 
 		return true;
 		return true;
 	}
 	}
@@ -1864,7 +1879,7 @@ public:
 	}
 	}
 
 
 	// Apply or update managed IPs for a configured network (be sure n.tap exists)
 	// Apply or update managed IPs for a configured network (be sure n.tap exists)
-	void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
+	void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes, bool syncDns)
 	{
 	{
 		char ipbuf[64];
 		char ipbuf[64];
 
 
@@ -1984,6 +1999,19 @@ public:
 #endif
 #endif
 			}
 			}
 		}
 		}
+
+		if (syncDns) {
+			if (strlen(n.config.dns.domain) != 0) {
+				std::vector<InetAddress> servers;
+				for (int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) {
+					InetAddress a(n.config.dns.server_addr[j]);
+					if (a.isV4() || a.isV6()) {
+						servers.push_back(a);
+					}
+				}
+				n.tap->setDns(n.config.dns.domain, servers);
+			}
+		}
 	}
 	}
 
 
 	// =========================================================================
 	// =========================================================================
@@ -2333,7 +2361,7 @@ public:
 						Sleep(10);
 						Sleep(10);
 					}
 					}
 #endif
 #endif
-					syncManagedStuff(n,true,true);
+					syncManagedStuff(n,true,true,true);
 					n.tap->setMtu(nwc->mtu);
 					n.tap->setMtu(nwc->mtu);
 				} else {
 				} else {
 					_nets.erase(nwid);
 					_nets.erase(nwid);

+ 10 - 4
windows/ZeroTierOne/ZeroTierOne.vcxproj

@@ -33,7 +33,6 @@
     <ClCompile Include="..\..\controller\FileDB.cpp" />
     <ClCompile Include="..\..\controller\FileDB.cpp" />
     <ClCompile Include="..\..\controller\LFDB.cpp" />
     <ClCompile Include="..\..\controller\LFDB.cpp" />
     <ClCompile Include="..\..\controller\PostgreSQL.cpp" />
     <ClCompile Include="..\..\controller\PostgreSQL.cpp" />
-    <ClCompile Include="..\..\controller\RabbitMQ.cpp" />
     <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
     <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
     <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
     <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
     <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
     <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
@@ -51,6 +50,8 @@
     <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
     <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
     <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
     <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
     <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
     <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
+    <ClCompile Include="..\..\node\Bond.cpp" />
+    <ClCompile Include="..\..\node\BondController.cpp" />
     <ClCompile Include="..\..\node\C25519.cpp">
     <ClCompile Include="..\..\node\C25519.cpp">
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MaxSpeed</Optimization>
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MaxSpeed</Optimization>
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MaxSpeed</Optimization>
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MaxSpeed</Optimization>
@@ -100,6 +101,7 @@
     <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
     <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
     <ClCompile Include="..\..\osdep\OSUtils.cpp" />
     <ClCompile Include="..\..\osdep\OSUtils.cpp" />
     <ClCompile Include="..\..\osdep\PortMapper.cpp" />
     <ClCompile Include="..\..\osdep\PortMapper.cpp" />
+    <ClCompile Include="..\..\osdep\WinDNSHelper.cpp" />
     <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
     <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
     <ClCompile Include="..\..\selftest.cpp">
     <ClCompile Include="..\..\selftest.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -135,7 +137,7 @@
     <ClInclude Include="..\..\controller\FileDB.hpp" />
     <ClInclude Include="..\..\controller\FileDB.hpp" />
     <ClInclude Include="..\..\controller\LFDB.hpp" />
     <ClInclude Include="..\..\controller\LFDB.hpp" />
     <ClInclude Include="..\..\controller\PostgreSQL.hpp" />
     <ClInclude Include="..\..\controller\PostgreSQL.hpp" />
-    <ClInclude Include="..\..\controller\RabbitMQ.hpp" />
+    <ClInclude Include="..\..\controller\Redis.hpp" />
     <ClInclude Include="..\..\ext\cpp-httplib\httplib.h" />
     <ClInclude Include="..\..\ext\cpp-httplib\httplib.h" />
     <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
     <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
     <ClInclude Include="..\..\ext\json\json.hpp" />
     <ClInclude Include="..\..\ext\json\json.hpp" />
@@ -162,6 +164,8 @@
     <ClInclude Include="..\..\include\ZeroTierOne.h" />
     <ClInclude Include="..\..\include\ZeroTierOne.h" />
     <ClInclude Include="..\..\node\Address.hpp" />
     <ClInclude Include="..\..\node\Address.hpp" />
     <ClInclude Include="..\..\node\AtomicCounter.hpp" />
     <ClInclude Include="..\..\node\AtomicCounter.hpp" />
+    <ClInclude Include="..\..\node\Bond.hpp" />
+    <ClInclude Include="..\..\node\BondController.hpp" />
     <ClInclude Include="..\..\node\Buffer.hpp" />
     <ClInclude Include="..\..\node\Buffer.hpp" />
     <ClInclude Include="..\..\node\C25519.hpp" />
     <ClInclude Include="..\..\node\C25519.hpp" />
     <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
     <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
@@ -205,6 +209,7 @@
     <ClInclude Include="..\..\osdep\Phy.hpp" />
     <ClInclude Include="..\..\osdep\Phy.hpp" />
     <ClInclude Include="..\..\osdep\PortMapper.hpp" />
     <ClInclude Include="..\..\osdep\PortMapper.hpp" />
     <ClInclude Include="..\..\osdep\Thread.hpp" />
     <ClInclude Include="..\..\osdep\Thread.hpp" />
+    <ClInclude Include="..\..\osdep\WinDNSHelper.hpp" />
     <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
     <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
     <ClInclude Include="..\..\service\OneService.hpp" />
     <ClInclude Include="..\..\service\OneService.hpp" />
     <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
     <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
@@ -240,6 +245,7 @@
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
     <PlatformToolset>v142</PlatformToolset>
     <PlatformToolset>v142</PlatformToolset>
     <CharacterSet>MultiByte</CharacterSet>
     <CharacterSet>MultiByte</CharacterSet>
+    <UseOfMfc>false</UseOfMfc>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>
@@ -367,7 +373,7 @@
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
       <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
       <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
     </Link>
     </Link>
@@ -456,7 +462,7 @@
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>

+ 24 - 9
windows/ZeroTierOne/ZeroTierOne.vcxproj.filters

@@ -90,9 +90,6 @@
     <ClCompile Include="..\..\service\OneService.cpp">
     <ClCompile Include="..\..\service\OneService.cpp">
       <Filter>Source Files\service</Filter>
       <Filter>Source Files\service</Filter>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
-      <Filter>Source Files\osdep</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\osdep\Http.cpp">
     <ClCompile Include="..\..\osdep\Http.cpp">
       <Filter>Source Files\osdep</Filter>
       <Filter>Source Files\osdep</Filter>
     </ClCompile>
     </ClCompile>
@@ -264,9 +261,6 @@
     <ClCompile Include="..\..\controller\PostgreSQL.cpp">
     <ClCompile Include="..\..\controller\PostgreSQL.cpp">
       <Filter>Source Files\controller</Filter>
       <Filter>Source Files\controller</Filter>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="..\..\controller\RabbitMQ.cpp">
-      <Filter>Source Files\controller</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\controller\LFDB.cpp">
     <ClCompile Include="..\..\controller\LFDB.cpp">
       <Filter>Source Files\controller</Filter>
       <Filter>Source Files\controller</Filter>
     </ClCompile>
     </ClCompile>
@@ -276,6 +270,18 @@
     <ClCompile Include="..\..\osdep\EthernetTap.cpp">
     <ClCompile Include="..\..\osdep\EthernetTap.cpp">
       <Filter>Source Files\osdep</Filter>
       <Filter>Source Files\osdep</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\node\Bond.cpp">
+      <Filter>Source Files\node</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\node\BondController.cpp">
+      <Filter>Source Files\node</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\osdep\WinDNSHelper.cpp">
+      <Filter>Source Files\osdep</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
+      <Filter>Source Files\osdep</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="resource.h">
     <ClInclude Include="resource.h">
@@ -515,9 +521,6 @@
     <ClInclude Include="..\..\controller\PostgreSQL.hpp">
     <ClInclude Include="..\..\controller\PostgreSQL.hpp">
       <Filter>Header Files\controller</Filter>
       <Filter>Header Files\controller</Filter>
     </ClInclude>
     </ClInclude>
-    <ClInclude Include="..\..\controller\RabbitMQ.hpp">
-      <Filter>Header Files\controller</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\controller\LFDB.hpp">
     <ClInclude Include="..\..\controller\LFDB.hpp">
       <Filter>Header Files\controller</Filter>
       <Filter>Header Files\controller</Filter>
     </ClInclude>
     </ClInclude>
@@ -530,6 +533,18 @@
     <ClInclude Include="..\..\osdep\EthernetTap.hpp">
     <ClInclude Include="..\..\osdep\EthernetTap.hpp">
       <Filter>Header Files\osdep</Filter>
       <Filter>Header Files\osdep</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\node\Bond.hpp">
+      <Filter>Header Files\node</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\node\BondController.hpp">
+      <Filter>Header Files\node</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\controller\Redis.hpp">
+      <Filter>Header Files\controller</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\osdep\WinDNSHelper.hpp">
+      <Filter>Header Files\osdep</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ZeroTierOne.rc">
     <ResourceCompile Include="ZeroTierOne.rc">