diff options
40 files changed, 1674 insertions, 654 deletions
@@ -71,6 +71,7 @@ zt1-src.tar.gz *.tmp .depend node_modules +zt1_update_* debian/files debian/zerotier-one debian/zerotier-one*.debhelper diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index bed76df7..3c13e7ce 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -63,53 +63,6 @@ using json = nlohmann::json; namespace ZeroTier { -// Get JSON values as unsigned integers, strings, or booleans, doing type conversion if possible -static uint64_t _jI(const json &jv,const uint64_t dfl) -{ - if (jv.is_number()) { - return (uint64_t)jv; - } else if (jv.is_string()) { - std::string s = jv; - return Utils::strToU64(s.c_str()); - } else if (jv.is_boolean()) { - return ((bool)jv ? 1ULL : 0ULL); - } - return dfl; -} -static bool _jB(const json &jv,const bool dfl) -{ - if (jv.is_boolean()) { - return (bool)jv; - } else if (jv.is_number()) { - return ((uint64_t)jv > 0ULL); - } else if (jv.is_string()) { - std::string s = jv; - if (s.length() > 0) { - switch(s[0]) { - case 't': - case 'T': - case '1': - return true; - } - } - return false; - } - return dfl; -} -static std::string _jS(const json &jv,const char *dfl) -{ - if (jv.is_string()) { - return jv; - } else if (jv.is_number()) { - char tmp[64]; - Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv); - return tmp; - } else if (jv.is_boolean()) { - return ((bool)jv ? std::string("1") : std::string("0")); - } - return std::string((dfl) ? dfl : ""); -} - static json _renderRule(ZT_VirtualNetworkRule &rule) { char tmp[128]; @@ -281,13 +234,13 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) if (!r.is_object()) return false; - const std::string t(_jS(r["type"],"")); + const std::string t(OSUtils::jsonString(r["type"],"")); memset(&rule,0,sizeof(ZT_VirtualNetworkRule)); - if (_jB(r["not"],false)) + if (OSUtils::jsonBool(r["not"],false)) rule.t = 0x80; else rule.t = 0x00; - if (_jB(r["or"],false)) + if (OSUtils::jsonBool(r["or"],false)) rule.t |= 0x40; if (t == "ACTION_DROP") { @@ -298,115 +251,115 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) return true; } else if (t == "ACTION_TEE") { rule.t |= ZT_NETWORK_RULE_ACTION_TEE; - rule.v.fwd.address = Utils::hexStrToU64(_jS(r["address"],"0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL); - rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); + rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL); return true; } else if (t == "ACTION_WATCH") { rule.t |= ZT_NETWORK_RULE_ACTION_WATCH; - rule.v.fwd.address = Utils::hexStrToU64(_jS(r["address"],"0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL); - rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); + rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL); return true; } else if (t == "ACTION_REDIRECT") { rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT; - rule.v.fwd.address = Utils::hexStrToU64(_jS(r["address"],"0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); return true; } else if (t == "ACTION_DEBUG_LOG") { rule.t |= ZT_NETWORK_RULE_ACTION_DEBUG_LOG; return true; } else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "MATCH_DEST_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "MATCH_VLAN_ID") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_ID; - rule.v.vlanId = (uint16_t)(_jI(r["vlanId"],0ULL) & 0xffffULL); + rule.v.vlanId = (uint16_t)(OSUtils::jsonInt(r["vlanId"],0ULL) & 0xffffULL); return true; } else if (t == "MATCH_VLAN_PCP") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_PCP; - rule.v.vlanPcp = (uint8_t)(_jI(r["vlanPcp"],0ULL) & 0xffULL); + rule.v.vlanPcp = (uint8_t)(OSUtils::jsonInt(r["vlanPcp"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_VLAN_DEI") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_DEI; - rule.v.vlanDei = (uint8_t)(_jI(r["vlanDei"],0ULL) & 0xffULL); + rule.v.vlanDei = (uint8_t)(OSUtils::jsonInt(r["vlanDei"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_MAC_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_SOURCE; - const std::string mac(_jS(r["mac"],"0")); + const std::string mac(OSUtils::jsonString(r["mac"],"0")); Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; } else if (t == "MATCH_MAC_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_DEST; - const std::string mac(_jS(r["mac"],"0")); + const std::string mac(OSUtils::jsonString(r["mac"],"0")); Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; } else if (t == "MATCH_IPV4_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE; - InetAddress ip(_jS(r["ip"],"0.0.0.0")); + InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0")); rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr; rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff; if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; } else if (t == "MATCH_IPV4_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST; - InetAddress ip(_jS(r["ip"],"0.0.0.0")); + InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0")); rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr; rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff; if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; } else if (t == "MATCH_IPV6_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE; - InetAddress ip(_jS(r["ip"],"::0")); + InetAddress ip(OSUtils::jsonString(r["ip"],"::0")); memcpy(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16); rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff; if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; } else if (t == "MATCH_IPV6_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST; - InetAddress ip(_jS(r["ip"],"::0")); + InetAddress ip(OSUtils::jsonString(r["ip"],"::0")); memcpy(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16); rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff; if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; } else if (t == "MATCH_IP_TOS") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_TOS; - rule.v.ipTos = (uint8_t)(_jI(r["ipTos"],0ULL) & 0xffULL); + rule.v.ipTos = (uint8_t)(OSUtils::jsonInt(r["ipTos"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_IP_PROTOCOL") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; - rule.v.ipProtocol = (uint8_t)(_jI(r["ipProtocol"],0ULL) & 0xffULL); + rule.v.ipProtocol = (uint8_t)(OSUtils::jsonInt(r["ipProtocol"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_ETHERTYPE") { rule.t |= ZT_NETWORK_RULE_MATCH_ETHERTYPE; - rule.v.etherType = (uint16_t)(_jI(r["etherType"],0ULL) & 0xffffULL); + rule.v.etherType = (uint16_t)(OSUtils::jsonInt(r["etherType"],0ULL) & 0xffffULL); return true; } else if (t == "MATCH_ICMP") { rule.t |= ZT_NETWORK_RULE_MATCH_ICMP; - rule.v.icmp.type = (uint8_t)(_jI(r["icmpType"],0ULL) & 0xffULL); + rule.v.icmp.type = (uint8_t)(OSUtils::jsonInt(r["icmpType"],0ULL) & 0xffULL); json &code = r["icmpCode"]; if (code.is_null()) { rule.v.icmp.code = 0; rule.v.icmp.flags = 0x00; } else { - rule.v.icmp.code = (uint8_t)(_jI(code,0ULL) & 0xffULL); + rule.v.icmp.code = (uint8_t)(OSUtils::jsonInt(code,0ULL) & 0xffULL); rule.v.icmp.flags = 0x01; } return true; } else if (t == "MATCH_IP_SOURCE_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE; - rule.v.port[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; } else if (t == "MATCH_IP_DEST_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE; - rule.v.port[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; } else if (t == "MATCH_CHARACTERISTICS") { rule.t |= ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; @@ -422,37 +375,37 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) return true; } else if (t == "MATCH_FRAME_SIZE_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE; - rule.v.frameSize[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); - rule.v.frameSize[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.frameSize[0]) & 0xffffULL); + rule.v.frameSize[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.frameSize[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.frameSize[0]) & 0xffffULL); return true; } else if (t == "MATCH_RANDOM") { rule.t |= ZT_NETWORK_RULE_MATCH_RANDOM; - rule.v.randomProbability = (uint32_t)(_jI(r["probability"],0ULL) & 0xffffffffULL); + rule.v.randomProbability = (uint32_t)(OSUtils::jsonInt(r["probability"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_DIFFERENCE") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE; - rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_AND") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND; - rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_OR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR; - rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_XOR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR; - rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_EQUAL") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_EQUAL; - rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } @@ -552,7 +505,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( return 404; _addMemberNonPersistedFields(member,OSUtils::now()); - responseBody = member.dump(2); + responseBody = OSUtils::jsonDump(member); responseContentType = "application/json"; return 200; @@ -565,9 +518,9 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( _db.filter(pfx,120000,[&responseBody](const std::string &n,const json &member) { if (member.size() > 0) { responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); - responseBody.append(_jS(member["id"],"")); + responseBody.append(OSUtils::jsonString(member["id"],"")); responseBody.append("\":"); - responseBody.append(_jS(member["revision"],"0")); + responseBody.append(OSUtils::jsonString(member["revision"],"0")); } return true; // never delete }); @@ -600,7 +553,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( _NetworkMemberInfo nmi; _getNetworkMemberInfo(now,nwid,nmi); _addNetworkNonPersistedFields(network,now,nmi); - responseBody = network.dump(2); + responseBody = OSUtils::jsonDump(network); responseContentType = "application/json"; return 200; @@ -655,7 +608,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json b; try { - b = json::parse(body); + b = OSUtils::jsonParse(body); if (!b.is_object()) { responseBody = "{ \"message\": \"body is not a JSON object\" }"; responseContentType = "application/json"; @@ -691,12 +644,12 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( _initMember(member); try { - if (b.count("activeBridge")) member["activeBridge"] = _jB(b["activeBridge"],false); - if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = _jB(b["noAutoAssignIps"],false); + if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false); + if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"],false); if (b.count("authorized")) { - const bool newAuth = _jB(b["authorized"],false); - if (newAuth != _jB(member["authorized"],false)) { + const bool newAuth = OSUtils::jsonBool(b["authorized"],false); + if (newAuth != OSUtils::jsonBool(member["authorized"],false)) { member["authorized"] = newAuth; member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now; @@ -732,7 +685,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( for(unsigned long i=0;i<tags.size();++i) { json &tag = tags[i]; if ((tag.is_array())&&(tag.size() == 2)) - mtags[_jI(tag[0],0ULL) & 0xffffffffULL] = _jI(tag[1],0ULL) & 0xffffffffULL; + mtags[OSUtils::jsonInt(tag[0],0ULL) & 0xffffffffULL] = OSUtils::jsonInt(tag[1],0ULL) & 0xffffffffULL; } json mtagsa = json::array(); for(std::map<uint64_t,uint64_t>::iterator t(mtags.begin());t!=mtags.end();++t) { @@ -750,7 +703,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (capabilities.is_array()) { json mcaps = json::array(); for(unsigned long i=0;i<capabilities.size();++i) { - mcaps.push_back(_jI(capabilities[i],0ULL)); + mcaps.push_back(OSUtils::jsonInt(capabilities[i],0ULL)); } std::sort(mcaps.begin(),mcaps.end()); mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end()); @@ -781,7 +734,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( // Add non-persisted fields member["clock"] = now; - responseBody = member.dump(2); + responseBody = OSUtils::jsonDump(member); responseContentType = "application/json"; return 200; } else if ((path.size() == 3)&&(path[2] == "test")) { @@ -809,7 +762,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } } } - test->reportAtEveryHop = (_jB(b["reportAtEveryHop"],true) ? 1 : 0); + test->reportAtEveryHop = (OSUtils::jsonBool(b["reportAtEveryHop"],true) ? 1 : 0); if (!test->hopCount) { ::free((void *)test); @@ -868,19 +821,19 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( _initNetwork(network); try { - if (b.count("name")) network["name"] = _jS(b["name"],""); - if (b.count("private")) network["private"] = _jB(b["private"],true); - if (b.count("enableBroadcast")) network["enableBroadcast"] = _jB(b["enableBroadcast"],false); - if (b.count("allowPassiveBridging")) network["allowPassiveBridging"] = _jB(b["allowPassiveBridging"],false); - if (b.count("multicastLimit")) network["multicastLimit"] = _jI(b["multicastLimit"],32ULL); + if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],""); + if (b.count("private")) network["private"] = OSUtils::jsonBool(b["private"],true); + if (b.count("enableBroadcast")) network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"],false); + if (b.count("allowPassiveBridging")) network["allowPassiveBridging"] = OSUtils::jsonBool(b["allowPassiveBridging"],false); + if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL); if (b.count("v4AssignMode")) { json nv4m; json &v4m = b["v4AssignMode"]; if (v4m.is_string()) { // backward compatibility - nv4m["zt"] = (_jS(v4m,"") == "zt"); + nv4m["zt"] = (OSUtils::jsonString(v4m,"") == "zt"); } else if (v4m.is_object()) { - nv4m["zt"] = _jB(v4m["zt"],false); + nv4m["zt"] = OSUtils::jsonBool(v4m["zt"],false); } else nv4m["zt"] = false; network["v4AssignMode"] = nv4m; } @@ -890,7 +843,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &v6m = b["v6AssignMode"]; if (!nv6m.is_object()) nv6m = json::object(); if (v6m.is_string()) { // backward compatibility - std::vector<std::string> v6ms(OSUtils::split(_jS(v6m,"").c_str(),",","","")); + std::vector<std::string> v6ms(OSUtils::split(OSUtils::jsonString(v6m,"").c_str(),",","","")); std::sort(v6ms.begin(),v6ms.end()); v6ms.erase(std::unique(v6ms.begin(),v6ms.end()),v6ms.end()); nv6m["rfc4193"] = false; @@ -905,9 +858,9 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( nv6m["6plane"] = true; } } else if (v6m.is_object()) { - if (v6m.count("rfc4193")) nv6m["rfc4193"] = _jB(v6m["rfc4193"],false); - if (v6m.count("zt")) nv6m["zt"] = _jB(v6m["zt"],false); - if (v6m.count("6plane")) nv6m["6plane"] = _jB(v6m["6plane"],false); + if (v6m.count("rfc4193")) nv6m["rfc4193"] = OSUtils::jsonBool(v6m["rfc4193"],false); + if (v6m.count("zt")) nv6m["zt"] = OSUtils::jsonBool(v6m["zt"],false); + if (v6m.count("6plane")) nv6m["6plane"] = OSUtils::jsonBool(v6m["6plane"],false); } else { nv6m["rfc4193"] = false; nv6m["zt"] = false; @@ -951,8 +904,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( for(unsigned long i=0;i<ipp.size();++i) { json &ip = ipp[i]; if ((ip.is_object())&&(ip.count("ipRangeStart"))&&(ip.count("ipRangeEnd"))) { - InetAddress f(_jS(ip["ipRangeStart"],"")); - InetAddress t(_jS(ip["ipRangeEnd"],"")); + InetAddress f(OSUtils::jsonString(ip["ipRangeStart"],"")); + InetAddress t(OSUtils::jsonString(ip["ipRangeEnd"],"")); if ( ((f.ss_family == AF_INET)||(f.ss_family == AF_INET6)) && (f.ss_family == t.ss_family) ) { json tmp = json::object(); tmp["ipRangeStart"] = f.toIpString(); @@ -992,8 +945,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (tstr.length() > 0) { json t = json::object(); t["token"] = tstr; - t["expires"] = _jI(token["expires"],0ULL); - t["maxUsesPerMember"] = _jI(token["maxUsesPerMember"],0ULL); + t["expires"] = OSUtils::jsonInt(token["expires"],0ULL); + t["maxUsesPerMember"] = OSUtils::jsonInt(token["maxUsesPerMember"],0ULL); nat.push_back(t); } } @@ -1010,7 +963,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &cap = capabilities[i]; if (cap.is_object()) { json ncap = json::object(); - const uint64_t capId = _jI(cap["id"],0ULL); + const uint64_t capId = OSUtils::jsonInt(cap["id"],0ULL); ncap["id"] = capId; json &rules = cap["rules"]; @@ -1065,7 +1018,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( _getNetworkMemberInfo(now,nwid,nmi); _addNetworkNonPersistedFields(network,now,nmi); - responseBody = network.dump(2); + responseBody = OSUtils::jsonDump(network); responseContentType = "application/json"; return 200; } // else 404 @@ -1113,7 +1066,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( if (!member.size()) return 404; - responseBody = member.dump(2); + responseBody = OSUtils::jsonDump(member); responseContentType = "application/json"; return 200; } @@ -1128,7 +1081,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( Mutex::Lock _l2(_nmiCache_m); _nmiCache.erase(nwid); - responseBody = network.dump(2); + responseBody = OSUtils::jsonDump(network); responseContentType = "application/json"; return 200; } @@ -1254,7 +1207,7 @@ void EmbeddedNetworkController::_request( _initMember(member); { - std::string haveIdStr(_jS(member["identity"],"")); + std::string haveIdStr(OSUtils::jsonString(member["identity"],"")); if (haveIdStr.length() > 0) { // If we already know this member's identity perform a full compare. This prevents // a "collision" from being able to auth onto our network in place of an already @@ -1283,9 +1236,9 @@ void EmbeddedNetworkController::_request( const char *authorizedBy = (const char *)0; bool autoAuthorized = false; json autoAuthCredentialType,autoAuthCredential; - if (_jB(member["authorized"],false)) { + if (OSUtils::jsonBool(member["authorized"],false)) { authorizedBy = "memberIsAuthorized"; - } else if (!_jB(network["private"],true)) { + } else if (!OSUtils::jsonBool(network["private"],true)) { authorizedBy = "networkIsPublic"; json &ahist = member["authHistory"]; if ((!ahist.is_array())||(ahist.size() == 0)) @@ -1304,9 +1257,9 @@ void EmbeddedNetworkController::_request( for(unsigned long i=0;i<authTokens.size();++i) { json &token = authTokens[i]; if (token.is_object()) { - const uint64_t expires = _jI(token["expires"],0ULL); - const uint64_t maxUses = _jI(token["maxUsesPerMember"],0ULL); - std::string tstr = _jS(token["token"],""); + const uint64_t expires = OSUtils::jsonInt(token["expires"],0ULL); + const uint64_t maxUses = OSUtils::jsonInt(token["maxUsesPerMember"],0ULL); + std::string tstr = OSUtils::jsonString(token["token"],""); if (((expires == 0ULL)||(expires > now))&&(tstr == presentedToken)) { bool usable = (maxUses == 0); @@ -1316,7 +1269,7 @@ void EmbeddedNetworkController::_request( if (ahist.is_array()) { for(unsigned long j=0;j<ahist.size();++j) { json &ah = ahist[j]; - if ((_jS(ah["ct"],"") == "token")&&(_jS(ah["c"],"") == tstr)&&(_jB(ah["a"],false))) + if ((OSUtils::jsonString(ah["ct"],"") == "token")&&(OSUtils::jsonString(ah["c"],"") == tstr)&&(OSUtils::jsonBool(ah["a"],false))) ++useCount; } } @@ -1415,15 +1368,15 @@ void EmbeddedNetworkController::_request( } nc.networkId = nwid; - nc.type = _jB(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; + nc.type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; nc.timestamp = now; nc.credentialTimeMaxDelta = credentialtmd; - nc.revision = _jI(network["revision"],0ULL); + nc.revision = OSUtils::jsonInt(network["revision"],0ULL); nc.issuedTo = identity.address(); - if (_jB(network["enableBroadcast"],true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; - if (_jB(network["allowPassiveBridging"],false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; - Utils::scopy(nc.name,sizeof(nc.name),_jS(network["name"],"").c_str()); - nc.multicastLimit = (unsigned int)_jI(network["multicastLimit"],32ULL); + if (OSUtils::jsonBool(network["enableBroadcast"],true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; + if (OSUtils::jsonBool(network["allowPassiveBridging"],false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; + Utils::scopy(nc.name,sizeof(nc.name),OSUtils::jsonString(network["name"],"").c_str()); + nc.multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL); for(std::set<Address>::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) { nc.addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); @@ -1459,11 +1412,11 @@ void EmbeddedNetworkController::_request( for(unsigned long i=0;i<capabilities.size();++i) { json &cap = capabilities[i]; if (cap.is_object()) - capsById[_jI(cap["id"],0ULL) & 0xffffffffULL] = ∩ + capsById[OSUtils::jsonInt(cap["id"],0ULL) & 0xffffffffULL] = ∩ } for(unsigned long i=0;i<memberCapabilities.size();++i) { - const uint64_t capId = _jI(memberCapabilities[i],0ULL) & 0xffffffffULL; + const uint64_t capId = OSUtils::jsonInt(memberCapabilities[i],0ULL) & 0xffffffffULL; json *cap = capsById[capId]; if ((cap->is_object())&&(cap->size() > 0)) { ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; @@ -1491,7 +1444,7 @@ void EmbeddedNetworkController::_request( for(unsigned long i=0;i<memberTags.size();++i) { json &t = memberTags[i]; if ((t.is_array())&&(t.size() == 2)) - tagsById[(uint32_t)(_jI(t[0],0ULL) & 0xffffffffULL)] = (uint32_t)(_jI(t[1],0ULL) & 0xffffffffULL); + tagsById[(uint32_t)(OSUtils::jsonInt(t[0],0ULL) & 0xffffffffULL)] = (uint32_t)(OSUtils::jsonInt(t[1],0ULL) & 0xffffffffULL); } for(std::map< uint32_t,uint32_t >::const_iterator t(tagsById.begin());t!=tagsById.end();++t) { if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) @@ -1525,14 +1478,14 @@ void EmbeddedNetworkController::_request( } } - const bool noAutoAssignIps = _jB(member["noAutoAssignIps"],false); + const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false); if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) { - if ((_jB(v6AssignMode["rfc4193"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()); nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; } - if ((_jB(v6AssignMode["6plane"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + if ((OSUtils::jsonBool(v6AssignMode["6plane"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt()); nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; } @@ -1572,12 +1525,12 @@ void EmbeddedNetworkController::_request( ipAssignments = json::array(); } - if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(_jB(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) { + if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(OSUtils::jsonBool(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) { for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv6AutoAssignment));++p) { json &pool = ipAssignmentPools[p]; if (pool.is_object()) { - InetAddress ipRangeStart(_jS(pool["ipRangeStart"],"")); - InetAddress ipRangeEnd(_jS(pool["ipRangeEnd"],"")); + InetAddress ipRangeStart(OSUtils::jsonString(pool["ipRangeStart"],"")); + InetAddress ipRangeEnd(OSUtils::jsonString(pool["ipRangeEnd"],"")); if ( (ipRangeStart.ss_family == AF_INET6) && (ipRangeEnd.ss_family == AF_INET6) ) { uint64_t s[2],e[2],x[2],xx[2]; memcpy(s,ipRangeStart.rawIpData(),16); @@ -1632,12 +1585,12 @@ void EmbeddedNetworkController::_request( } } - if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(_jB(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) { + if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(OSUtils::jsonBool(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) { for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv4AutoAssignment));++p) { json &pool = ipAssignmentPools[p]; if (pool.is_object()) { - InetAddress ipRangeStartIA(_jS(pool["ipRangeStart"],"")); - InetAddress ipRangeEndIA(_jS(pool["ipRangeEnd"],"")); + InetAddress ipRangeStartIA(OSUtils::jsonString(pool["ipRangeStart"],"")); + InetAddress ipRangeEndIA(OSUtils::jsonString(pool["ipRangeEnd"],"")); if ( (ipRangeStartIA.ss_family == AF_INET) && (ipRangeEndIA.ss_family == AF_INET) ) { uint32_t ipRangeStart = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeStartIA)->sin_addr.s_addr)); uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeEndIA)->sin_addr.s_addr)); @@ -1722,7 +1675,7 @@ void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid Mutex::Lock _l(_db_m); _db.filter(pfx,120000,[&nmi,&now](const std::string &n,const json &member) { try { - if (_jB(member["authorized"],false)) { + if (OSUtils::jsonBool(member["authorized"],false)) { ++nmi.authorizedMemberCount; if (member.count("recentLog")) { @@ -1730,28 +1683,28 @@ void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid if ((mlog.is_array())&&(mlog.size() > 0)) { const json &mlog1 = mlog[0]; if (mlog1.is_object()) { - if ((now - _jI(mlog1["ts"],0ULL)) < ZT_NETCONF_NODE_ACTIVE_THRESHOLD) + if ((now - OSUtils::jsonInt(mlog1["ts"],0ULL)) < ZT_NETCONF_NODE_ACTIVE_THRESHOLD) ++nmi.activeMemberCount; } } } - if (_jB(member["activeBridge"],false)) { - nmi.activeBridges.insert(_jS(member["id"],"0000000000")); + if (OSUtils::jsonBool(member["activeBridge"],false)) { + nmi.activeBridges.insert(OSUtils::jsonString(member["id"],"0000000000")); } if (member.count("ipAssignments")) { const json &mips = member["ipAssignments"]; if (mips.is_array()) { for(unsigned long i=0;i<mips.size();++i) { - InetAddress mip(_jS(mips[i],"")); + InetAddress mip(OSUtils::jsonString(mips[i],"")); if ((mip.ss_family == AF_INET)||(mip.ss_family == AF_INET6)) nmi.allocatedIps.insert(mip); } } } } else { - nmi.mostRecentDeauthTime = std::max(nmi.mostRecentDeauthTime,_jI(member["lastDeauthorizedTime"],0ULL)); + nmi.mostRecentDeauthTime = std::max(nmi.mostRecentDeauthTime,OSUtils::jsonInt(member["lastDeauthorizedTime"],0ULL)); } } catch ( ... ) {} return true; diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 37c067ff..bd3a6666 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -165,6 +165,7 @@ private: if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}}; if (!network.count("authTokens")) network["authTokens"] = nlohmann::json::array(); if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array(); + if (!network.count("routes")) network["routes"] = nlohmann::json::array(); if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array(); if (!network.count("rules")) { // If unspecified, rules are set to allow anything and behave like a flat L2 segment diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp index dfea9dd1..044f791c 100644 --- a/controller/JSONDB.cpp +++ b/controller/JSONDB.cpp @@ -31,7 +31,7 @@ bool JSONDB::put(const std::string &n,const nlohmann::json &obj) if (!path.length()) return false; - const std::string buf(obj.dump(2)); + const std::string buf(OSUtils::jsonDump(obj)); if (!OSUtils::writeFile(path.c_str(),buf)) return false; @@ -69,7 +69,7 @@ const nlohmann::json &JSONDB::get(const std::string &n,unsigned long maxSinceChe if (e->second.lastModifiedOnDisk != lm) { if (OSUtils::readFile(path.c_str(),buf)) { try { - e->second.obj = nlohmann::json::parse(buf); + e->second.obj = OSUtils::jsonParse(buf); e->second.lastModifiedOnDisk = lm; // don't update these if there is a parse error -- try again and again ASAP e->second.lastCheck = now; @@ -91,7 +91,7 @@ const nlohmann::json &JSONDB::get(const std::string &n,unsigned long maxSinceChe const uint64_t lm = OSUtils::getLastModified(path.c_str()); _E &e2 = _db[n]; try { - e2.obj = nlohmann::json::parse(buf); + e2.obj = OSUtils::jsonParse(buf); } catch ( ... ) { e2.obj = _EMPTY_JSON; buf = "{}"; @@ -161,7 +161,7 @@ std::string JSONDB::_genPath(const std::string &n,bool create) std::string p(_basePath); if (create) OSUtils::mkdir(p.c_str()); - for(unsigned long i=0,j=pt.size()-1;i<j;++i) { + for(unsigned long i=0,j=(unsigned long)(pt.size()-1);i<j;++i) { p.push_back(ZT_PATH_SEPARATOR); p.append(pt[i]); if (create) OSUtils::mkdir(p.c_str()); diff --git a/controller/README.md b/controller/README.md index 805641d9..edbdf804 100644 --- a/controller/README.md +++ b/controller/README.md @@ -15,15 +15,9 @@ The migration tool is written in nodeJS and can be used like this: cd migrate-sqlite npm install - node migrate-sqlite.js <path to ZeroTier working directory> + node migrate.js </path/to/controller.db> </path/to/controller.d> -You may need to `sudo node ...` if the ZeroTier working directory is owned by root. - -This code will dump the contents of any `controller.db` in the ZeroTier working directory and recreate its data in the form of JSON objects under `controller.d`. The old `controller.db` will then be renamed to `controller.db.migrated` and the controller will start normally. - -After migrating make sure that the contents of `controller.d` are owned and writable by the user that will be running the ZeroTier controller process! (Usually this is root but controllers that don't also join networks are sometimes run as unprivileged users.) - -If you don't have nodeJS on the machine running ZeroTier it is perfectly fine to just copy `controller.db` to a directory on another machine and run the migration tool there. After running your migration the contents of `controller.d` can be tar'd up and copied back over to the controller. Just remember to rename or remove `controller.db` on the controller machine afterwords so the controller will start. +Very old versions of nodeJS may have issues. We tested it with version 7. ### Scalability and Reliability diff --git a/controller/migrate-sqlite/migrate.js b/controller/migrate-sqlite/migrate.js new file mode 100644 index 00000000..ac9678a7 --- /dev/null +++ b/controller/migrate-sqlite/migrate.js @@ -0,0 +1,320 @@ +'use strict'; + +var sqlite3 = require('sqlite3').verbose(); +var fs = require('fs'); +var async = require('async'); + +function blobToIPv4(b) +{ + if (!b) + return null; + if (b.length !== 16) + return null; + return b.readUInt8(12).toString()+'.'+b.readUInt8(13).toString()+'.'+b.readUInt8(14).toString()+'.'+b.readUInt8(15).toString(); +} +function blobToIPv6(b) +{ + if (!b) + return null; + if (b.length !== 16) + return null; + var s = ''; + for(var i=0;i<16;++i) { + var x = b.readUInt8(i).toString(16); + if (x.length === 1) + s += '0'; + s += x; + if ((((i+1) & 1) === 0)&&(i !== 15)) + s += ':'; + } + return s; +} + +if (process.argv.length !== 4) { + console.log('ZeroTier Old Sqlite3 Controller DB Migration Utility'); + console.log('(c)2017 ZeroTier, Inc. [GPL3]'); + console.log(''); + console.log('Usage: node migrate.js </path/to/controller.db> </path/to/controller.d>'); + console.log(''); + console.log('The first argument must be the path to the old Sqlite3 controller.db'); + console.log('file. The second must be the path to the EMPTY controller.d database'); + console.log('directory for a new (1.1.17 or newer) controller. If this path does'); + console.log('not exist it will be created.'); + console.log(''); + console.log('WARNING: this will ONLY work correctly on a 1.1.14 controller database.'); + console.log('If your controller is old you should first upgrade to 1.1.14 and run the'); + console.log('controller so that it will brings its Sqlite3 database up to the latest'); + console.log('version before running this migration.'); + console.log(''); + process.exit(1); +} + +var oldDbPath = process.argv[2]; +var newDbPath = process.argv[3]; + +console.log('Starting migrate of "'+oldDbPath+'" to "'+newDbPath+'"...'); +console.log(''); + +var old = new sqlite3.Database(oldDbPath); + +var networks = {}; + +var nodeIdentities = {}; +var networkCount = 0; +var memberCount = 0; +var routeCount = 0; +var ipAssignmentPoolCount = 0; +var ipAssignmentCount = 0; +var ruleCount = 0; +var oldSchemaVersion = -1; + +async.series([function(nextStep) { + + old.each('SELECT v from Config WHERE k = \'schemaVersion\'',function(err,row) { + oldSchemaVersion = parseInt(row.v)||-1; + },nextStep); + +},function(nextStep) { + + if (oldSchemaVersion !== 4) { + console.log('FATAL: this MUST be run on a 1.1.14 controller.db! Upgrade your old'); + console.log('controller to 1.1.14 first and run it once to bring its DB up to date.'); + return process.exit(1); + } + + console.log('Reading networks...'); + old.each('SELECT * FROM Network',function(err,row) { + if ((typeof row.id === 'string')&&(row.id.length === 16)) { + var flags = parseInt(row.flags)||0; + networks[row.id] = { + id: row.id, + nwid: row.id, + objtype: 'network', + authTokens: [], + capabilities: [], + creationTime: parseInt(row.creationTime)||0, + enableBroadcast: !!row.enableBroadcast, + ipAssignmentPools: [], + lastModified: Date.now(), + multicastLimit: row.multicastLimit||32, + name: row.name||'', + private: !!row.private, + revision: parseInt(row.revision)||1, + rules: [{ 'type': 'ACTION_ACCEPT' }], // populated later if there are defined rules, otherwise default is allow all + routes: [], + v4AssignMode: { + 'zt': ((flags & 1) !== 0) + }, + v6AssignMode: { + '6plane': ((flags & 4) !== 0), + 'rfc4193': ((flags & 2) !== 0), + 'zt': ((flags & 8) !== 0) + }, + _members: {} // temporary + }; + ++networkCount; + //console.log(networks[row.id]); + } + },nextStep); + +},function(nextStep) { + + console.log(' '+networkCount+' networks.'); + console.log('Reading network route definitions...'); + old.each('SELECT * from Route WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) { + var network = networks[row.networkId]; + if (network) { + var rt = { + target: (((row.ipVersion == 4) ? blobToIPv4(row.target) : blobToIPv6(row.target))+'/'+row.targetNetmaskBits), + via: ((row.via) ? ((row.ipVersion == 4) ? blobToIPv4(row.via) : blobToIPv6(row.via)) : null) + }; + network.routes.push(rt); + ++routeCount; + } + },nextStep); + +},function(nextStep) { + + console.log(' '+routeCount+' routes in '+networkCount+' networks.'); + console.log('Reading IP assignment pools...'); + old.each('SELECT * FROM IpAssignmentPool WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) { + var network = networks[row.networkId]; + if (network) { + var p = { + ipRangeStart: ((row.ipVersion == 4) ? blobToIPv4(row.ipRangeStart) : blobToIPv6(row.ipRangeStart)), + ipRangeEnd: ((row.ipVersion == 4) ? blobToIPv4(row.ipRangeEnd) : blobToIPv6(row.ipRangeEnd)) + }; + network.ipAssignmentPools.push(p); + ++ipAssignmentPoolCount; + } + },nextStep); + +},function(nextStep) { + + console.log(' '+ipAssignmentPoolCount+' IP assignment pools in '+networkCount+' networks.'); + console.log('Reading known node identities...'); + old.each('SELECT * FROM Node',function(err,row) { + nodeIdentities[row.id] = row.identity; + },nextStep); + +},function(nextStep) { + + console.log(' '+Object.keys(nodeIdentities).length+' known identities.'); + console.log('Reading network members...'); + old.each('SELECT * FROM Member',function(err,row) { + var network = networks[row.networkId]; + if (network) { + network._members[row.nodeId] = { + id: row.nodeId, + address: row.nodeId, + objtype: 'member', + authorized: !!row.authorized, + activeBridge: !!row.activeBridge, + authHistory: [], + capabilities: [], + creationTime: 0, + identity: nodeIdentities[row.nodeId]||null, + ipAssignments: [], + lastAuthorizedTime: (row.authorized) ? Date.now() : 0, + lastDeauthorizedTime: (row.authorized) ? 0 : Date.now(), + lastModified: Date.now(), + lastRequestMetaData: '', + noAutoAssignIps: false, + nwid: row.networkId, + revision: parseInt(row.memberRevision)||1, + tags: [], + recentLog: [] + }; + ++memberCount; + //console.log(network._members[row.nodeId]); + } + },nextStep); + +},function(nextStep) { + + console.log(' '+memberCount+' members of '+networkCount+' networks.'); + console.log('Reading static IP assignments...'); + old.each('SELECT * FROM IpAssignment WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) { + var network = networks[row.networkId]; + if (network) { + var member = network._members[row.nodeId]; + if ((member)&&((member.authorized)||(!network['private']))) { // don't mirror assignments to unauthorized members to avoid conflicts + if (row.ipVersion == 4) { + member.ipAssignments.push(blobToIPv4(row.ip)); + ++ipAssignmentCount; + } else if (row.ipVersion == 6) { + member.ipAssignments.push(blobToIPv6(row.ip)); + ++ipAssignmentCount; + } + } + } + },nextStep); + +},function(nextStep) { + + // Old versions only supported Ethertype whitelisting, so that's + // all we mirror forward. The other fields were always unused. + + console.log(' '+ipAssignmentCount+' IP assignments for '+memberCount+' authorized members of '+networkCount+' networks.'); + console.log('Reading allowed Ethernet types (old basic rules)...'); + var etherTypesByNetwork = {}; + old.each('SELECT DISTINCT networkId,ruleNo,etherType FROM Rule WHERE "action" = \'accept\'',function(err,row) { + if (row.networkId in networks) { + var et = parseInt(row.etherType)||0; + var ets = etherTypesByNetwork[row.networkId]; + if (!ets) + etherTypesByNetwork[row.networkId] = [ et ]; + else ets.push(et); + } + },function(err) { + if (err) return nextStep(err); + for(var nwid in etherTypesByNetwork) { + var ets = etherTypesByNetwork[nwid].sort(); + var network = networks[nwid]; + if (network) { + var rules = []; + if (ets.indexOf(0) >= 0) { + // If 0 is in the list, all Ethernet types are allowed so we accept all. + rules.push({ 'type': 'ACTION_ACCEPT' }); + } else { + // Otherwise we whitelist. + for(var i=0;i<ets.length;++i) { + rules.push({ + 'etherType': ets[i], + 'not': true, + 'or': false, + 'type': 'MATCH_ETHERTYPE' + }); + } + rules.push({ 'type': 'ACTION_DROP' }); + rules.push({ 'type': 'ACTION_ACCEPT' }); + } + network.rules = rules; + ++ruleCount; + } + } + return nextStep(null); + }); + +}],function(err) { + + if (err) { + console.log('FATAL: '+err.toString()); + return process.exit(1); + } + + console.log(' '+ruleCount+' ethernet type whitelists converted to new format rules.'); + old.close(); + console.log('Done reading and converting Sqlite3 database! Writing JSONDB files...'); + + try { + fs.mkdirSync(newDbPath,0o700); + } catch (e) {} + var nwBase = newDbPath+'/network'; + try { + fs.mkdirSync(nwBase,0o700); + } catch (e) {} + nwBase = nwBase + '/'; + var nwids = Object.keys(networks).sort(); + var fileCount = 0; + for(var ni=0;ni<nwids.length;++ni) { + var network = networks[nwids[ni]]; + + var mids = Object.keys(network._members).sort(); + if (mids.length > 0) { + try { + fs.mkdirSync(nwBase+network.id); + } catch (e) {} + var mbase = nwBase+network.id+'/member'; + try { + fs.mkdirSync(mbase,0o700); + } catch (e) {} + mbase = mbase + '/'; + + for(var mi=0;mi<mids.length;++mi) { + var member = network._members[mids[mi]]; + fs.writeFileSync(mbase+member.id+'.json',JSON.stringify(member,null,1),{ mode: 0o600 }); + ++fileCount; + //console.log(mbase+member.id+'.json'); + } + } + + delete network._members; // temporary field, not part of actual JSONDB, so don't write + fs.writeFileSync(nwBase+network.id+'.json',JSON.stringify(network,null,1),{ mode: 0o600 }); + ++fileCount; + //console.log(nwBase+network.id+'.json'); + } + + console.log(''); + console.log('SUCCESS! Wrote '+fileCount+' JSONDB files.'); + + console.log(''); + console.log('You should still inspect the new DB before going live. Also be sure'); + console.log('to "chown -R" and "chgrp -R" the new DB to the user and group under'); + console.log('which the ZeroTier One instance acting as controller will be running.'); + console.log('The controller must be able to read and write the DB, of course.'); + console.log(''); + console.log('Have fun!'); + + return process.exit(0); +}); diff --git a/controller/migrate-sqlite/package.json b/controller/migrate-sqlite/package.json new file mode 100644 index 00000000..0dac008f --- /dev/null +++ b/controller/migrate-sqlite/package.json @@ -0,0 +1,15 @@ +{ + "name": "migrate-sqlite", + "version": "1.0.0", + "description": "Migrate old SQLite to new JSON filesystem DB for ZeroTier network controller", + "main": "migrate.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Adam Ierymenko <adam.ierymenko@zerotier.com>", + "license": "GPL-3.0", + "dependencies": { + "async": "^2.1.4", + "sqlite3": "^3.1.8" + } +} diff --git a/ext/installfiles/mac-update/updater.tmpl.sh b/ext/installfiles/mac-update/updater.tmpl.sh new file mode 100644 index 00000000..0b07f6d9 --- /dev/null +++ b/ext/installfiles/mac-update/updater.tmpl.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +export PATH=/bin:/usr/bin:/sbin:/usr/sbin +shopt -s expand_aliases + +if [ "$UID" -ne 0 ]; then + echo '*** Auto-updater must be run as root.' + exit 1 +fi + +scriptPath="`dirname "$0"`/`basename "$0"`" +if [ ! -s "$scriptPath" ]; then + scriptPath="$0" + if [ ! -s "$scriptPath" ]; then + echo "*** Auto-updater cannot determine its own path; $scriptPath is not readable." + exit 2 + fi +fi + +endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1` +if [ "$endMarkerIndex" -le 100 ]; then + echo 'Internal error: unable to find end of script / start of binary data marker.' + exit 2 +fi +blobStart=`expr $endMarkerIndex + 17` +if [ "$blobStart" -le "$endMarkerIndex" ]; then + echo 'Internal error: unable to find end of script / start of binary data marker.' + exit 2 +fi + +rm -f /tmp/ZeroTierOne-update.pkg +tail -c +$blobStart "$scriptPath" >/tmp/ZeroTierOne-update.pkg +chmod 0600 /tmp/ZeroTierOne-update.pkg + +if [ -s /tmp/ZeroTierOne-update.pkg ]; then + rm -f '/Library/Application Support/ZeroTier/One/latest-update.exe' '/Library/Application Support/ZeroTier/One/latest-update.json' /tmp/ZeroTierOne-update.log + installer -verbose -pkg /tmp/ZeroTierOne-update.pkg -target / >/tmp/ZeroTierOne-update.log 2>&1 + rm -f /tmp/ZeroTierOne-update.pkg + exit 0 +else + echo '*** Error self-unpacking update!' + exit 3 +fi + +# Do not remove the last line or add a carriage return to it! The installer +# looks for an unterminated line beginning with 16 #'s in itself to find +# the binary blob data, which is appended after it. + +################
\ No newline at end of file diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip index a3811b80..3fb6d5c6 100644 --- a/ext/installfiles/windows/ZeroTier One.aip +++ b/ext/installfiles/windows/ZeroTier One.aip @@ -7,6 +7,7 @@ <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/> <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/> <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/> + <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/> <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/> <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/> <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/> @@ -26,19 +27,19 @@ <ROW Property="CTRLS" Value="2"/> <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/> <ROW Property="Manufacturer" Value="ZeroTier, Inc."/> - <ROW Property="ProductCode" Value="1033:{71692739-29E8-49D1-B1EA-BA701E8A8B7D} " Type="16"/> + <ROW Property="ProductCode" Value="1033:{EDA42EDB-45DE-446E-AB3A-31D209558255} " Type="16"/> <ROW Property="ProductLanguage" Value="1033"/> <ROW Property="ProductName" Value="ZeroTier One"/> <ROW Property="ProductVersion" Value="1.1.17" Type="32"/> <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/> <ROW Property="RUNAPPLICATION" Value="1" Type="4"/> - <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/> + <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/> <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/> - <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/> - <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/> + <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/> + <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/> <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/> - <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/> - <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/> + <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/> + <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/> <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/> <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/> <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/> @@ -60,6 +61,7 @@ <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent"> <ROW Component="AI_CustomARPName" ComponentId="{449F1B5F-F32A-4290-B892-F1B63EB68D29}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/> <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/> + <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/> <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/> <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/> <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/> @@ -73,7 +75,7 @@ <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent"> - <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/> + <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/> <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent"> @@ -92,6 +94,7 @@ </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent"> <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/> + <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]"/> <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent"> @@ -130,6 +133,10 @@ <ROW Fragment="WelcomeDlg.aip" Path="<AI_THEMES>classic\fragments\WelcomeDlg.aip"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent"> + <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/> + <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/> + <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/> + <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/> <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: "[1]"" DescriptionLocId="ActionText.Description.AI_FwConfig" TemplateLocId="ActionText.Template.AI_FwConfig"/> <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/> <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: "[1]"" DescriptionLocId="ActionText.Description.AI_FwRemove" TemplateLocId="ActionText.Template.AI_FwRemove"/> @@ -146,14 +153,19 @@ <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." Template="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" TemplateLocId="ActionText.Template.AI_XmlRollback"/> <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/> </COMPONENT> + <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent"> + <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/> + <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/> + </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent"> <ROW Name="ExternalUICleaner.dll" SourcePath="<AI_CUSTACTS>ExternalUICleaner.dll"/> <ROW Name="NetFirewall.dll" SourcePath="<AI_CUSTACTS>NetFirewall.dll"/> - <ROW Name="ShortcutFlags.dll" SourcePath="<AI_CUSTACTS>ShortcutFlags.dll"/> + <ROW Name="Prereq.dll" SourcePath="<AI_CUSTACTS>Prereq.dll"/> <ROW Name="SoftwareDetector.dll" SourcePath="<AI_CUSTACTS>SoftwareDetector.dll"/> <ROW Name="TxtUpdater.dll" SourcePath="<AI_CUSTACTS>TxtUpdater.dll"/> <ROW Name="aicustact.dll" SourcePath="<AI_CUSTACTS>aicustact.dll"/> <ROW Name="chainersupport.dll" SourcePath="<AI_CUSTACTS>chainersupport.dll"/> + <ROW Name="lzmaextractor.dll" SourcePath="<AI_CUSTACTS>lzmaextractor.dll"/> <ROW Name="msichainer.exe" SourcePath="<AI_CUSTACTS>msichainer.exe"/> <ROW Name="xmlCfg.dll" SourcePath="<AI_CUSTACTS>xmlCfg.dll"/> </COMPONENT> @@ -194,17 +206,27 @@ <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent"> - <ROW Action="AI_ApplyShortcutFlags" Type="3073" Source="ShortcutFlags.dll" Target="UpdateShortcutFlags" WithoutSeq="true"/> <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/> <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/> <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/> <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/> <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/> <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/> + <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/> + <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/> <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/> + <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/> + <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/> + <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/> + <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/> <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/> <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/> <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/> + <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/> + <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/> + <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/> + <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/> + <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/> <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/> <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/> <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/> @@ -214,10 +236,7 @@ <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/> <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/> <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/> - <ROW Action="AI_PinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="PinShortcuts"/> - <ROW Action="AI_PinToTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="PinToTaskbar" WithoutSeq="true"/> <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/> - <ROW Action="AI_PrepareShortcutFlags" Type="1" Source="ShortcutFlags.dll" Target="PrepareActionData"/> <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/> <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/> <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/> @@ -229,8 +248,6 @@ <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/> <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/> <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/> - <ROW Action="AI_UnpinFromTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="UnpinFromTaskbar" WithoutSeq="true"/> - <ROW Action="AI_UnpinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="UnpinShortcuts"/> <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/> <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/> <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/> @@ -242,6 +259,7 @@ <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/> <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/> <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/> + <ROW Action="TerminateUI" Type="1" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent"> <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi >= "4.05"" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/> @@ -258,7 +276,6 @@ <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1502"/> <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE="No" AND (Not Installed)" Sequence="1399"/> <ROW Action="AI_ResolveKnownFolders" Sequence="51"/> - <ROW Action="AI_PrepareShortcutFlags" Condition="(VersionNT > 501) AND ((NOT Installed) OR (Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove")))" Sequence="4501"/> <ROW Action="AI_XmlInstall" Condition="(REMOVE <> "ALL")" Sequence="5103"/> <ROW Action="AI_DATA_SETTER" Condition="(REMOVE <> "ALL")" Sequence="5102"/> <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/> @@ -266,17 +283,29 @@ <ROW Action="InstallFinalize" Sequence="6597" SeqType="0" MsiKey="InstallFinalize"/> <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE="ALL") AND ((VersionNT > 500) OR((VersionNT = 500) AND (ServicePackLevel >= 4)))" Sequence="1501"/> <ROW Action="AI_GetArpIconPath" Sequence="1401"/> - <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = "ALL" OR AI_INSTALL_MODE = "Remove" ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1601"/> - <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = "ALL" OR AI_INSTALL_MODE = "Remove" ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1602"/> + <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = "ALL" OR AI_INSTALL_MODE = "Remove" ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/> + <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = "ALL" OR AI_INSTALL_MODE = "Remove" ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/> <ROW Action="AI_PrepareChainers" Condition="VersionMsi >= "4.05"" Sequence="5851"/> <ROW Action="AI_FwInstall" Condition="(VersionNT >= 501) AND (REMOVE <> "ALL")" Sequence="5802"/> <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT >= 501) AND (REMOVE <> "ALL")" Sequence="5801"/> <ROW Action="AI_FwUninstall" Condition="(VersionNT >= 501) AND (REMOVE="ALL")" Sequence="1702"/> <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT >= 501) AND (REMOVE="ALL")" Sequence="1701"/> - <ROW Action="AI_DetectSoftware" Sequence="101"/> - <ROW Action="AI_PinShortcuts" Condition="(VersionNT > 600) AND ((NOT Installed) OR (Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove")))" Sequence="4502"/> - <ROW Action="AI_UnpinShortcuts" Condition="(VersionNT > 600) AND (REMOVE = "ALL")" Sequence="3199"/> + <ROW Action="AI_DetectSoftware" Sequence="102"/> <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/> + <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/> + <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101" Builds="ExeBuild"/> + <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/> + <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/> + <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/> + <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/> + <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/> + <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/> + <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR="" AND Installed AND (REMOVE<>"ALL") AND (AI_INSTALL_MODE<>"Remove") AND (NOT PATCH)" Sequence="6596" Builds="ExeBuild"/> + <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/> + <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/> + <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/> + <ROW Action="TerminateUI" Sequence="1602"/> + <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent"> <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=""" Sequence="749"/> @@ -290,12 +319,18 @@ <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent"> <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 <> 502) OR (((VersionNT64 = 502) AND (ServicePackLevel >= 1)) OR (MsiNTProductType <> 1))) AND ((VersionNT64 <> 502) OR (((VersionNT64 = 502) AND (ServicePackLevel <> 1)) OR (MsiNTProductType <> 1))) AND ((VersionNT64 <> 502) OR (((VersionNT64 = 502) AND (ServicePackLevel <> 2)) OR (MsiNTProductType <> 1))) AND ((VersionNT64 <> 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel >= 1)))) AND ((VersionNT64 <> 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel <> 1)))) AND ((VersionNT64 <> 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel <> 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/> <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT <> 500) OR ((VersionNT = 500) AND (ServicePackLevel >= 1))) AND ((VersionNT <> 500) OR ((VersionNT = 500) AND (ServicePackLevel <> 1))) AND ((VersionNT <> 500) OR ((VersionNT = 500) AND (ServicePackLevel <> 2))) AND ((VersionNT <> 500) OR ((VersionNT = 500) AND (ServicePackLevel <> 3))) AND ((VersionNT <> 500) OR ((VersionNT = 500) AND (ServicePackLevel <> 4))) AND (((VersionNT <> 501) OR ((VersionNT = 501) AND (ServicePackLevel >= 1))) OR VersionNT64) AND (((VersionNT <> 501) OR ((VersionNT = 501) AND (ServicePackLevel <> 1))) OR VersionNT64) AND (((VersionNT <> 501) OR ((VersionNT = 501) AND (ServicePackLevel <> 2))) OR VersionNT64) AND (((VersionNT <> 501) OR ((VersionNT = 501) AND (ServicePackLevel <> 3))) OR VersionNT64) AND (((VersionNT <> 502) OR ((VersionNT = 502) AND (ServicePackLevel >= 1))) OR VersionNT64) AND (((VersionNT <> 502) OR ((VersionNT = 502) AND (ServicePackLevel <> 1))) OR VersionNT64) AND (((VersionNT <> 502) OR ((VersionNT = 502) AND (ServicePackLevel <> 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/> - <ROW Condition="(VersionNT <> 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/> + <ROW Condition="(VersionNT <> 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/> <ROW Condition="AI_DETECTED_DOTNET_VERSION >= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/> <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/> - <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/> + <ROW Condition="SETUPEXEDIR OR (REMOVE="ALL")" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/> + <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/> + </COMPONENT> + <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent"> + <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/> + <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent"> + <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/> <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/> <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/> <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/> @@ -321,6 +356,8 @@ <ROW FileKey="devcon.log" Component_="ProductInformation" FileName="devcon.log" DirProperty="One_Dir" InstallMode="3"/> <ROW FileKey="devcon_x64.exe" Component_="ProductInformation" FileName="devcon_x64.exe" DirProperty="One_Dir" InstallMode="3"/> <ROW FileKey="devcon_x86.exe" Component_="ProductInformation" FileName="devcon_x86.exe" DirProperty="One_Dir" InstallMode="3"/> + <ROW FileKey="latestupdate.exe" Component_="ProductInformation" FileName="latest-update.exe" DirProperty="One_Dir" InstallMode="3"/> + <ROW FileKey="latestupdate.json" Component_="ProductInformation" FileName="latest-update.json" DirProperty="One_Dir" InstallMode="3"/> <ROW FileKey="node.log" Component_="ProductInformation" FileName="node.log" DirProperty="One_Dir" InstallMode="3"/> <ROW FileKey="node.log.old" Component_="ProductInformation" FileName="node.log.old" DirProperty="One_Dir" InstallMode="3"/> <ROW FileKey="roottopology" Component_="ProductInformation" FileName="root-topology" DirProperty="One_Dir" InstallMode="3"/> @@ -334,7 +371,7 @@ <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent"> - <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR" CustomFlags="1"/> + <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/> </COMPONENT> <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent"> <ATTRIBUTE name="UsedTheme" value="classic"/> diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 21544b96..8b1ee0ac 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -393,7 +393,17 @@ enum ZT_Event * * Meta-data: C string, TRACE message */ - ZT_EVENT_TRACE = 5 + ZT_EVENT_TRACE = 5, + + /** + * VERB_USER_MESSAGE received + * + * These are generated when a VERB_USER_MESSAGE packet is received via + * ZeroTier VL1. + * + * Meta-data: ZT_UserMessage structure + */ + ZT_EVENT_USER_MESSAGE = 6 }; /** @@ -407,6 +417,32 @@ enum ZT_RelayPolicy }; /** + * User message used with ZT_EVENT_USER_MESSAGE + */ +typedef struct +{ + /** + * ZeroTier address of sender (least significant 40 bits) + */ + uint64_t origin; + + /** + * User message type ID + */ + uint64_t typeId; + + /** + * User message data (not including type ID) + */ + const void *data; + + /** + * Length of data in bytes + */ + unsigned int length; +} ZT_UserMessage; + +/** * Current node status */ typedef struct @@ -1854,6 +1890,20 @@ int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node); /** + * Send a VERB_USER_MESSAGE to another ZeroTier node + * + * There is no delivery guarantee here. Failure can occur if the message is + * too large or if dest is not a valid ZeroTier address. + * + * @param dest Destination ZeroTier address + * @param typeId VERB_USER_MESSAGE type ID + * @param data Payload data to attach to user message + * @param len Length of data in bytes + * @return Boolean: non-zero on success, zero on failure + */ +int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); + +/** * Set peer role * * Right now this can only be used to set a peer to either LEAF or diff --git a/java/jni/Android.mk b/java/jni/Android.mk index 73eb2c46..e5d501f8 100644 --- a/java/jni/Android.mk +++ b/java/jni/Android.mk @@ -35,9 +35,9 @@ LOCAL_SRC_FILES := \ $(ZT1)/node/Switch.cpp \ $(ZT1)/node/Tag.cpp \ $(ZT1)/node/Topology.cpp \ - $(ZT1)/node/Utils.cpp \ - $(ZT1)/osdep/Http.cpp \ - $(ZT1)/osdep/OSUtils.cpp + $(ZT1)/node/Utils.cpp +# $(ZT1)/osdep/Http.cpp \ +# $(ZT1)/osdep/OSUtils.cpp # JNI Files LOCAL_SRC_FILES += \ diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp index 6b882c6d..e355271c 100644 --- a/java/jni/ZT_jniutils.cpp +++ b/java/jni/ZT_jniutils.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - + #include "ZT_jniutils.h" #include "ZT_jnilookup.h" #include <string> @@ -30,7 +30,7 @@ extern "C" { jobject createResultObject(JNIEnv *env, ZT_ResultCode code) { jclass resultClass = NULL; - + jobject resultObject = NULL; resultClass = lookup.findClass("com/zerotier/sdk/ResultCode"); @@ -67,14 +67,14 @@ jobject createResultObject(JNIEnv *env, ZT_ResultCode code) } jfieldID enumField = lookup.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;"); - if(env->ExceptionCheck() || enumField == NULL) + if(env->ExceptionCheck() || enumField == NULL) { LOGE("Error on FindStaticField"); return NULL; } resultObject = env->GetStaticObjectField(resultClass, enumField); - if(env->ExceptionCheck() || resultObject == NULL) + if(env->ExceptionCheck() || resultObject == NULL) { LOGE("Error on GetStaticObjectField"); } @@ -154,6 +154,8 @@ jobject createEvent(JNIEnv *env, ZT_Event event) case ZT_EVENT_TRACE: fieldName = "EVENT_TRACE"; break; + case ZT_EVENT_USER_MESSAGE: + break; } jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;"); @@ -332,7 +334,7 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) } jobject inetAddressObject = NULL; - + if(addr.ss_family != 0) { inetAddressObject = newInetAddress(env, addr); @@ -468,7 +470,7 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) return pppObject; } -jobject newPeer(JNIEnv *env, const ZT_Peer &peer) +jobject newPeer(JNIEnv *env, const ZT_Peer &peer) { LOGV("newPeer called"); @@ -570,7 +572,7 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer) jobjectArray arrayObject = env->NewObjectArray( peer.pathCount, peerPhysicalPathClass, NULL); - if(env->ExceptionCheck() || arrayObject == NULL) + if(env->ExceptionCheck() || arrayObject == NULL) { LOGE("Error creating PeerPhysicalPath[] array"); return NULL; @@ -709,7 +711,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig) return NULL; } - assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", + assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;"); if(env->ExceptionCheck() || assignedAddressesField == NULL) { @@ -717,7 +719,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig) return NULL; } - routesField = lookup.findField(vnetConfigClass, "routes", + routesField = lookup.findField(vnetConfigClass, "routes", "[Lcom/zerotier/sdk/VirtualNetworkRoute;"); if(env->ExceptionCheck() || routesField == NULL) { diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index d0228d13..eb62d985 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -139,8 +139,8 @@ namespace { } return env->CallIntMethod( - ref->configListener, - configListenerCallbackMethod, + ref->configListener, + configListenerCallbackMethod, (jlong)nwid, operationObject, networkConfigObject); } @@ -203,7 +203,7 @@ namespace { void EventCallback(ZT_Node *node, void *userData, - enum ZT_Event event, + enum ZT_Event event, const void *data) { LOGV("EventCallback"); @@ -291,6 +291,8 @@ namespace { } } break; + case ZT_EVENT_USER_MESSAGE: + break; } } @@ -348,7 +350,7 @@ namespace { objectName, buffer, bufferIndex, objectSizeObj); long retval = (long)env->CallLongMethod( - ref->dataStoreGetListener, dataStoreGetCallbackMethod, + ref->dataStoreGetListener, dataStoreGetCallbackMethod, nameStr, bufferObj, (jlong)bufferIndex, objectSizeObj); if(retval > 0) @@ -463,7 +465,7 @@ namespace { LOGE("Couldn't find onSendPacketRequested method"); return -2; } - + jobject localAddressObj = NULL; if(memcmp(localAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0) { @@ -496,7 +498,7 @@ namespace { } } -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { lookup.setJavaVM(vm); return JNI_VERSION_1_6; @@ -641,7 +643,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( ZeroTier::Mutex::Lock lock(nodeMapMutex); ref->node = node; nodeMap.insert(std::make_pair(ref->id, ref)); - + return resultObject; } @@ -659,7 +661,7 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete( NodeMap::iterator found; { - ZeroTier::Mutex::Lock lock(nodeMapMutex); + ZeroTier::Mutex::Lock lock(nodeMapMutex); found = nodeMap.find(nodeId); } @@ -685,9 +687,9 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete( * Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( - JNIEnv *env, jobject obj, - jlong id, - jlong in_now, + JNIEnv *env, jobject obj, + jlong id, + jlong in_now, jlong in_nwid, jlong in_sourceMac, jlong in_destMac, @@ -697,7 +699,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( jlongArray out_nextBackgroundTaskDeadline) { uint64_t nodeId = (uint64_t) id; - + ZT_Node *node = findNode(nodeId); if(node == NULL) { @@ -752,9 +754,9 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( * Signature: (JJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( - JNIEnv *env, jobject obj, + JNIEnv *env, jobject obj, jlong id, - jlong in_now, + jlong in_now, jobject in_localAddress, jobject in_remoteAddress, jbyteArray in_packetData, @@ -820,7 +822,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( jmethodID inetSock_getPort = lookup.findMethod( InetSocketAddressClass, "getPort", "()I"); - if(env->ExceptionCheck() || inetSock_getPort == NULL) + if(env->ExceptionCheck() || inetSock_getPort == NULL) { LOGE("Couldn't find getPort method on InetSocketAddress"); return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); @@ -844,10 +846,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( } unsigned int addrSize = env->GetArrayLength(remoteAddressArray); - + sockaddr_storage localAddress = {}; - + if(localAddrObj == NULL) { localAddress = ZT_SOCKADDR_NULL; @@ -939,7 +941,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( localData, packetLength, &nextBackgroundTaskDeadline); - if(rc != ZT_RESULT_OK) + if(rc != ZT_RESULT_OK) { LOGE("ZT_Node_processWirePacket returned: %d", rc); } @@ -959,7 +961,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( * Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks( - JNIEnv *env, jobject obj, + JNIEnv *env, jobject obj, jlong id, jlong in_now, jlongArray out_nextBackgroundTaskDeadline) @@ -1032,7 +1034,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave( uint64_t nwid = (uint64_t)in_nwid; ZT_ResultCode rc = ZT_Node_leave(node, nwid, NULL); - + return createResultObject(env, rc); } @@ -1042,8 +1044,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave( * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe( - JNIEnv *env, jobject obj, - jlong id, + JNIEnv *env, jobject obj, + jlong id, jlong in_nwid, jlong in_multicastGroup, jlong in_multicastAdi) @@ -1072,8 +1074,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe( * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe( - JNIEnv *env, jobject obj, - jlong id, + JNIEnv *env, jobject obj, + jlong id, jlong in_nwid, jlong in_multicastGroup, jlong in_multicastAdi) @@ -1141,7 +1143,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status { return NULL; } - + nodeStatusConstructor = lookup.findMethod( nodeStatusClass, "<init>", "()V"); if(nodeStatusConstructor == NULL) @@ -1225,7 +1227,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig( } ZT_VirtualNetworkConfig *vnetConfig = ZT_Node_networkConfig(node, nwid); - + jobject vnetConfigObject = newNetworkConfig(env, *vnetConfig); ZT_Node_freeQueryResult(node, vnetConfig); @@ -1267,7 +1269,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( } ZT_PeerList *peerList = ZT_Node_peers(node); - + if(peerList == NULL) { LOGE("ZT_Node_peers returned NULL"); @@ -1306,7 +1308,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( { jobject peerObj = newPeer(env, peerList->peers[i]); env->SetObjectArrayElement(peerArrayObj, i, peerObj); - if(env->ExceptionCheck()) + if(env->ExceptionCheck()) { LOGE("Error assigning Peer object to array"); break; diff --git a/java/src/com/zerotier/sdk/VirtualNetworkRoute.java b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java index 738c4158..b89dce7b 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkRoute.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java @@ -29,9 +29,14 @@ package com.zerotier.sdk; import java.net.InetSocketAddress; -public final class VirtualNetworkRoute +public final class VirtualNetworkRoute implements Comparable<VirtualNetworkRoute> { - private VirtualNetworkRoute() {} + private VirtualNetworkRoute() { + target = null; + via = null; + flags = 0; + metric = 0; + } /** * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default @@ -52,4 +57,46 @@ public final class VirtualNetworkRoute * Route metric (not currently used) */ public int metric; + + + @Override + public int compareTo(VirtualNetworkRoute other) { + return target.toString().compareTo(other.target.toString()); + } + + public boolean equals(VirtualNetworkRoute other) { + boolean targetEquals; + if (target == null && other.target == null) { + targetEquals = true; + } + else if (target == null && other.target != null) { + targetEquals = false; + } + else if (target != null && other.target == null) { + targetEquals = false; + } + else { + targetEquals = target.equals(other.target); + } + + + boolean viaEquals; + if (via == null && other.via == null) { + viaEquals = true; + } + else if (via == null && other.via != null) { + viaEquals = false; + } + else if (via != null && other.via == null) { + viaEquals = false; + } + else { + viaEquals = via.equals(other.via); + } + + return viaEquals && + viaEquals && + flags == other.flags && + metric == other.metric; + } } diff --git a/make-freebsd.mk b/make-freebsd.mk index cb9a2e6d..7ac50895 100644 --- a/make-freebsd.mk +++ b/make-freebsd.mk @@ -1,6 +1,5 @@ CC=cc CXX=c++ - INCLUDES= DEFS= LIBS= @@ -8,11 +7,6 @@ LIBS= include objects.mk OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o -# "make official" is a shortcut for this -ifeq ($(ZT_OFFICIAL_RELEASE),1) - DEFS+=-DZT_OFFICIAL_RELEASE -endif - # Build with ZT_ENABLE_CLUSTER=1 to build with cluster support ifeq ($(ZT_ENABLE_CLUSTER),1) DEFS+=-DZT_ENABLE_CLUSTER @@ -34,6 +28,32 @@ else STRIP=strip --strip-all endif +# Determine system build architecture from compiler target +CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1) +ZT_ARCHITECTURE=0 +ifeq ($(CC_MACH),x86_64) + ZT_ARCHITECTURE=2 +endif +ifeq ($(CC_MACH),amd64) + ZT_ARCHITECTURE=2 +endif +ifeq ($(CC_MACH),i386) + ZT_ARCHITECTURE=1 +endif +ifeq ($(CC_MACH),i686) + ZT_ARCHITECTURE=1 +endif +ifeq ($(CC_MACH),arm) + ZT_ARCHITECTURE=3 +endif +ifeq ($(CC_MACH),arm64) + ZT_ARCHITECTURE=4 +endif +ifeq ($(CC_MACH),aarch64) + ZT_ARCHITECTURE=4 +endif +DEFS+=-DZT_BUILD_PLATFORM=7 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\"" + CXXFLAGS+=$(CFLAGS) -fno-rtti all: one @@ -48,18 +68,10 @@ selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) $(STRIP) zerotier-selftest -# No installer on FreeBSD yet -#installer: one FORCE -# ./buildinstaller.sh - clean: rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* debug: FORCE make -j 4 ZT_DEBUG=1 -#official: FORCE -# make -j 4 ZT_OFFICIAL_RELEASE=1 -# ./buildinstaller.sh - FORCE: diff --git a/make-linux.mk b/make-linux.mk index ed8ec44b..cb91ab88 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -1,24 +1,3 @@ -# -# Makefile for ZeroTier One on Linux -# -# This is confirmed to work on distributions newer than CentOS 6 (the -# one used for reference builds) and on 32 and 64 bit x86 and ARM -# machines. It should also work on other 'normal' machines and recent -# distributions. Editing might be required for tiny devices or weird -# distros. -# -# Targets -# one: zerotier-one and symlinks (cli and idtool) -# manpages: builds manpages, requires 'ronn' or nodeJS (will use either) -# all: builds 'one' and 'manpages' -# selftest: zerotier-selftest -# debug: builds 'one' and 'selftest' with tracing and debug flags -# clean: removes all built files, objects, other trash -# distclean: removes a few other things that might be present -# debian: build DEB packages; deb dev tools must be present -# redhat: build RPM packages; rpm dev tools must be present -# - # Automagically pick clang or gcc, with preference for clang # This is only done if we have not overridden these with an environment or CLI variable ifeq ($(origin CC),default) @@ -28,8 +7,6 @@ ifeq ($(origin CXX),default) CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi) endif -#UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1) - INCLUDES?= DEFS?=-D_FORTIFY_SOURCE=2 LDLIBS?= @@ -52,28 +29,23 @@ else DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER endif -ifeq ($(ZT_USE_MINIUPNPC),1) - OBJS+=osdep/PortMapper.o - - DEFS+=-DZT_USE_MINIUPNPC - - # Auto-detect libminiupnpc at least v2.0 - MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1) - ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1) - DEFS+=-DZT_USE_SYSTEM_MINIUPNPC - LDLIBS+=-lminiupnpc - else - DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR - OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o - endif - - # Auto-detect libnatpmp - ifeq ($(wildcard /usr/include/natpmp.h),) - OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o - else - LDLIBS+=-lnatpmp - DEFS+=-DZT_USE_SYSTEM_NATPMP - endif +# Auto-detect miniupnpc and nat-pmp as well and use system libs if present, +# otherwise build into binary as done on Mac and Windows. +OBJS+=osdep/PortMapper.o +DEFS+=-DZT_USE_MINIUPNPC +MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1) +ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1) + DEFS+=-DZT_USE_SYSTEM_MINIUPNPC + LDLIBS+=-lminiupnpc +else + DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR + OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o +endif +ifeq ($(wildcard /usr/include/natpmp.h),) + OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o +else + LDLIBS+=-lnatpmp + DEFS+=-DZT_USE_SYSTEM_NATPMP endif ifeq ($(ZT_ENABLE_CLUSTER),1) @@ -113,6 +85,32 @@ endif #LDFLAGS= #STRIP=echo +# Determine system build architecture from compiler target +CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1) +ZT_ARCHITECTURE=0 +ifeq ($(CC_MACH),x86_64) + ZT_ARCHITECTURE=2 +endif +ifeq ($(CC_MACH),amd64) + ZT_ARCHITECTURE=2 +endif +ifeq ($(CC_MACH),i386) + ZT_ARCHITECTURE=1 +endif +ifeq ($(CC_MACH),i686) + ZT_ARCHITECTURE=1 +endif +ifeq ($(CC_MACH),arm) + ZT_ARCHITECTURE=3 +endif +ifeq ($(CC_MACH),arm64) + ZT_ARCHITECTURE=4 +endif +ifeq ($(CC_MACH),aarch64) + ZT_ARCHITECTURE=4 +endif +DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\"" + all: one one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o osdep/LinuxDropPrivileges.o diff --git a/make-mac.mk b/make-mac.mk index 0fbd3817..298d1a2e 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -1,49 +1,45 @@ -ifeq ($(origin CC),default) - CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi) -endif -ifeq ($(origin CXX),default) - CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi) -endif - +CC=clang +CXX=clang++ INCLUDES= DEFS= LIBS= -ARCH_FLAGS=-arch x86_64 - -include objects.mk -OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o - -# Disable codesign since open source users will not have ZeroTier's certs +ARCH_FLAGS= CODESIGN=echo PRODUCTSIGN=echo CODESIGN_APP_CERT= CODESIGN_INSTALLER_CERT= -# Build with libminiupnpc by default for Mac -- desktops/laptops almost always want this -ZT_USE_MINIUPNPC?=1 +ZT_BUILD_PLATFORM=3 +ZT_BUILD_ARCHITECTURE=2 +ZT_VERSION_MAJOR=$(shell cat version.h | grep -F VERSION_MAJOR | cut -d ' ' -f 3) +ZT_VERSION_MINOR=$(shell cat version.h | grep -F VERSION_MINOR | cut -d ' ' -f 3) +ZT_VERSION_REV=$(shell cat version.h | grep -F VERSION_REVISION | cut -d ' ' -f 3) +ZT_VERSION_BUILD=$(shell cat version.h | grep -F VERSION_BUILD | cut -d ' ' -f 3) + +DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE) -# For internal use only -- signs everything with ZeroTier's developer cert +include objects.mk +OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o + +# Official releases are signed with our Apple cert and apply software updates by default ifeq ($(ZT_OFFICIAL_RELEASE),1) - DEFS+=-DZT_OFFICIAL_RELEASE -DZT_AUTO_UPDATE + DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"apply\"" ZT_USE_MINIUPNPC=1 CODESIGN=codesign PRODUCTSIGN=productsign CODESIGN_APP_CERT="Developer ID Application: ZeroTier, Inc (8ZD9JUCZ4V)" CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier, Inc (8ZD9JUCZ4V)" +else + DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"download\"" endif ifeq ($(ZT_ENABLE_CLUSTER),1) DEFS+=-DZT_ENABLE_CLUSTER endif -ifeq ($(ZT_AUTO_UPDATE),1) - DEFS+=-DZT_AUTO_UPDATE -endif - -ifeq ($(ZT_USE_MINIUPNPC),1) - DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR - OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o -endif +# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources +DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR +OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o # Debug mode -- dump trace output, build binary with -g ifeq ($(ZT_DEBUG),1) @@ -59,7 +55,7 @@ else STRIP=strip endif -CXXFLAGS=$(CFLAGS) -mmacosx-version-min=10.7 -std=c++11 -stdlib=libc++ +CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++ all: one macui @@ -69,16 +65,14 @@ one: $(OBJS) service/OneService.o one.o ln -sf zerotier-one zerotier-idtool ln -sf zerotier-one zerotier-cli $(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one - #$(CODESIGN) -vvv zerotier-one macui: FORCE cd macui && xcodebuild -target "ZeroTier One" -configuration Release $(CODESIGN) -f -s $(CODESIGN_APP_CERT) "macui/build/Release/ZeroTier One.app" - #$(CODESIGN) -vvv "macui/build/Release/ZeroTier One.app" -cli: FORCE - $(CXX) $(CXXFLAGS) -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl - $(STRIP) zerotier +#cli: FORCE +# $(CXX) $(CXXFLAGS) -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl +# $(STRIP) zerotier selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) @@ -90,18 +84,23 @@ mac-dist-pkg: FORCE rm -f "ZeroTier One Signed.pkg" $(PRODUCTSIGN) --sign $(CODESIGN_INSTALLER_CERT) "ZeroTier One.pkg" "ZeroTier One Signed.pkg" if [ -f "ZeroTier One Signed.pkg" ]; then mv -f "ZeroTier One Signed.pkg" "ZeroTier One.pkg"; fi + rm -f zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_* + cat ext/installfiles/mac-update/updater.tmpl.sh "ZeroTier One.pkg" >zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_$(ZT_VERSION_MAJOR).$(ZT_VERSION_MINOR).$(ZT_VERSION_REV)_$(ZT_VERSION_BUILD) # For ZeroTier, Inc. to build official signed packages official: FORCE make clean - make ZT_OFFICIAL_RELEASE=1 -j 4 one macui + make ZT_OFFICIAL_RELEASE=1 -j 4 one + make ZT_OFFICIAL_RELEASE=1 macui make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg clean: - rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier ZeroTierOneInstaller-* mkworld doc/node_modules macui/build + rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier mkworld doc/node_modules macui/build zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_* distclean: clean +realclean: clean + # For those building from source -- installs signed binary tap driver in system ZT home install-mac-tap: FORCE mkdir -p /Library/Application\ Support/ZeroTier/One diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7b828f8b..562aee91 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -106,8 +106,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer); case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer); - case Packet::VERB_USER_MESSAGE: - return true; + case Packet::VERB_USER_MESSAGE: return _doUSER_MESSAGE(RR,peer); } } else { RR->sw->requestWhois(sourceAddress); @@ -1345,6 +1344,24 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S return true; } +bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer) +{ + try { + if (size() >= (ZT_PACKET_IDX_PAYLOAD + 8)) { + ZT_UserMessage um; + um.origin = peer->address().toInt(); + um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD); + um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8); + um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8); + RR->node->postEvent(ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um)); + } + peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false); + } catch ( ... ) { + TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str()); + } + return true; +} + void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid) { const uint64_t now = RR->node->now(); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index 80244ea4..febff28a 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -131,6 +131,7 @@ private: bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); + bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid); diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index 5c94cd3a..32dec9cf 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -220,7 +220,7 @@ private: { _GatherAuthKey() : member(0),networkId(0) {} _GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {} - inline unsigned long hashCode() const { return (member ^ networkId); } + inline unsigned long hashCode() const { return (unsigned long)(member ^ networkId); } inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); } uint64_t member; uint64_t networkId; diff --git a/node/Node.cpp b/node/Node.cpp index ed60817f..0d0750ca 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -473,6 +473,21 @@ void Node::clearLocalInterfaceAddresses() _directPaths.clear(); } +int Node::sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len) +{ + try { + if (RR->identity.address().toInt() != dest) { + Packet outp(Address(dest),RR->identity.address(),Packet::VERB_USER_MESSAGE); + outp.append(typeId); + outp.append(data,len); + outp.compress(); + RR->sw->send(outp,true); + return 1; + } + } catch ( ... ) {} + return 0; +} + void Node::setRole(uint64_t ztAddress,ZT_PeerRole role) { RR->topology->setUpstream(Address(ztAddress),(role == ZT_PEER_ROLE_UPSTREAM)); @@ -992,6 +1007,15 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node) } catch ( ... ) {} } +int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len) +{ + try { + return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(dest,typeId,data,len); + } catch ( ... ) { + return 0; + } +} + void ZT_Node_setRole(ZT_Node *node,uint64_t ztAddress,ZT_PeerRole role) { try { diff --git a/node/Node.hpp b/node/Node.hpp index eb46527d..64c9fcb4 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -98,6 +98,7 @@ public: void freeQueryResult(void *qr); int addLocalInterfaceAddress(const struct sockaddr_storage *addr); void clearLocalInterfaceAddresses(); + int sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len); void setRole(uint64_t ztAddress,ZT_PeerRole role); void setNetconfMaster(void *networkControllerInstance); ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); diff --git a/node/Packet.hpp b/node/Packet.hpp index 8ff817aa..5ecbecba 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1048,6 +1048,10 @@ public: * This can be used to send arbitrary messages over VL1. It generates no * OK or ERROR and has no special semantics outside of whatever the user * (via the ZeroTier core API) chooses to give it. + * + * Message type IDs less than or equal to 65535 are reserved for use by + * ZeroTier, Inc. itself. We recommend making up random ones for your own + * implementations. */ VERB_USER_MESSAGE = 0x14 }; diff --git a/node/Utils.hpp b/node/Utils.hpp index 48c43da3..48cf799e 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -335,8 +335,7 @@ public: * * @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second */ - static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2) - throw() + static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2) { if (maj1 > maj2) return 1; @@ -352,7 +351,13 @@ public: return 1; else if (rev1 < rev2) return -1; - else return 0; + else { + if (b1 > b2) + return 1; + else if (b1 < b2) + return -1; + else return 0; + } } } } @@ -31,4 +31,5 @@ OBJS=\ osdep/Http.o \ osdep/OSUtils.o \ service/ClusterGeoIpService.o \ - service/ControlPlane.o + service/ControlPlane.o \ + service/SoftwareUpdater.o @@ -495,7 +495,7 @@ static int cli(int argc,char **argv) (std::string("/network/") + arg1).c_str(), requestHeaders, jsons, - strlen(jsons), + (unsigned long)strlen(jsons), responseHeaders, responseBody); if (scode == 200) { @@ -572,7 +572,7 @@ static int idtool(int argc,char **argv) int vanityBits = 0; if (argc >= 5) { vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL; - vanityBits = 4 * strlen(argv[4]); + vanityBits = 4 * (int)strlen(argv[4]); if (vanityBits > 40) vanityBits = 40; } @@ -897,9 +897,6 @@ static void printHelp(const char *cn,FILE *out) fprintf(out, COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S); - std::string updateUrl(OneService::autoUpdateUrl()); - if (updateUrl.length()) - fprintf(out,"Automatic updates enabled:" ZT_EOL_S" %s" ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)" ZT_EOL_S"" ZT_EOL_S,updateUrl.c_str()); fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn); fprintf(out,"Available switches:" ZT_EOL_S); fprintf(out," -h - Display this help" ZT_EOL_S); diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index 65704fa3..170e1376 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -23,6 +23,7 @@ #include <sys/stat.h> #include "../node/Constants.hpp" +#include "../node/Utils.hpp" #ifdef __UNIX_LIKE__ #include <unistd.h> @@ -395,6 +396,81 @@ std::string OSUtils::platformDefaultHomePath() #endif // __UNIX_LIKE__ or not... } +// Inline these massive JSON operations in one place only to reduce binary footprint and compile time +nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf); } +std::string OSUtils::jsonDump(const nlohmann::json &j) { return j.dump(1); } + +uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl) +{ + try { + if (jv.is_number()) { + return (uint64_t)jv; + } else if (jv.is_string()) { + std::string s = jv; + return Utils::strToU64(s.c_str()); + } else if (jv.is_boolean()) { + return ((bool)jv ? 1ULL : 0ULL); + } + } catch ( ... ) {} + return dfl; +} + +bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl) +{ + try { + if (jv.is_boolean()) { + return (bool)jv; + } else if (jv.is_number()) { + return ((uint64_t)jv > 0ULL); + } else if (jv.is_string()) { + std::string s = jv; + if (s.length() > 0) { + switch(s[0]) { + case 't': + case 'T': + case '1': + return true; + } + } + return false; + } + } catch ( ... ) {} + return dfl; +} + +std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl) +{ + try { + if (jv.is_string()) { + return jv; + } else if (jv.is_number()) { + char tmp[64]; + Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv); + return tmp; + } else if (jv.is_boolean()) { + return ((bool)jv ? std::string("1") : std::string("0")); + } + } catch ( ... ) {} + return std::string((dfl) ? dfl : ""); +} + +std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv) +{ + std::string s(jsonString(jv,"")); + if (s.length() > 0) { + char *buf = new char[(s.length() / 2) + 1]; + try { + unsigned int l = Utils::unhex(s,buf,(unsigned int)s.length()); + std::string b(buf,l); + delete [] buf; + return b; + } catch ( ... ) { + delete [] buf; + } + } + return std::string(); +} + // Used to convert HTTP header names to ASCII lower case const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index c73f0bd3..2fe01979 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -45,6 +45,8 @@ #include <arpa/inet.h> #endif +#include "../ext/json/json.hpp" + namespace ZeroTier { /** @@ -267,6 +269,13 @@ public: */ static std::string platformDefaultHomePath(); + static nlohmann::json jsonParse(const std::string &buf); + static std::string jsonDump(const nlohmann::json &j); + static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl); + static bool jsonBool(const nlohmann::json &jv,const bool dfl); + static std::string jsonString(const nlohmann::json &jv,const char *dfl); + static std::string jsonBinFromHex(const nlohmann::json &jv); + private: static const unsigned char TOLOWER_TABLE[256]; }; diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index a9ff31d3..8ee088bb 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -640,7 +640,7 @@ WindowsEthernetTap::WindowsEthernetTap( if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR) throw std::runtime_error("unable to convert device interface GUID to LUID"); - _initialized = true; + //_initialized = true; if (friendlyName) setFriendlyName(friendlyName); @@ -1007,6 +1007,10 @@ void WindowsEthernetTap::threadMain() ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); bool writeInProgress = false; ULONGLONG timeOfLastBorkCheck = GetTickCount64(); + + + _initialized = true; + while (_run) { DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE); if (!_run) break; // will also break outer while(_run) diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp index 0bbb17d8..53bba3e9 100644 --- a/osdep/WindowsEthernetTap.hpp +++ b/osdep/WindowsEthernetTap.hpp @@ -110,6 +110,8 @@ public: void threadMain() throw(); + bool isInitialized() const { return _initialized; }; + private: NET_IFINDEX _getDeviceIndex(); // throws on failure std::vector<std::string> _getRegistryIPv4Value(const char *regKey); diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 3bdfdf1b..86158a91 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -515,7 +515,7 @@ unsigned int ControlPlane::handleRequest( _svc->getNetworkSettings(nws->networks[i].nwid,localSettings); try { - nlohmann::json j(nlohmann::json::parse(body)); + nlohmann::json j(OSUtils::jsonParse(body)); if (j.is_object()) { nlohmann::json &allowManaged = j["allowManaged"]; if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged; diff --git a/service/OneService.cpp b/service/OneService.cpp index 3a7edacb..0fd8496d 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -52,6 +52,7 @@ #include "ControlPlane.hpp" #include "ClusterGeoIpService.hpp" #include "ClusterDefinition.hpp" +#include "SoftwareUpdater.hpp" #ifdef ZT_USE_SYSTEM_HTTP_PARSER #include <http_parser.h> @@ -148,261 +149,6 @@ namespace ZeroTier { namespace { -static uint64_t _jI(const json &jv,const uint64_t dfl) -{ - if (jv.is_number()) { - return (uint64_t)jv; - } else if (jv.is_string()) { - std::string s = jv; - return Utils::strToU64(s.c_str()); - } else if (jv.is_boolean()) { - return ((bool)jv ? 1ULL : 0ULL); - } - return dfl; -} -static bool _jB(const json &jv,const bool dfl) -{ - if (jv.is_boolean()) { - return (bool)jv; - } else if (jv.is_number()) { - return ((uint64_t)jv > 0ULL); - } else if (jv.is_string()) { - std::string s = jv; - if (s.length() > 0) { - switch(s[0]) { - case 't': - case 'T': - case '1': - return true; - } - } - return false; - } - return dfl; -} -static std::string _jS(const json &jv,const char *dfl) -{ - if (jv.is_string()) { - return jv; - } else if (jv.is_number()) { - char tmp[64]; - Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv); - return tmp; - } else if (jv.is_boolean()) { - return ((bool)jv ? std::string("1") : std::string("0")); - } - return std::string((dfl) ? dfl : ""); -} - -#ifdef ZT_AUTO_UPDATE -#define ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE (1024 * 1024 * 64) -#define ZT_AUTO_UPDATE_CHECK_PERIOD 21600000 -class BackgroundSoftwareUpdateChecker -{ -public: - bool isValidSigningIdentity(const Identity &id) - { - return ( - /* 0001 - 0004 : obsolete, used in old versions */ - /* 0005 */ (id == Identity("ba57ea350e:0:9d4be6d7f86c5660d5ee1951a3d759aa6e12a84fc0c0b74639500f1dbc1a8c566622e7d1c531967ebceb1e9d1761342f88324a8ba520c93c35f92f35080fa23f")) - /* 0006 */ ||(id == Identity("5067b21b83:0:8af477730f5055c48135b84bed6720a35bca4c0e34be4060a4c636288b1ec22217eb22709d610c66ed464c643130c51411bbb0294eef12fbe8ecc1a1e2c63a7a")) - /* 0007 */ ||(id == Identity("4f5e97a8f1:0:57880d056d7baeb04bbc057d6f16e6cb41388570e87f01492fce882485f65a798648595610a3ad49885604e7fb1db2dd3c2c534b75e42c3c0b110ad07b4bb138")) - /* 0008 */ ||(id == Identity("580bbb8e15:0:ad5ef31155bebc6bc413991992387e083fed26d699997ef76e7c947781edd47d1997161fa56ba337b1a2b44b129fd7c7197ce5185382f06011bc88d1363b4ddd")) - ); - } - - void doUpdateCheck() - { - std::string url(OneService::autoUpdateUrl()); - if ((url.length() <= 7)||(url.substr(0,7) != "http://")) - return; - - std::string httpHost; - std::string httpPath; - { - std::size_t slashIdx = url.substr(7).find_first_of('/'); - if (slashIdx == std::string::npos) { - httpHost = url.substr(7); - httpPath = "/"; - } else { - httpHost = url.substr(7,slashIdx); - httpPath = url.substr(slashIdx + 7); - } - } - if (httpHost.length() == 0) - return; - - std::vector<InetAddress> ips(OSUtils::resolve(httpHost.c_str())); - for(std::vector<InetAddress>::iterator ip(ips.begin());ip!=ips.end();++ip) { - if (!ip->port()) - ip->setPort(80); - std::string nfoPath = httpPath + "LATEST.nfo"; - std::map<std::string,std::string> requestHeaders,responseHeaders; - std::string body; - requestHeaders["Host"] = httpHost; - unsigned int scode = Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),nfoPath.c_str(),requestHeaders,responseHeaders,body); - //fprintf(stderr,"UPDATE %s %s %u %lu\n",ip->toString().c_str(),nfoPath.c_str(),scode,body.length()); - if ((scode == 200)&&(body.length() > 0)) { - /* NFO fields: - * - * file=<filename> - * signedBy=<signing identity> - * ed25519=<ed25519 ECC signature of archive in hex> - * vMajor=<major version> - * vMinor=<minor version> - * vRevision=<revision> */ - Dictionary<4096> nfo(body.c_str()); - char tmp[2048]; - - if (nfo.get("vMajor",tmp,sizeof(tmp)) <= 0) return; - const unsigned int vMajor = Utils::strToUInt(tmp); - if (nfo.get("vMinor",tmp,sizeof(tmp)) <= 0) return; - const unsigned int vMinor = Utils::strToUInt(tmp); - if (nfo.get("vRevision",tmp,sizeof(tmp)) <= 0) return; - const unsigned int vRevision = Utils::strToUInt(tmp); - if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) { - //fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision); - return; - } - - if (nfo.get("signedBy",tmp,sizeof(tmp)) <= 0) return; - Identity signedBy; - if ((!signedBy.fromString(tmp))||(!isValidSigningIdentity(signedBy))) { - //fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n"); - return; - } - - if (nfo.get("file",tmp,sizeof(tmp)) <= 0) return; - std::string filePath(tmp); - if ((!filePath.length())||(filePath.find("..") != std::string::npos)) - return; - filePath = httpPath + filePath; - - std::string fileData; - if (Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),filePath.c_str(),requestHeaders,responseHeaders,fileData) != 200) { - //fprintf(stderr,"UPDATE GET %s failed\n",filePath.c_str()); - return; - } - - if (nfo.get("ed25519",tmp,sizeof(tmp)) <= 0) return; - std::string ed25519(Utils::unhex(tmp)); - if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) { - //fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str()); - return; - } - - /* --------------------------------------------------------------- */ - /* We made it! Begin OS-specific installation code. */ - -#ifdef __APPLE__ - /* OSX version is in the form of a MacOSX .pkg file, so we will - * launch installer (normally in /usr/sbin) to install it. It will - * then turn around and shut down the service, update files, and - * relaunch. */ - { - char bashp[128],pkgp[128]; - Utils::snprintf(bashp,sizeof(bashp),"/tmp/ZeroTierOne-update-%u.%u.%u.sh",vMajor,vMinor,vRevision); - Utils::snprintf(pkgp,sizeof(pkgp),"/tmp/ZeroTierOne-update-%u.%u.%u.pkg",vMajor,vMinor,vRevision); - FILE *pkg = fopen(pkgp,"w"); - if ((!pkg)||(fwrite(fileData.data(),fileData.length(),1,pkg) != 1)) { - fclose(pkg); - unlink(bashp); - unlink(pkgp); - fprintf(stderr,"UPDATE error writing %s\n",pkgp); - return; - } - fclose(pkg); - FILE *bash = fopen(bashp,"w"); - if (!bash) { - fclose(pkg); - unlink(bashp); - unlink(pkgp); - fprintf(stderr,"UPDATE error writing %s\n",bashp); - return; - } - fprintf(bash, - "#!/bin/bash\n" - "export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin\n" - "sleep 1\n" - "installer -pkg \"%s\" -target /\n" - "sleep 1\n" - "rm -f \"%s\" \"%s\"\n" - "exit 0\n", - pkgp, - pkgp, - bashp); - fclose(bash); - long pid = (long)vfork(); - if (pid == 0) { - setsid(); // detach from parent so that shell isn't killed when parent is killed - signal(SIGHUP,SIG_IGN); - signal(SIGTERM,SIG_IGN); - signal(SIGQUIT,SIG_IGN); - execl("/bin/bash","/bin/bash",bashp,(char *)0); - exit(0); - } - } -#endif // __APPLE__ - -#ifdef __WINDOWS__ - /* Windows version comes in the form of .MSI package that - * takes care of everything. */ - { - char tempp[512],batp[512],msip[512],cmdline[512]; - if (GetTempPathA(sizeof(tempp),tempp) <= 0) - return; - CreateDirectoryA(tempp,(LPSECURITY_ATTRIBUTES)0); - Utils::snprintf(batp,sizeof(batp),"%s\\ZeroTierOne-update-%u.%u.%u.bat",tempp,vMajor,vMinor,vRevision); - Utils::snprintf(msip,sizeof(msip),"%s\\ZeroTierOne-update-%u.%u.%u.msi",tempp,vMajor,vMinor,vRevision); - FILE *msi = fopen(msip,"wb"); - if ((!msi)||(fwrite(fileData.data(),(size_t)fileData.length(),1,msi) != 1)) { - fclose(msi); - return; - } - fclose(msi); - FILE *bat = fopen(batp,"wb"); - if (!bat) - return; - fprintf(bat, - "TIMEOUT.EXE /T 1 /NOBREAK\r\n" - "NET.EXE STOP \"ZeroTierOneService\"\r\n" - "TIMEOUT.EXE /T 1 /NOBREAK\r\n" - "MSIEXEC.EXE /i \"%s\" /qn\r\n" - "TIMEOUT.EXE /T 1 /NOBREAK\r\n" - "NET.EXE START \"ZeroTierOneService\"\r\n" - "DEL \"%s\"\r\n" - "DEL \"%s\"\r\n", - msip, - msip, - batp); - fclose(bat); - STARTUPINFOA si; - PROCESS_INFORMATION pi; - memset(&si,0,sizeof(si)); - memset(&pi,0,sizeof(pi)); - Utils::snprintf(cmdline,sizeof(cmdline),"CMD.EXE /c \"%s\"",batp); - CreateProcessA(NULL,cmdline,NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi); - } -#endif // __WINDOWS__ - - /* --------------------------------------------------------------- */ - - return; - } // else try to fetch from next IP address - } - } - - void threadMain() - throw() - { - try { - this->doUpdateCheck(); - } catch ( ... ) {} - } -}; -static BackgroundSoftwareUpdateChecker backgroundSoftwareUpdateChecker; -#endif // ZT_AUTO_UPDATE - static std::string _trimString(const std::string &s) { unsigned long end = (unsigned long)s.length(); @@ -517,6 +263,8 @@ public: EmbeddedNetworkController *_controller; Phy<OneServiceImpl *> _phy; Node *_node; + SoftwareUpdater *_updater; + bool _updateAutoApply; unsigned int _primaryPort; // Local configuration and memo-ized static path definitions @@ -619,6 +367,8 @@ public: ,_controller((EmbeddedNetworkController *)0) ,_phy(this,false,true) ,_node((Node *)0) + ,_updater((SoftwareUpdater *)0) + ,_updateAutoApply(false) ,_primaryPort(port) ,_controlPlane((ControlPlane *)0) ,_lastDirectReceiveFromGlobal(0) @@ -743,7 +493,7 @@ public: std::string lcbuf; if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "local.conf").c_str(),lcbuf)) { try { - _localConfig = json::parse(lcbuf); + _localConfig = OSUtils::jsonParse(lcbuf); if (!_localConfig.is_object()) { fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S); } @@ -756,11 +506,11 @@ public: json &physical = _localConfig["physical"]; if (physical.is_object()) { for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { - InetAddress net(_jS(phy.key(),"")); + InetAddress net(OSUtils::jsonString(phy.key(),"")); if (net) { if (phy.value().is_object()) { uint64_t tpid; - if ((tpid = _jI(phy.value()["trustedPathId"],0ULL)) != 0ULL) { + if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) { if ( ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) && (trustedPathCount < ZT_MAX_TRUSTED_PATHS) && (net.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (net.netmaskBits() > 0) ) { trustedPathIds[trustedPathCount] = tpid; trustedPathNetworks[trustedPathCount] = net; @@ -875,7 +625,15 @@ public: for(int i=0;i<3;++i) _portsBE[i] = Utils::hton((uint16_t)_ports[i]); - _controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(FILE *)0); + // Check for legacy controller.db and terminate if present to prevent nasty surprises for DIY controller folks + if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S "controller.db").c_str())) { + Mutex::Lock _l(_termReason_m); + _termReason = ONE_UNRECOVERABLE_ERROR; + _fatalErrorMessage = "controller.db is present in our home path! run migrate-sqlite to migrate to new controller.d format."; + return _termReason; + } + + _controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S ZT_CONTROLLER_DB_PATH).c_str(),(FILE *)0); _node->setNetconfMaster((void *)_controller); #ifdef ZT_ENABLE_CLUSTER @@ -946,10 +704,8 @@ public: uint64_t lastTapMulticastGroupCheck = 0; uint64_t lastTcpFallbackResolve = 0; uint64_t lastBindRefresh = 0; - uint64_t lastLocalInterfaceAddressCheck = (OSUtils::now() - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle -#ifdef ZT_AUTO_UPDATE - uint64_t lastSoftwareUpdateCheck = 0; -#endif // ZT_AUTO_UPDATE + uint64_t lastUpdateCheck = clockShouldBe; + uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle for(;;) { _run_m.lock(); if (!_run) { @@ -971,6 +727,13 @@ public: restarted = true; } + // Check for updates (if enabled) + if ((_updater)&&((now - lastUpdateCheck) > 10000)) { + lastUpdateCheck = now; + if (_updater->check(now) && _updateAutoApply) + _updater->apply(); + } + // Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default) if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) { lastBindRefresh = now; @@ -994,13 +757,6 @@ public: dl = _nextBackgroundTaskDeadline; } -#ifdef ZT_AUTO_UPDATE - if ((now - lastSoftwareUpdateCheck) >= ZT_AUTO_UPDATE_CHECK_PERIOD) { - lastSoftwareUpdateCheck = now; - Thread::start(&backgroundSoftwareUpdateChecker); - } -#endif // ZT_AUTO_UPDATE - if ((now - lastTcpFallbackResolve) >= ZT_TCP_FALLBACK_RERESOLVE_DELAY) { lastTcpFallbackResolve = now; _tcpFallbackResolver.resolveNow(); @@ -1070,6 +826,8 @@ public: delete _controlPlane; _controlPlane = (ControlPlane *)0; + delete _updater; + _updater = (SoftwareUpdater *)0; delete _node; _node = (Node *)0; @@ -1163,7 +921,7 @@ public: if ((nstr.length() == ZT_ADDRESS_LENGTH_HEX)&&(v.value().is_object())) { const Address ztaddr(nstr.c_str()); if (ztaddr) { - const std::string rstr(_jS(v.value()["role"],"")); + const std::string rstr(OSUtils::jsonString(v.value()["role"],"")); _node->setRole(ztaddr.toInt(),((rstr == "upstream")||(rstr == "UPSTREAM")) ? ZT_PEER_ROLE_UPSTREAM : ZT_PEER_ROLE_LEAF); const uint64_t ztaddr2 = ztaddr.toInt(); @@ -1175,7 +933,7 @@ public: json &tryAddrs = v.value()["try"]; if (tryAddrs.is_array()) { for(unsigned long i=0;i<tryAddrs.size();++i) { - const InetAddress ip(_jS(tryAddrs[i],"")); + const InetAddress ip(OSUtils::jsonString(tryAddrs[i],"")); if (ip.ss_family == AF_INET) v4h.push_back(ip); else if (ip.ss_family == AF_INET6) @@ -1185,7 +943,7 @@ public: json &blAddrs = v.value()["blacklist"]; if (blAddrs.is_array()) { for(unsigned long i=0;i<blAddrs.size();++i) { - const InetAddress ip(_jS(tryAddrs[i],"")); + const InetAddress ip(OSUtils::jsonString(tryAddrs[i],"")); if (ip.ss_family == AF_INET) v4b.push_back(ip); else if (ip.ss_family == AF_INET6) @@ -1207,10 +965,10 @@ public: json &physical = _localConfig["physical"]; if (physical.is_object()) { for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { - const InetAddress net(_jS(phy.key(),"")); + const InetAddress net(OSUtils::jsonString(phy.key(),"")); if ((net)&&(net.netmaskBits() > 0)) { if (phy.value().is_object()) { - if (_jB(phy.value()["blacklist"],false)) { + if (OSUtils::jsonBool(phy.value()["blacklist"],false)) { if (net.ss_family == AF_INET) _globalV4Blacklist.push_back(net); else if (net.ss_family == AF_INET6) @@ -1225,17 +983,31 @@ public: _interfacePrefixBlacklist.clear(); json &settings = _localConfig["settings"]; if (settings.is_object()) { - const std::string rp(_jS(settings["relayPolicy"],"")); + const std::string rp(OSUtils::jsonString(settings["relayPolicy"],"")); if ((rp == "always")||(rp == "ALWAYS")) _node->setRelayPolicy(ZT_RELAY_POLICY_ALWAYS); else if ((rp == "never")||(rp == "NEVER")) _node->setRelayPolicy(ZT_RELAY_POLICY_NEVER); else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED); + const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT)); + const bool udist = OSUtils::jsonBool(settings["softwareUpdateDist"],false); + if (((up == "apply")||(up == "download"))||(udist)) { + if (!_updater) + _updater = new SoftwareUpdater(*_node,_homePath); + _updateAutoApply = (up == "apply"); + _updater->setUpdateDistribution(udist); + _updater->setChannel(OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL)); + } else { + delete _updater; + _updater = (SoftwareUpdater *)0; + _updateAutoApply = false; + } + json &ignoreIfs = settings["interfacePrefixBlacklist"]; if (ignoreIfs.is_array()) { for(unsigned long i=0;i<ignoreIfs.size();++i) { - const std::string tmp(_jS(ignoreIfs[i],"")); + const std::string tmp(OSUtils::jsonString(ignoreIfs[i],"")); if (tmp.length() > 0) _interfacePrefixBlacklist.push_back(tmp); } @@ -1244,7 +1016,7 @@ public: json &amf = settings["allowManagementFrom"]; if (amf.is_array()) { for(unsigned long i=0;i<amf.size();++i) { - const InetAddress nw(_jS(amf[i],"")); + const InetAddress nw(OSUtils::jsonString(amf[i],"")); if (nw) _allowManagementFrom.push_back(nw); } @@ -1666,6 +1438,16 @@ public: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig)); if (n.tap) { // sanity check +#ifdef __WINDOWS__ + // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized + // + // without WindowsEthernetTap::isInitialized() returning true, the won't actually + // be online yet and setting managed routes on it will fail. + const int MAX_SLEEP_COUNT = 500; + for (int i = 0; !n.tap->isInitialized() && i < MAX_SLEEP_COUNT; i++) { + Sleep(10); + } +#endif syncManagedStuff(n,true,true); } else { _nets.erase(nwid); @@ -1717,6 +1499,13 @@ public: } } break; + case ZT_EVENT_USER_MESSAGE: { + const ZT_UserMessage *um = reinterpret_cast<const ZT_UserMessage *>(metaData); + if ((um->typeId == ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE)&&(_updater)) { + _updater->handleSoftwareUpdateUserMessage(um->origin,um->data,um->length); + } + } break; + default: break; } @@ -2235,30 +2024,6 @@ std::string OneService::platformDefaultHomePath() return OSUtils::platformDefaultHomePath(); } -std::string OneService::autoUpdateUrl() -{ -#ifdef ZT_AUTO_UPDATE - -/* -#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) - if (sizeof(void *) == 8) - return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x64-LATEST.nfo"; - else return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x86-LATEST.nfo"; -#endif -*/ - -#if defined(__APPLE__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) - return "http://download.zerotier.com/update/mac_intel/"; -#endif - -#ifdef __WINDOWS__ - return "http://download.zerotier.com/update/win_intel/"; -#endif - -#endif // ZT_AUTO_UPDATE - return std::string(); -} - OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } OneService::~OneService() {} diff --git a/service/OneService.hpp b/service/OneService.hpp index aebc051b..7aa3b361 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -82,11 +82,6 @@ public: static std::string platformDefaultHomePath(); /** - * @return Auto-update URL or empty string if auto-updates unsupported or not enabled - */ - static std::string autoUpdateUrl(); - - /** * Create a new instance of the service * * Once created, you must call the run() method to actually start diff --git a/service/SoftwareUpdater.cpp b/service/SoftwareUpdater.cpp new file mode 100644 index 00000000..c650ce42 --- /dev/null +++ b/service/SoftwareUpdater.cpp @@ -0,0 +1,397 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include "../node/Constants.hpp" +#include "../version.h" + +#ifdef __WINDOWS__ +#include <WinSock2.h> +#include <Windows.h> +#include <ShlObj.h> +#include <netioapi.h> +#include <iphlpapi.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> +#include <ifaddrs.h> +#endif + +#include "SoftwareUpdater.hpp" + +#include "../node/Utils.hpp" +#include "../node/SHA512.hpp" +#include "../node/Buffer.hpp" +#include "../node/Node.hpp" + +#include "../osdep/OSUtils.hpp" + +#ifndef ZT_BUILD_ARCHITECTURE +#define ZT_BUILD_ARCHITECTURE 0 +#endif +#ifndef ZT_BUILD_PLATFORM +#define ZT_BUILD_PLATFORM 0 +#endif + +namespace ZeroTier { + +SoftwareUpdater::SoftwareUpdater(Node &node,const std::string &homePath) : + _node(node), + _lastCheckTime(0), + _homePath(homePath), + _channel(ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL), + _distLog((FILE *)0), + _latestValid(false), + _downloadLength(0) +{ + // Check for a cached newer update. If there's a cached update that is not newer or looks bad, delete. + try { + std::string buf; + if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_META_FILENAME).c_str(),buf)) { + nlohmann::json meta = OSUtils::jsonParse(buf); + buf = std::string(); + const unsigned int rvMaj = (unsigned int)OSUtils::jsonInt(meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR],0); + const unsigned int rvMin = (unsigned int)OSUtils::jsonInt(meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0); + const unsigned int rvRev = (unsigned int)OSUtils::jsonInt(meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0); + const unsigned int rvBld = (unsigned int)OSUtils::jsonInt(meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0); + if ((Utils::compareVersion(rvMaj,rvMin,rvRev,rvBld,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD) > 0)&& + (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str(),buf))) { + if ((uint64_t)buf.length() == OSUtils::jsonInt(meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE],0)) { + _latestMeta = meta; + _latestValid = true; + //printf("CACHED UPDATE IS NEWER AND LOOKS GOOD\n"); + } + } + } + } catch ( ... ) {} // exceptions indicate invalid cached update + if (!_latestValid) { + OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_META_FILENAME).c_str()); + OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str()); + } +} + +SoftwareUpdater::~SoftwareUpdater() +{ + if (_distLog) + fclose(_distLog); +} + +void SoftwareUpdater::setUpdateDistribution(bool distribute) +{ + _dist.clear(); + if (distribute) { + _distLog = fopen((_homePath + ZT_PATH_SEPARATOR_S "update-dist.log").c_str(),"a"); + std::string udd(_homePath + ZT_PATH_SEPARATOR_S "update-dist.d"); + std::vector<std::string> ud(OSUtils::listDirectory(udd.c_str())); + for(std::vector<std::string>::iterator u(ud.begin());u!=ud.end();++u) { + // Each update has a companion .json file describing it. Other files are ignored. + if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) { + std::string buf; + if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) { + try { + _D d; + d.meta = OSUtils::jsonParse(buf); + std::string metaHash = OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]); + std::string binPath(udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5)); + if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile(binPath.c_str(),d.bin))) { + uint8_t sha512[ZT_SHA512_DIGEST_LEN]; + SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length()); + if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct + d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional + _dist[Array<uint8_t,16>(sha512)] = d; + if (_distLog) { + fprintf(_distLog,".......... INIT: DISTRIBUTING %s (%u bytes)" ZT_EOL_S,binPath.c_str(),(unsigned int)d.bin.length()); + fflush(_distLog); + } + } + } + } catch ( ... ) {} // ignore bad meta JSON, etc. + } + } + } + } else { + if (_distLog) { + fclose(_distLog); + _distLog = (FILE *)0; + } + } +} + +void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void *data,unsigned int len) +{ + if (!len) return; + try { + const MessageVerb v = (MessageVerb)reinterpret_cast<const uint8_t *>(data)[0]; + switch(v) { + case VERB_GET_LATEST: + case VERB_LATEST: { + nlohmann::json req = OSUtils::jsonParse(std::string(reinterpret_cast<const char *>(data) + 1,len - 1)); + if (req.is_object()) { + const unsigned int rvMaj = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR],0); + const unsigned int rvMin = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0); + const unsigned int rvRev = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0); + const unsigned int rvBld = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0); + const unsigned int rvPlatform = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_PLATFORM],0); + const unsigned int rvArch = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE],0); + const unsigned int rvVendor = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VENDOR],0); + const std::string rvChannel(OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_CHANNEL],"")); + if (v == VERB_GET_LATEST) { + + if (_dist.size() > 0) { + const nlohmann::json *latest = (const nlohmann::json *)0; + const std::string expectedSigner = OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY],""); + unsigned int bestVMaj = rvMaj; + unsigned int bestVMin = rvMin; + unsigned int bestVRev = rvRev; + unsigned int bestVBld = rvBld; + for(std::map< Array<uint8_t,16>,_D >::const_iterator d(_dist.begin());d!=_dist.end();++d) { + if ((OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_PLATFORM],0) == rvPlatform)&& + (OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE],0) == rvArch)&& + (OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VENDOR],0) == rvVendor)&& + (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_CHANNEL],"") == rvChannel)&& + (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY],"") == expectedSigner)) { + const unsigned int dvMaj = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR],0); + const unsigned int dvMin = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0); + const unsigned int dvRev = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0); + const unsigned int dvBld = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0); + if (Utils::compareVersion(dvMaj,dvMin,dvRev,dvBld,bestVMaj,bestVMin,bestVRev,bestVBld) > 0) { + latest = &(d->second.meta); + bestVMaj = dvMaj; + bestVMin = dvMin; + bestVRev = dvRev; + bestVBld = dvBld; + } + } + } + if (latest) { + std::string lj; + lj.push_back((char)VERB_LATEST); + lj.append(OSUtils::jsonDump(*latest)); + _node.sendUserMessage(origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,lj.data(),(unsigned int)lj.length()); + if (_distLog) { + fprintf(_distLog,"%.10llx GET_LATEST %u.%u.%u_%u platform %u arch %u vendor %u channel %s -> LATEST %u.%u.%u_%u" ZT_EOL_S,(unsigned long long)origin,rvMaj,rvMin,rvRev,rvBld,rvPlatform,rvArch,rvVendor,rvChannel.c_str(),bestVMaj,bestVMin,bestVRev,bestVBld); + fflush(_distLog); + } + } + } // else no reply, since we have nothing to distribute + + } else { // VERB_LATEST + + if ((origin == ZT_SOFTWARE_UPDATE_SERVICE)&& + (Utils::compareVersion(rvMaj,rvMin,rvRev,rvBld,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD) > 0)&& + (OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY],"") == ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY)) { + const unsigned long len = (unsigned long)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE],0); + const std::string hash = OSUtils::jsonBinFromHex(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]); + if ((len <= ZT_SOFTWARE_UPDATE_MAX_SIZE)&&(hash.length() >= 16)) { + if (_latestMeta != req) { + _latestMeta = req; + _latestValid = false; + + OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_META_FILENAME).c_str()); + OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str()); + + _download = std::string(); + memcpy(_downloadHashPrefix.data,hash.data(),16); + _downloadLength = len; + } + + if ((_downloadLength > 0)&&(_download.length() < _downloadLength)) { + Buffer<128> gd; + gd.append((uint8_t)VERB_GET_DATA); + gd.append(_downloadHashPrefix.data,16); + gd.append((uint32_t)_download.length()); + _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); + //printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); + } + } + } + + } + } + } break; + + case VERB_GET_DATA: + if ((len >= 21)&&(_dist.size() > 0)) { + unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 17) << 24; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20); + //printf("<< GET_DATA @%u from %.10llx for %s\n",(unsigned int)idx,origin,Utils::hex(reinterpret_cast<const uint8_t *>(data) + 1,16).c_str()); + std::map< Array<uint8_t,16>,_D >::iterator d(_dist.find(Array<uint8_t,16>(reinterpret_cast<const uint8_t *>(data) + 1))); + if ((d != _dist.end())&&(idx < (unsigned long)d->second.bin.length())) { + Buffer<ZT_SOFTWARE_UPDATE_CHUNK_SIZE + 128> buf; + buf.append((uint8_t)VERB_DATA); + buf.append(reinterpret_cast<const uint8_t *>(data) + 1,16); + buf.append((uint32_t)idx); + buf.append(d->second.bin.data() + idx,std::min((unsigned long)ZT_SOFTWARE_UPDATE_CHUNK_SIZE,(unsigned long)(d->second.bin.length() - idx))); + _node.sendUserMessage(origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,buf.data(),buf.size()); + //printf(">> DATA @%u\n",(unsigned int)idx); + } + } + break; + + case VERB_DATA: + if ((len >= 21)&&(_downloadLength > 0)&&(!memcmp(_downloadHashPrefix.data,reinterpret_cast<const uint8_t *>(data) + 1,16))) { + unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 17) << 24; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8; + idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20); + //printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_download.length()); + if (idx == (unsigned long)_download.length()) { + _download.append(reinterpret_cast<const char *>(data) + 21,len - 21); + if (_download.length() < _downloadLength) { + Buffer<128> gd; + gd.append((uint8_t)VERB_GET_DATA); + gd.append(_downloadHashPrefix.data,16); + gd.append((uint32_t)_download.length()); + _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); + //printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); + } + } + } + break; + + default: + break; + } + } catch ( ... ) { + // Discard bad messages + } +} + +bool SoftwareUpdater::check(const uint64_t now) +{ + if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) { + _lastCheckTime = now; + char tmp[512]; + const unsigned int len = Utils::snprintf(tmp,sizeof(tmp), + "%c{\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "\":\"%s\"," + "\"" ZT_SOFTWARE_UPDATE_JSON_PLATFORM "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_VENDOR "\":%d," + "\"" ZT_SOFTWARE_UPDATE_JSON_CHANNEL "\":\"%s\"}", + (char)VERB_GET_LATEST, + ZEROTIER_ONE_VERSION_MAJOR, + ZEROTIER_ONE_VERSION_MINOR, + ZEROTIER_ONE_VERSION_REVISION, + ZEROTIER_ONE_VERSION_BUILD, + ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY, + ZT_BUILD_PLATFORM, + ZT_BUILD_ARCHITECTURE, + (int)ZT_VENDOR_ZEROTIER, + _channel.c_str()); + _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len); + //printf(">> GET_LATEST\n"); + } + + if (_latestValid) + return true; + + if (_downloadLength > 0) { + if (_download.length() >= _downloadLength) { + // This is the very important security validation part that makes sure + // this software update doesn't have cooties. + + const std::string metaPath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_META_FILENAME); + const std::string binPath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME); + + try { + // (1) Check the hash itself to make sure the image is basically okay + uint8_t sha512[ZT_SHA512_DIGEST_LEN]; + SHA512::hash(sha512,_download.data(),(unsigned int)_download.length()); + if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"~")) { + // (2) Check signature by signing authority + std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE])); + if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_download.data(),(unsigned int)_download.length(),sig.data(),(unsigned int)sig.length())) { + // (3) Try to save file, and if so we are good. + if (OSUtils::writeFile(metaPath.c_str(),OSUtils::jsonDump(_latestMeta)) && OSUtils::writeFile(binPath.c_str(),_download)) { + OSUtils::lockDownFile(metaPath.c_str(),false); + OSUtils::lockDownFile(binPath.c_str(),false); + _latestValid = true; + //printf("VALID UPDATE\n%s\n",OSUtils::jsonDump(_latestMeta).c_str()); + _download = std::string(); + _downloadLength = 0; + return true; + } + } + } + } catch ( ... ) {} // any exception equals verification failure + + // If we get here, checks failed. + //printf("INVALID UPDATE (!!!)\n%s\n",OSUtils::jsonDump(_latestMeta).c_str()); + OSUtils::rm(metaPath.c_str()); + OSUtils::rm(binPath.c_str()); + _latestMeta = nlohmann::json(); + _latestValid = false; + _download = std::string(); + _downloadLength = 0; + } else { + Buffer<128> gd; + gd.append((uint8_t)VERB_GET_DATA); + gd.append(_downloadHashPrefix.data,16); + gd.append((uint32_t)_download.length()); + _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); + //printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); + } + } + + return false; +} + +void SoftwareUpdater::apply() +{ + std::string updatePath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME); + if ((_latestMeta.is_object())&&(_latestValid)&&(OSUtils::fileExists(updatePath.c_str(),false))) { +#ifdef __WINDOWS__ + std::string cmdArgs(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS],"")); + if (cmdArgs.length() > 0) { + updatePath.push_back(' '); + updatePath.append(cmdArgs); + } + STARTUPINFOA si; + PROCESS_INFORMATION pi; + memset(&si,0,sizeof(si)); + memset(&pi,0,sizeof(pi)); + CreateProcessA(NULL,const_cast<LPSTR>(updatePath.c_str()),NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi); +#else + char *argv[256]; + unsigned long ac = 0; + argv[ac++] = const_cast<char *>(updatePath.c_str()); + std::vector<std::string> argsSplit(OSUtils::split(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS],"").c_str()," ","\\","\"")); + for(std::vector<std::string>::iterator a(argsSplit.begin());a!=argsSplit.end();++a) { + argv[ac] = const_cast<char *>(a->c_str()); + if (++ac == 255) break; + } + argv[ac] = (char *)0; + chmod(updatePath.c_str(),0700); + execv(updatePath.c_str(),argv); +#endif + } +} + +} // namespace ZeroTier diff --git a/service/SoftwareUpdater.hpp b/service/SoftwareUpdater.hpp new file mode 100644 index 00000000..e6e6b0c7 --- /dev/null +++ b/service/SoftwareUpdater.hpp @@ -0,0 +1,215 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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/>. + */ + +#ifndef ZT_SOFTWAREUPDATER_HPP +#define ZT_SOFTWAREUPDATER_HPP + +#include <stdint.h> +#include <stdio.h> + +#include <vector> +#include <map> +#include <string> + +#include "../include/ZeroTierOne.h" + +#include "../node/Identity.hpp" +#include "../node/Array.hpp" +#include "../node/Packet.hpp" + +#include "../ext/json/json.hpp" + +/** + * VERB_USER_MESSAGE type ID for software update messages + */ +#define ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE 100 + +/** + * ZeroTier address of node that provides software updates + */ +#define ZT_SOFTWARE_UPDATE_SERVICE 0xb1d366e81fULL + +/** + * ZeroTier identity that must be used to sign software updates + * + * df24360f3e - update-signing-key-0010 generated Fri Jan 13th, 2017 at 4:05pm PST + */ +#define ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY "df24360f3e:0:06072642959c8dfb68312904d74d90197c8a7692697caa1b3fd769eca714f4370fab462fcee6ebcb5fffb63bc5af81f28a2514b2cd68daabb42f7352c06f21db" + +/** + * Chunk size for in-band downloads (can be changed, designed to always fit in one UDP packet easily) + */ +#define ZT_SOFTWARE_UPDATE_CHUNK_SIZE (ZT_PROTO_MAX_PACKET_LENGTH - 128) + +/** + * Sanity limit for the size of an update binary image + */ +#define ZT_SOFTWARE_UPDATE_MAX_SIZE (1024 * 1024 * 256) + +/** + * How often (ms) do we check? + */ +//#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD (60 * 60 * 1000) +#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD 5000 + +/** + * Default update channel + */ +#define ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL "release" + +/** + * Filename for latest update's meta JSON + */ +#define ZT_SOFTWARE_UPDATE_META_FILENAME "latest-update.json" + +/** + * Filename for latest update's binary image + */ +#define ZT_SOFTWARE_UPDATE_BIN_FILENAME "latest-update.exe" + +#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "versionMajor" +#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "versionMinor" +#define ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "versionRev" +#define ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "versionBuild" +#define ZT_SOFTWARE_UPDATE_JSON_PLATFORM "platform" +#define ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "arch" +#define ZT_SOFTWARE_UPDATE_JSON_VENDOR "vendor" +#define ZT_SOFTWARE_UPDATE_JSON_CHANNEL "channel" +#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY "updateSigner" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE "updateSig" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH "updateHash" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE "updateSize" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS "updateExecArgs" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_URL "updateUrl" + +namespace ZeroTier { + +class Node; + +/** + * This class handles retrieving and executing updates, or serving them + */ +class SoftwareUpdater +{ +public: + /** + * Each message begins with an 8-bit message verb + */ + enum MessageVerb + { + /** + * Payload: JSON containing current system platform, version, etc. + */ + VERB_GET_LATEST = 1, + + /** + * Payload: JSON describing latest update for this target. (No response is sent if there is none.) + */ + VERB_LATEST = 2, + + /** + * Payload: + * <[16] first 128 bits of hash of data object> + * <[4] 32-bit index of chunk to get> + */ + VERB_GET_DATA = 3, + + /** + * Payload: + * <[16] first 128 bits of hash of data object> + * <[4] 32-bit index of chunk> + * <[...] chunk data> + */ + VERB_DATA = 4 + }; + + SoftwareUpdater(Node &node,const std::string &homePath); + ~SoftwareUpdater(); + + /** + * Set whether or not we will distribute updates + * + * @param distribute If true, scan update-dist.d now and distribute updates found there -- if false, clear and stop distributing + */ + void setUpdateDistribution(bool distribute); + + /** + * Handle a software update user message + * + * @param origin ZeroTier address of message origin + * @param data Message payload + * @param len Length of message + */ + void handleSoftwareUpdateUserMessage(uint64_t origin,const void *data,unsigned int len); + + /** + * Check for updates and do other update-related housekeeping + * + * It should be called about every 10 seconds. + * + * @return True if we've downloaded and verified an update + */ + bool check(const uint64_t now); + + /** + * @return Meta-data for downloaded update or NULL if none + */ + inline const nlohmann::json &pending() const { return _latestMeta; } + + /** + * Apply any ready update now + * + * Depending on the platform this function may never return and may forcibly + * exit the process. It does nothing if no update is ready. + */ + void apply(); + + /** + * Set software update channel + * + * @param channel 'release', 'beta', etc. + */ + inline void setChannel(const std::string &channel) { _channel = channel; } + +private: + Node &_node; + uint64_t _lastCheckTime; + std::string _homePath; + std::string _channel; + FILE *_distLog; + + // Offered software updates if we are an update host (we have update-dist.d and update hosting is enabled) + struct _D + { + nlohmann::json meta; + std::string bin; + }; + std::map< Array<uint8_t,16>,_D > _dist; // key is first 16 bytes of hash + + nlohmann::json _latestMeta; + bool _latestValid; + + std::string _download; + Array<uint8_t,16> _downloadHashPrefix; + unsigned long _downloadLength; +}; + +} // namespace ZeroTier + +#endif @@ -34,4 +34,13 @@ */ #define ZEROTIER_ONE_VERSION_REVISION 17 +/** + * Build version + * + * This starts at 0 for each major.minor.rev tuple and can be incremented + * to force a minor update without an actual version number change. It's + * not part of the actual release version number. + */ +#define ZEROTIER_ONE_VERSION_BUILD 0 + #endif diff --git a/windows/WinUI/APIHandler.cs b/windows/WinUI/APIHandler.cs index 177d336f..a762fff7 100644 --- a/windows/WinUI/APIHandler.cs +++ b/windows/WinUI/APIHandler.cs @@ -61,7 +61,7 @@ namespace WinUI String curPath = System.Reflection.Assembly.GetEntryAssembly().Location; int index = curPath.LastIndexOf("\\"); curPath = curPath.Substring(0, index); - ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", globalZtDir + " " + localZtDir); + ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", "\""+globalZtDir+"\"" + " " + "\""+localZtDir+"\""); startInfo.Verb = "runas"; diff --git a/windows/WinUI/ToolbarItem.xaml.cs b/windows/WinUI/ToolbarItem.xaml.cs index 6ecac6c2..29791a4e 100644 --- a/windows/WinUI/ToolbarItem.xaml.cs +++ b/windows/WinUI/ToolbarItem.xaml.cs @@ -138,6 +138,8 @@ namespace WinUI netListLocation.X = netListView.Left; netListLocation.Y = netListView.Top; } + + netListView.Activate(); } private void ShowNetworksClosed(object sender, System.EventArgs e) @@ -169,6 +171,10 @@ namespace WinUI joinNetLocation.Y = joinNetView.Top; } } + else + { + joinNetView.Activate(); + } } private void JoinNetworkClosed(object sender, System.EventArgs e) @@ -200,6 +206,10 @@ namespace WinUI aboutViewLocation.Y = aboutView.Top; } } + else + { + aboutView.Activate(); + } } private void AboutClosed(object sender, System.EventArgs e) @@ -231,6 +241,10 @@ namespace WinUI prefsViewLocation.Y = prefsView.Top; } } + else + { + prefsView.Activate(); + } } private void PreferencesClosed(object sender, System.EventArgs e) diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index d19eee6c..2a6545eb 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -75,6 +75,7 @@ <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" /> <ClCompile Include="..\..\service\ControlPlane.cpp" /> <ClCompile Include="..\..\service\OneService.cpp" /> + <ClCompile Include="..\..\service\SoftwareUpdater.cpp" /> <ClCompile Include="ServiceBase.cpp" /> <ClCompile Include="ServiceInstaller.cpp" /> <ClCompile Include="ZeroTierOneService.cpp" /> @@ -169,8 +170,8 @@ <ClInclude Include="..\..\osdep\Thread.hpp" /> <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" /> <ClInclude Include="..\..\service\ControlPlane.hpp" /> - <ClInclude Include="..\..\service\ControlPlaneSubsystem.hpp" /> <ClInclude Include="..\..\service\OneService.hpp" /> + <ClInclude Include="..\..\service\SoftwareUpdater.hpp" /> <ClInclude Include="..\..\version.h" /> <ClInclude Include="resource.h" /> <ClInclude Include="ServiceBase.h" /> @@ -254,7 +255,7 @@ <SDLCheck>true</SDLCheck> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions> <DisableSpecificWarnings>4996</DisableSpecificWarnings> </ClCompile> <Link> @@ -270,7 +271,7 @@ <SDLCheck>true</SDLCheck> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions> <MultiProcessorCompilation>false</MultiProcessorCompilation> <DisableSpecificWarnings>4996</DisableSpecificWarnings> </ClCompile> @@ -290,7 +291,7 @@ <SDLCheck>true</SDLCheck> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>STATICLIB;ZT_OFFICIAL_RELEASE;ZT_AUTO_UPDATE;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet> <StringPooling>true</StringPooling> @@ -316,7 +317,7 @@ <SDLCheck>true</SDLCheck> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>STATICLIB;ZT_OFFICIAL_RELEASE;ZT_AUTO_UPDATE;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet> <StringPooling>true</StringPooling> diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index 1fa39abd..8a426d4b 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -264,6 +264,9 @@ <ClCompile Include="..\..\controller\JSONDB.cpp"> <Filter>Source Files\controller</Filter> </ClCompile> + <ClCompile Include="..\..\service\SoftwareUpdater.cpp"> + <Filter>Source Files\service</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="resource.h"> @@ -293,9 +296,6 @@ <ClInclude Include="..\..\service\ControlPlane.hpp"> <Filter>Header Files\service</Filter> </ClInclude> - <ClInclude Include="..\..\service\ControlPlaneSubsystem.hpp"> - <Filter>Header Files\service</Filter> - </ClInclude> <ClInclude Include="..\..\service\OneService.hpp"> <Filter>Header Files\service</Filter> </ClInclude> @@ -554,6 +554,9 @@ <ClInclude Include="..\..\osdep\ManagedRoute.hpp"> <Filter>Header Files\osdep</Filter> </ClInclude> + <ClInclude Include="..\..\service\SoftwareUpdater.hpp"> + <Filter>Header Files\service</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="ZeroTierOne.rc"> |