summaryrefslogtreecommitdiff
path: root/node/NodeConfig.cpp
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
committerAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
commit150850b80012f852521c9935145cf966946334d5 (patch)
treec082369f2fef2515cfa2e4acf1b83250a3963158 /node/NodeConfig.cpp
downloadinfinitytier-150850b80012f852521c9935145cf966946334d5.tar.gz
infinitytier-150850b80012f852521c9935145cf966946334d5.zip
New git repository for release - version 0.2.0 tagged
Diffstat (limited to 'node/NodeConfig.cpp')
-rw-r--r--node/NodeConfig.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp
new file mode 100644
index 00000000..47310050
--- /dev/null
+++ b/node/NodeConfig.cpp
@@ -0,0 +1,206 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <memory>
+#include <string>
+
+#include <json/json.h>
+
+#include "NodeConfig.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Defaults.hpp"
+#include "Utils.hpp"
+#include "Logger.hpp"
+
+namespace ZeroTier {
+
+NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const std::string &url) :
+ _r(renv),
+ _lastAutoconfigure(0),
+ _lastAutoconfigureLastModified(),
+ _url(url),
+ _autoconfigureLock(),
+ _networks(),
+ _networks_m()
+{
+}
+
+NodeConfig::~NodeConfig()
+{
+ _autoconfigureLock.lock(); // wait for any autoconfs to finish
+ _autoconfigureLock.unlock();
+}
+
+void NodeConfig::refreshConfiguration()
+{
+ _autoconfigureLock.lock(); // unlocked when handler gets called
+
+ TRACE("refreshing autoconfigure URL %s (if modified since: '%s')",_url.c_str(),_lastAutoconfigureLastModified.c_str());
+
+ std::map<std::string,std::string> reqHeaders;
+ reqHeaders["X-ZT-ID"] = _r->identity.toString(false);
+ reqHeaders["X-ZT-OVSH"] = _r->ownershipVerificationSecretHash;
+ if (_lastAutoconfigureLastModified.length())
+ reqHeaders["If-Modified-Since"] = _lastAutoconfigureLastModified;
+
+ new Http::Request(Http::HTTP_METHOD_GET,_url,reqHeaders,std::string(),&NodeConfig::_CBautoconfHandler,this);
+}
+
+void NodeConfig::__CBautoconfHandler(const std::string &lastModified,const std::string &body)
+{
+ try {
+ Json::Value root;
+ Json::Reader reader;
+
+ std::string dec(_r->identity.decrypt(_r->configAuthority,body.data(),body.length()));
+ if (!dec.length()) {
+ LOG("autoconfigure from %s failed: data did not decrypt as from config authority %s",_url.c_str(),_r->configAuthority.address().toString().c_str());
+ return;
+ }
+ TRACE("decrypted autoconf: %s",dec.c_str());
+
+ if (!reader.parse(dec,root,false)) {
+ LOG("autoconfigure from %s failed: JSON parse error: %s",_url.c_str(),reader.getFormattedErrorMessages().c_str());
+ return;
+ }
+
+ if (!root.isObject()) {
+ LOG("autoconfigure from %s failed: not a JSON object",_url.c_str());
+ return;
+ }
+
+ // Configure networks
+ const Json::Value &networks = root["_networks"];
+ if (networks.isArray()) {
+ Mutex::Lock _l(_networks_m);
+ for(unsigned int ni=0;ni<networks.size();++ni) {
+ if (networks[ni].isObject()) {
+ const Json::Value &nwid_ = networks[ni]["id"];
+ uint64_t nwid = nwid_.isNumeric() ? (uint64_t)nwid_.asUInt64() : (uint64_t)strtoull(networks[ni]["id"].asString().c_str(),(char **)0,10);
+
+ if (nwid) {
+ SharedPtr<Network> nw;
+ std::map< uint64_t,SharedPtr<Network> >::iterator nwent(_networks.find(nwid));
+ if (nwent != _networks.end())
+ nw = nwent->second;
+ else {
+ try {
+ nw = SharedPtr<Network>(new Network(_r,nwid));
+ _networks[nwid] = nw;
+ } catch (std::exception &exc) {
+ LOG("unable to create network %llu: %s",nwid,exc.what());
+ } catch ( ... ) {
+ LOG("unable to create network %llu: unknown exception",nwid);
+ }
+ }
+
+ if (nw) {
+ Mutex::Lock _l2(nw->_lock);
+ nw->_open = networks[ni]["isOpen"].asBool();
+
+ // Ensure that TAP device has all the right IP addresses
+ // TODO: IPv6 might work a tad differently
+ std::set<InetAddress> allIps;
+ const Json::Value &addresses = networks[ni]["_addresses"];
+ if (addresses.isArray()) {
+ for(unsigned int ai=0;ai<addresses.size();++ai) {
+ if (addresses[ai].isString()) {
+ InetAddress addr(addresses[ai].asString());
+ if (addr) {
+ TRACE("network %llu IP/netmask: %s",nwid,addr.toString().c_str());
+ allIps.insert(addr);
+ }
+ }
+ }
+ }
+ nw->_tap.setIps(allIps);
+
+ // NOTE: the _members field is optional for open networks,
+ // since members of open nets do not need to check membership
+ // of packet senders and mutlicasters.
+ const Json::Value &members = networks[ni]["_members"];
+ nw->_members.clear();
+ if (members.isArray()) {
+ for(unsigned int mi=0;mi<members.size();++mi) {
+ std::string rawAddr(Utils::unhex(members[mi].asString()));
+ if (rawAddr.length() == ZT_ADDRESS_LENGTH) {
+ Address addr(rawAddr.data());
+ if ((addr)&&(!addr.isReserved())) {
+ TRACE("network %llu member: %s",nwid,addr.toString().c_str());
+ nw->_members.insert(addr);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ TRACE("ignored networks[%u], 'id' field missing");
+ }
+ } else {
+ TRACE("ignored networks[%u], not a JSON object",ni);
+ }
+ }
+ }
+
+ _lastAutoconfigure = Utils::now();
+ _lastAutoconfigureLastModified = lastModified;
+ } catch (std::exception &exc) {
+ TRACE("exception parsing autoconf URL response: %s",exc.what());
+ } catch ( ... ) {
+ TRACE("unexpected exception parsing autoconf URL response");
+ }
+}
+
+bool NodeConfig::_CBautoconfHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body)
+{
+#ifdef ZT_TRACE
+ const RuntimeEnvironment *_r = ((NodeConfig *)arg)->_r;
+#endif
+
+ if (code == 200) {
+ TRACE("200 got autoconfigure response from %s: %u bytes",url.c_str(),(unsigned int)body.length());
+
+ std::map<std::string,std::string>::const_iterator lm(headers.find("Last-Modified"));
+ if (lm != headers.end())
+ ((NodeConfig *)arg)->__CBautoconfHandler(lm->second,body);
+ else ((NodeConfig *)arg)->__CBautoconfHandler(std::string(),body);
+ } else if (code == 304) {
+ TRACE("304 autoconfigure deferred, remote URL %s not modified",url.c_str());
+ ((NodeConfig *)arg)->_lastAutoconfigure = Utils::now(); // still considered a success
+ } else if (code == 409) { // conflict, ID address in use by another ID
+ TRACE("%d autoconfigure failed from %s",code,url.c_str());
+ } else {
+ TRACE("%d autoconfigure failed from %s",code,url.c_str());
+ }
+
+ ((NodeConfig *)arg)->_autoconfigureLock.unlock();
+ return false; // causes Request to delete itself
+}
+
+} // namespace ZeroTier