From bcd05fbdfa7e340ef4df962773bb7c32cf5013c2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko <adam.ierymenko@gmail.com> Date: Tue, 9 Aug 2016 09:34:13 -0700 Subject: [PATCH] Chunking of network config replies. --- node/Dictionary.hpp | 34 +++++++++++++++++++++++++++++ node/IncomingPacket.cpp | 48 +++++++++++++++++++++++++---------------- node/NetworkConfig.hpp | 2 ++ node/Packet.hpp | 14 +++++------- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 59fc4bbf5..5d453fd95 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -23,6 +23,7 @@ #include "Utils.hpp" #include "Buffer.hpp" #include "Address.hpp" +#include "C25519.hpp" #include <stdint.h> @@ -443,6 +444,39 @@ public: return found; } + /** + * Sign this Dictionary, replacing any previous signature + * + * @param sigKey Key to use for signature in dictionary + * @param kp Key pair to sign with + */ + inline void wrapWithSignature(const char *sigKey,const C25519::Pair &kp) + { + this->erase(sigKey); + C25519::Signature sig(C25519::sign(kp,this->data(),this->sizeBytes())); + this->add(sigKey,sig.data,ZT_C25519_SIGNATURE_LEN); + } + + /** + * Verify signature (and erase signature key) + * + * This erases this Dictionary's signature key (if present) and verifies + * the signature. The key is erased to render the Dictionary into the + * original unsigned form it was signed in for verification purposes. + * + * @param sigKey Key to use for signature in dictionary + * @param pk Public key to check against + * @return True if signature was present and valid + */ + inline bool unwrapAndVerify(const char *sigKey,const C25519::Public &pk) + { + char sig[ZT_C25519_SIGNATURE_LEN+1]; + if (this->get(sigKey,sig,sizeof(sig)) != ZT_C25519_SIGNATURE_LEN) + return false; + this->erase(sigKey); + return C25519::verify(pk,this->data(),this->sizeBytes(),sig); + } + /** * @return Dictionary data as a 0-terminated C-string */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index fae689d18..147f54da1 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -719,35 +719,46 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength); const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength); - //const uint64_t haveRevision = ((ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength + 8) <= size()) ? at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength) : 0ULL; - const unsigned int h = hops(); - const uint64_t pid = packetId(); - peer->received(_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP); + const uint64_t requestPacketId = packetId(); + peer->received(_localAddress,_remoteAddress,h,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP); if (RR->localNetworkController) { NetworkConfig netconf; switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,RR->identity,peer->identity(),nwid,metaData,netconf)) { case NetworkController::NETCONF_QUERY_OK: { - Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf; - if (netconf.toDictionary(dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); - outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(pid); - outp.append(nwid); - const unsigned int dlen = dconf.sizeBytes(); - outp.append((uint16_t)dlen); - outp.append((const void *)dconf.data(),dlen); - outp.compress(); - RR->sw->send(outp,true,0); + Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(); + try { + if (netconf.toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) { + const unsigned int totalSize = dconf->sizeBytes(); + unsigned int chunkPtr = 0; + while (chunkPtr < totalSize) { + const unsigned int chunkLen = std::min(totalSize - chunkPtr,(unsigned int)(ZT_PROTO_MAX_PACKET_LENGTH - (ZT_PROTO_MIN_PACKET_LENGTH + 32))); + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(requestPacketId); + outp.append(nwid); + outp.append((uint16_t)chunkLen); + outp.append((const void *)(dconf->data() + chunkPtr),chunkLen); + outp.append((uint32_t)totalSize); + outp.append((uint32_t)chunkPtr); + outp.compress(); + RR->sw->send(outp,true,0); + chunkPtr += chunkLen; + } + } + delete dconf; + } catch ( ... ) { + delete dconf; + throw; } } break; case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(pid); + outp.append(requestPacketId); outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); outp.append(nwid); outp.armor(peer->key(),true); @@ -757,7 +768,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons case NetworkController::NETCONF_QUERY_ACCESS_DENIED: { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(pid); + outp.append(requestPacketId); outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_); outp.append(nwid); outp.armor(peer->key(),true); @@ -765,7 +776,6 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons } break; case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: - // TRACE("NETWORK_CONFIG_REQUEST failed: internal error: %s",netconf.get("error","(unknown)").c_str()); break; case NetworkController::NETCONF_QUERY_IGNORE: @@ -779,7 +789,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(pid); + outp.append(requestPacketId); outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); outp.append(nwid); outp.armor(peer->key(),true); diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 907da936e..3682c466b 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -135,6 +135,8 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP" // tags (binary blobs) #define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG" +// curve25519 signature +#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519" // Legacy fields -- these are obsoleted but are included when older clients query diff --git a/node/Packet.hpp b/node/Packet.hpp index dce9f208f..9d4c82894 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -724,7 +724,7 @@ public: * <[8] 64-bit network ID> * <[2] 16-bit length of request meta-data dictionary> * <[...] string-serialized request meta-data> - * [<[8] 64-bit timestamp of netconf we currently have>] + * <[8] 64-bit timestamp of netconf we currently have> * * This message requests network configuration from a node capable of * providing it. If the optional revision is included, a response is @@ -732,14 +732,10 @@ public: * * OK response payload: * <[8] 64-bit network ID> - * <[2] 16-bit length of network configuration dictionary field> - * <[...] network configuration dictionary (or fragment)> - * [<[4] 32-bit total length of assembled dictionary>] - * [<[4] 32-bit index of fragment in this reply>] - * - * Fields after the dictionary are extensions to support multipart - * sending of large network configs. If they are not present the - * sent config must be assumed to be whole. + * <[2] 16-bit length of network configuration dictionary chunk> + * <[...] network configuration dictionary (may be incomplete)> + * <[4] 32-bit total length of assembled dictionary> + * <[4] 32-bit index of chunk in this reply> * * ERROR response payload: * <[8] 64-bit network ID>