mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-08 04:10:13 +00:00
Integrating new crypto, work still in progress...
This commit is contained in:
parent
3b2d98e7dc
commit
ceb024ab03
@ -71,14 +71,64 @@ std::string Identity::toString(bool includePrivate) const
|
|||||||
|
|
||||||
r.append(_address.toString());
|
r.append(_address.toString());
|
||||||
r.append(":2:"); // 2 == IDENTITY_TYPE_C25519
|
r.append(":2:"); // 2 == IDENTITY_TYPE_C25519
|
||||||
|
r.append(Utils::hex(_publicKey.data,_publicKey.size()));
|
||||||
|
r.push_back(':');
|
||||||
|
r.append(Utils::hex(_signature.data,_signature.size()));
|
||||||
|
if ((_privateKey)&&(includePrivate)) {
|
||||||
|
r.push_back(':');
|
||||||
|
r.append(Utils::hex(_privateKey.data,_privateKey.size()));
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Identity::fromString(const char *str)
|
bool Identity::fromString(const char *str)
|
||||||
{
|
{
|
||||||
|
char *saveptr = (char *)0;
|
||||||
|
char tmp[4096];
|
||||||
|
if (!Utils::scopy(tmp,sizeof(tmp),str))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delete _privateKey;
|
||||||
|
_privateKey = (C25519::Private *)0;
|
||||||
|
|
||||||
|
int fno = 0;
|
||||||
|
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
|
||||||
|
switch(fno++) {
|
||||||
|
case 0:
|
||||||
|
_address = Address(f);
|
||||||
|
if (_address.isReserved())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (strcmp(f,"2"))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (Utils::unhex(f,_publicKey.data,_publicKey.size()) != _publicKey.size())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (Utils::unhex(f,_signature.data,_signature.size()) != _signature.size())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
_privateKey = new C25519::Private();
|
||||||
|
if (Utils::unhex(f,_privateKey->data,_privateKey->size()) != _privateKey->size())
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fno < 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are fixed parameters and can't be changed without a new
|
||||||
|
// identity type.
|
||||||
#define ZT_IDENTITY_DERIVEADDRESS_DIGESTS 540672
|
#define ZT_IDENTITY_DERIVEADDRESS_DIGESTS 540672
|
||||||
#define ZT_IDENTITY_DERIVEADDRESS_ROUNDS 4
|
#define ZT_IDENTITY_DERIVEADDRESS_ROUNDS 4
|
||||||
|
|
||||||
@ -91,7 +141,7 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
|
|||||||
* unfortunately cannot be used here. If that were used, it would be
|
* unfortunately cannot be used here. If that were used, it would be
|
||||||
* equivalently costly to simply increment/vary the public key and find
|
* equivalently costly to simply increment/vary the public key and find
|
||||||
* a collision as it would be to find the address. We need something
|
* a collision as it would be to find the address. We need something
|
||||||
* that creates a costly 1:~1 mapping from key to address, hence this odd
|
* that creates a costly 1:~1 mapping from key to address, hence this
|
||||||
* algorithm.
|
* algorithm.
|
||||||
*
|
*
|
||||||
* Search for "sequential memory hard algorithm" for academic references
|
* Search for "sequential memory hard algorithm" for academic references
|
||||||
|
@ -57,7 +57,6 @@
|
|||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
#include "InetAddress.hpp"
|
#include "InetAddress.hpp"
|
||||||
#include "Salsa20.hpp"
|
#include "Salsa20.hpp"
|
||||||
#include "HMAC.hpp"
|
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
#include "NodeConfig.hpp"
|
#include "NodeConfig.hpp"
|
||||||
#include "Defaults.hpp"
|
#include "Defaults.hpp"
|
||||||
|
@ -35,8 +35,6 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
@ -54,7 +52,8 @@
|
|||||||
#include "InetAddress.hpp"
|
#include "InetAddress.hpp"
|
||||||
#include "Peer.hpp"
|
#include "Peer.hpp"
|
||||||
#include "Salsa20.hpp"
|
#include "Salsa20.hpp"
|
||||||
#include "HMAC.hpp"
|
#include "Poly1305.hpp"
|
||||||
|
#include "SHA512.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
@ -68,10 +67,11 @@ NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken)
|
|||||||
_r(renv),
|
_r(renv),
|
||||||
_controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this)
|
_controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this)
|
||||||
{
|
{
|
||||||
SHA256_CTX sha;
|
{
|
||||||
SHA256_Init(&sha);
|
unsigned int csk[64];
|
||||||
SHA256_Update(&sha,authToken,strlen(authToken));
|
SHA512::hash(authToken,strlen(authToken));
|
||||||
SHA256_Final(_controlSocketKey,&sha);
|
memcpy(_controlSocketKey,csk,32);
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string,bool> networksDotD(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));
|
std::map<std::string,bool> networksDotD(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));
|
||||||
std::set<uint64_t> nwids;
|
std::set<uint64_t> nwids;
|
||||||
@ -249,12 +249,13 @@ std::vector<std::string> NodeConfig::execute(const char *command)
|
|||||||
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMessage(const void *key,unsigned long conversationId,const std::vector<std::string> &payload)
|
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMessage(const void *key,unsigned long conversationId,const std::vector<std::string> &payload)
|
||||||
throw(std::out_of_range)
|
throw(std::out_of_range)
|
||||||
{
|
{
|
||||||
char hmac[32];
|
char poly1305tag[ZT_POLY1305_MAC_LEN];
|
||||||
|
char iv[8];
|
||||||
char keytmp[32];
|
char keytmp[32];
|
||||||
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets;
|
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets;
|
||||||
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet;
|
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet;
|
||||||
|
|
||||||
packet.setSize(16); // room for HMAC and IV
|
packet.setSize(16); // room for poly1305 auth tag and IV
|
||||||
packet.append((uint32_t)(conversationId & 0xffffffff));
|
packet.append((uint32_t)(conversationId & 0xffffffff));
|
||||||
|
|
||||||
for(unsigned int i=0;i<payload.size();++i) {
|
for(unsigned int i=0;i<payload.size();++i) {
|
||||||
@ -262,20 +263,21 @@ std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMe
|
|||||||
packet.append((unsigned char)0);
|
packet.append((unsigned char)0);
|
||||||
|
|
||||||
if (((i + 1) >= payload.size())||((packet.size() + payload[i + 1].length() + 1) >= packet.capacity())) {
|
if (((i + 1) >= payload.size())||((packet.size() + payload[i + 1].length() + 1) >= packet.capacity())) {
|
||||||
Utils::getSecureRandom(packet.field(8,8),8);
|
Utils::getSecureRandom(iv,8);
|
||||||
|
memcpy(packet.field(8,8),iv,8);
|
||||||
|
|
||||||
Salsa20 s20(key,256,packet.field(8,8));
|
Salsa20 s20(key,256,iv);
|
||||||
s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
|
s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
|
||||||
|
|
||||||
memcpy(keytmp,key,32);
|
memcpy(keytmp,key,32);
|
||||||
for(unsigned int i=0;i<32;++i)
|
for(unsigned int i=0;i<8;++i)
|
||||||
keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
|
keytmp[i] ^= iv[i]; // can't reuse poly1305 keys, so mangle key with IV each time
|
||||||
HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac);
|
Poly1305::compute(poly1305tag,packet.field(16,packet.size() - 16),packet.size() - 16,keytmp);
|
||||||
memcpy(packet.field(0,8),hmac,8);
|
memcpy(packet.field(0,8),poly1305tag,8);
|
||||||
|
|
||||||
packets.push_back(packet);
|
packets.push_back(packet);
|
||||||
|
|
||||||
packet.setSize(16); // room for HMAC and IV
|
packet.setSize(16); // room for poly1305 auth tag and IV
|
||||||
packet.append((uint32_t)(conversationId & 0xffffffff));
|
packet.append((uint32_t)(conversationId & 0xffffffff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,8 +287,9 @@ std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMe
|
|||||||
|
|
||||||
bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector<std::string> &payload)
|
bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector<std::string> &payload)
|
||||||
{
|
{
|
||||||
char hmac[32];
|
char poly1305tag[ZT_POLY1305_MAC_LEN];
|
||||||
char keytmp[32];
|
char keytmp[32];
|
||||||
|
char iv[8];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (len < 20)
|
if (len < 20)
|
||||||
@ -295,10 +298,11 @@ bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,uns
|
|||||||
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet(data,len);
|
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet(data,len);
|
||||||
|
|
||||||
memcpy(keytmp,key,32);
|
memcpy(keytmp,key,32);
|
||||||
for(unsigned int i=0;i<32;++i)
|
memcpy(iv,packet.field(8,8),8);
|
||||||
keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
|
for(unsigned int i=0;i<8;++i)
|
||||||
HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac);
|
keytmp[i] ^= iv[i];
|
||||||
if (memcmp(packet.field(0,8),hmac,8))
|
Poly1305::compute(poly1305tag,packet.field(16,packet.size() - 16),packet.size() - 16,keytmp);
|
||||||
|
if (!Utils::secureEq(packet.field(0,8),poly1305tag,8))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Salsa20 s20(key,256,packet.field(8,8));
|
Salsa20 s20(key,256,packet.field(8,8));
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "HMAC.hpp"
|
#include "Poly1305.hpp"
|
||||||
#include "Salsa20.hpp"
|
#include "Salsa20.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
@ -65,7 +65,7 @@
|
|||||||
* Header flag indicating that a packet is encrypted with Salsa20
|
* Header flag indicating that a packet is encrypted with Salsa20
|
||||||
*
|
*
|
||||||
* If this is not set, then the packet's payload is in the clear and the
|
* If this is not set, then the packet's payload is in the clear and the
|
||||||
* HMAC is over this (since there is no ciphertext). Otherwise the HMAC is
|
* MAC is over this (since there is no ciphertext). Otherwise the MAC is
|
||||||
* of the ciphertext after encryption.
|
* of the ciphertext after encryption.
|
||||||
*/
|
*/
|
||||||
#define ZT_PROTO_FLAG_ENCRYPTED 0x80
|
#define ZT_PROTO_FLAG_ENCRYPTED 0x80
|
||||||
@ -89,7 +89,7 @@
|
|||||||
#define ZT_PACKET_IDX_DEST 8
|
#define ZT_PACKET_IDX_DEST 8
|
||||||
#define ZT_PACKET_IDX_SOURCE 13
|
#define ZT_PACKET_IDX_SOURCE 13
|
||||||
#define ZT_PACKET_IDX_FLAGS 18
|
#define ZT_PACKET_IDX_FLAGS 18
|
||||||
#define ZT_PACKET_IDX_HMAC 19
|
#define ZT_PACKET_IDX_MAC 19
|
||||||
#define ZT_PACKET_IDX_VERB 27
|
#define ZT_PACKET_IDX_VERB 27
|
||||||
#define ZT_PACKET_IDX_PAYLOAD 28
|
#define ZT_PACKET_IDX_PAYLOAD 28
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ namespace ZeroTier {
|
|||||||
* <[5] destination ZT address>
|
* <[5] destination ZT address>
|
||||||
* <[5] source ZT address>
|
* <[5] source ZT address>
|
||||||
* <[1] flags (LS 5 bits) and ZT hop count (MS 3 bits)>
|
* <[1] flags (LS 5 bits) and ZT hop count (MS 3 bits)>
|
||||||
* <[8] first 8 bytes of 32-byte HMAC-SHA-256 MAC>
|
* <[8] 8-bit MAC (currently first 8 bytes of poly1305 tag)>
|
||||||
* [... -- begin encryption envelope -- ...]
|
* [... -- begin encryption envelope -- ...]
|
||||||
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
|
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
|
||||||
* [... verb-specific payload ...]
|
* [... verb-specific payload ...]
|
||||||
@ -770,39 +770,39 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the HMAC of this packet's payload and set HMAC field
|
* Generate a message authenticationc code and set MAC field of packet
|
||||||
*
|
*
|
||||||
* For encrypted packets, this must be called after encryption.
|
* For encrypted packets, this must be called after encryption.
|
||||||
*
|
*
|
||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline void hmacSet(const void *key)
|
inline void macSet(const void *key)
|
||||||
{
|
{
|
||||||
unsigned char mac[32];
|
unsigned char mac[16];
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
unsigned int hmacLen = (size() >= ZT_PACKET_IDX_VERB) ? (size() - ZT_PACKET_IDX_VERB) : 0;
|
unsigned int macLen = (size() >= ZT_PACKET_IDX_VERB) ? (size() - ZT_PACKET_IDX_VERB) : 0;
|
||||||
HMAC::sha256(key2,sizeof(key2),field(ZT_PACKET_IDX_VERB,hmacLen),hmacLen,mac);
|
Poly1305::compute(mac,field(ZT_PACKET_IDX_VERB,macLen),macLen,key2);
|
||||||
memcpy(field(ZT_PACKET_IDX_HMAC,8),mac,8);
|
memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the HMAC of this packet's payload
|
* Check the MAC of this packet's payload
|
||||||
*
|
*
|
||||||
* For encrypted packets, this must be checked before decryption.
|
* For encrypted packets, this must be checked before decryption.
|
||||||
*
|
*
|
||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline bool hmacVerify(const void *key) const
|
inline bool macVerify(const void *key) const
|
||||||
{
|
{
|
||||||
unsigned char mac[32];
|
unsigned char mac[16];
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
if (size() < ZT_PACKET_IDX_VERB)
|
if (size() < ZT_PACKET_IDX_VERB)
|
||||||
return false; // incomplete packets fail
|
return false; // incomplete packets fail
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
unsigned int hmacLen = size() - ZT_PACKET_IDX_VERB;
|
unsigned int macLen = size() - ZT_PACKET_IDX_VERB;
|
||||||
HMAC::sha256(key2,sizeof(key2),field(ZT_PACKET_IDX_VERB,hmacLen),hmacLen,mac);
|
Poly1305::compute(mac,field(ZT_PACKET_IDX_VERB,macLen),macLen,key2);
|
||||||
return (!memcmp(field(ZT_PACKET_IDX_HMAC,8),mac,8));
|
return Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -895,38 +895,31 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Deterministically mangle a 256-bit crypto key based on packet characteristics
|
* Deterministically mangle a 256-bit crypto key based on packet
|
||||||
*
|
|
||||||
* This takes the static agreed-upon input key and mangles it using
|
|
||||||
* info from the packet. This serves two purposes:
|
|
||||||
*
|
|
||||||
* (1) It reduces the (already minute) probability of a duplicate key /
|
|
||||||
* IV combo, which is good since keys are extremely long-lived. Another
|
|
||||||
* way of saying this is that it increases the effective IV size by
|
|
||||||
* using other parts of the packet as IV material.
|
|
||||||
* (2) It causes HMAC to fail should any of the following change: ordering
|
|
||||||
* of source and dest addresses, flags, IV, or packet size. HMAC has
|
|
||||||
* no explicit scheme for AAD (additional authenticated data).
|
|
||||||
*
|
|
||||||
* NOTE: this function will have to be changed if the order of any packet
|
|
||||||
* fields or their sizes/padding changes in the spec.
|
|
||||||
*
|
*
|
||||||
* @param in Input key (32 bytes)
|
* @param in Input key (32 bytes)
|
||||||
* @param out Output buffer (32 bytes)
|
* @param out Output buffer (32 bytes)
|
||||||
*/
|
*/
|
||||||
inline void _mangleKey(const unsigned char *in,unsigned char *out) const
|
inline void _mangleKey(const unsigned char *in,unsigned char *out) const
|
||||||
{
|
{
|
||||||
// Random IV (Salsa20 also uses the IV natively, but HMAC doesn't), and
|
// IV and source/destination addresses. Salsa uses the IV natively
|
||||||
// destination and source addresses. Using dest and source addresses
|
// so this is redundant there, but not harmful. But Poly1305 depends
|
||||||
// gives us a (likely) different key space for a->b vs b->a.
|
// on the key being mangled with the IV. Using the source and
|
||||||
|
// destination addresses bifurcates the key space into a different
|
||||||
|
// key space for each direction of the conversation.
|
||||||
for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18
|
for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18
|
||||||
out[i] = in[i] ^ (unsigned char)(*this)[i];
|
out[i] = in[i] ^ (unsigned char)(*this)[i];
|
||||||
// Flags, but masking off hop count which is altered by forwarding nodes
|
|
||||||
|
// Flags, but with hop count masked off. Hop count is altered by forwarding
|
||||||
|
// nodes. It's one of the only parts of a packet modifiable by people
|
||||||
|
// without the key.
|
||||||
out[18] = in[18] ^ ((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & 0xf8);
|
out[18] = in[18] ^ ((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & 0xf8);
|
||||||
// Raw packet size in bytes -- each raw packet size defines a possibly
|
|
||||||
// different space of keys.
|
// Raw packet size in bytes -- thus each packet size defines a new
|
||||||
|
// key space.
|
||||||
out[19] = in[19] ^ (unsigned char)(size() & 0xff);
|
out[19] = in[19] ^ (unsigned char)(size() & 0xff);
|
||||||
out[20] = in[20] ^ (unsigned char)((size() >> 8) & 0xff); // little endian
|
out[20] = in[20] ^ (unsigned char)((size() >> 8) & 0xff); // little endian
|
||||||
|
|
||||||
// Rest of raw key is used unchanged
|
// Rest of raw key is used unchanged
|
||||||
for(unsigned int i=21;i<32;++i)
|
for(unsigned int i=21;i<32;++i)
|
||||||
out[i] = in[i];
|
out[i] = in[i];
|
||||||
|
@ -75,8 +75,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No saved state? Verify MAC before we proceed.
|
// No saved state? Verify MAC before we proceed.
|
||||||
if (!hmacVerify(peer->macKey())) {
|
if (!macVerify(peer->macKey())) {
|
||||||
TRACE("dropped packet from %s(%s), HMAC authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size());
|
TRACE("dropped packet from %s(%s), authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
|
|||||||
decrypt(peer->cryptKey());
|
decrypt(peer->cryptKey());
|
||||||
} else {
|
} else {
|
||||||
// Unencrypted is tolerated in case we want to run this on
|
// Unencrypted is tolerated in case we want to run this on
|
||||||
// devices where squeezing out cycles matters. HMAC is
|
// devices where squeezing out cycles matters. MAC is
|
||||||
// what's really important. But log it in debug to catch any
|
// what's really important. But log it in debug to catch any
|
||||||
// packets being mistakenly sent in the clear.
|
// packets being mistakenly sent in the clear.
|
||||||
TRACE("ODD: %s from %s(%s) wasn't encrypted",Packet::verbString(verb()),source().toString().c_str(),_remoteAddress.toString().c_str());
|
TRACE("ODD: %s from %s(%s) wasn't encrypted",Packet::verbString(verb()),source().toString().c_str(),_remoteAddress.toString().c_str());
|
||||||
@ -130,7 +130,7 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
|
|||||||
return _doNETWORK_CONFIG_REFRESH(_r,peer);
|
return _doNETWORK_CONFIG_REFRESH(_r,peer);
|
||||||
default:
|
default:
|
||||||
// This might be something from a new or old version of the protocol.
|
// This might be something from a new or old version of the protocol.
|
||||||
// Technically it passed HMAC so the packet is still valid, but we
|
// Technically it passed MAC so the packet is still valid, but we
|
||||||
// ignore it.
|
// ignore it.
|
||||||
TRACE("ignored unrecognized verb %.2x from %s(%s)",(unsigned int)v,source().toString().c_str(),_remoteAddress.toString().c_str());
|
TRACE("ignored unrecognized verb %.2x from %s(%s)",(unsigned int)v,source().toString().c_str(),_remoteAddress.toString().c_str());
|
||||||
return true;
|
return true;
|
||||||
@ -162,7 +162,7 @@ void PacketDecoder::_CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topol
|
|||||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
||||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||||
outp.encrypt(p->cryptKey());
|
outp.encrypt(p->cryptKey());
|
||||||
outp.hmacSet(p->macKey());
|
outp.macSet(p->macKey());
|
||||||
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ void PacketDecoder::_CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topol
|
|||||||
outp.append(req->helloPacketId);
|
outp.append(req->helloPacketId);
|
||||||
outp.append((unsigned char)Packet::ERROR_IDENTITY_INVALID);
|
outp.append((unsigned char)Packet::ERROR_IDENTITY_INVALID);
|
||||||
outp.encrypt(p->cryptKey());
|
outp.encrypt(p->cryptKey());
|
||||||
outp.hmacSet(p->macKey());
|
outp.macSet(p->macKey());
|
||||||
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ void PacketDecoder::_CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topol
|
|||||||
outp.append(req->helloPacketId);
|
outp.append(req->helloPacketId);
|
||||||
outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
|
outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
|
||||||
outp.encrypt(p->cryptKey());
|
outp.encrypt(p->cryptKey());
|
||||||
outp.hmacSet(p->macKey());
|
outp.macSet(p->macKey());
|
||||||
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)
|
|||||||
outp.append(packetId());
|
outp.append(packetId());
|
||||||
outp.append(timestamp);
|
outp.append(timestamp);
|
||||||
outp.encrypt(existingPeer->cryptKey());
|
outp.encrypt(existingPeer->cryptKey());
|
||||||
outp.hmacSet(existingPeer->macKey());
|
outp.macSet(existingPeer->macKey());
|
||||||
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -353,7 +353,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>
|
|||||||
outp.append(packetId());
|
outp.append(packetId());
|
||||||
p->identity().serialize(outp,false);
|
p->identity().serialize(outp,false);
|
||||||
outp.encrypt(peer->cryptKey());
|
outp.encrypt(peer->cryptKey());
|
||||||
outp.hmacSet(peer->macKey());
|
outp.macSet(peer->macKey());
|
||||||
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
||||||
TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
|
TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
|
||||||
} else {
|
} else {
|
||||||
@ -363,7 +363,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>
|
|||||||
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
|
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
|
||||||
outp.append(payload(),ZT_ADDRESS_LENGTH);
|
outp.append(payload(),ZT_ADDRESS_LENGTH);
|
||||||
outp.encrypt(peer->cryptKey());
|
outp.encrypt(peer->cryptKey());
|
||||||
outp.hmacSet(peer->macKey());
|
outp.macSet(peer->macKey());
|
||||||
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
||||||
TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
|
TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
|
||||||
}
|
}
|
||||||
@ -467,7 +467,7 @@ bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedP
|
|||||||
outp.append(packetId());
|
outp.append(packetId());
|
||||||
outp.append((uint16_t)numAccepted);
|
outp.append((uint16_t)numAccepted);
|
||||||
outp.encrypt(peer->cryptKey());
|
outp.encrypt(peer->cryptKey());
|
||||||
outp.hmacSet(peer->macKey());
|
outp.macSet(peer->macKey());
|
||||||
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
||||||
} catch (std::exception &ex) {
|
} catch (std::exception &ex) {
|
||||||
TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());
|
TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());
|
||||||
@ -654,7 +654,7 @@ bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const
|
|||||||
outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
|
outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
|
||||||
outp.append(nwid);
|
outp.append(nwid);
|
||||||
outp.encrypt(peer->cryptKey());
|
outp.encrypt(peer->cryptKey());
|
||||||
outp.hmacSet(peer->macKey());
|
outp.macSet(peer->macKey());
|
||||||
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
|
||||||
#ifndef __WINDOWS__
|
#ifndef __WINDOWS__
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ bool Switch::sendHELLO(const SharedPtr<Peer> &dest,Demarc::Port localPort,const
|
|||||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||||
outp.append(now);
|
outp.append(now);
|
||||||
_r->identity.serialize(outp,false);
|
_r->identity.serialize(outp,false);
|
||||||
outp.hmacSet(dest->macKey());
|
outp.macSet(dest->macKey());
|
||||||
|
|
||||||
if (_r->demarc->send(localPort,remoteAddr,outp.data(),outp.size(),-1)) {
|
if (_r->demarc->send(localPort,remoteAddr,outp.data(),outp.size(),-1)) {
|
||||||
dest->onSent(_r,false,Packet::VERB_HELLO,now);
|
dest->onSent(_r,false,Packet::VERB_HELLO,now);
|
||||||
@ -277,7 +277,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
|
|||||||
outp.append(cg.first.rawIpData(),4);
|
outp.append(cg.first.rawIpData(),4);
|
||||||
}
|
}
|
||||||
outp.encrypt(p1p->cryptKey());
|
outp.encrypt(p1p->cryptKey());
|
||||||
outp.hmacSet(p1p->macKey());
|
outp.macSet(p1p->macKey());
|
||||||
if (p1p->send(_r,outp.data(),outp.size(),now))
|
if (p1p->send(_r,outp.data(),outp.size(),now))
|
||||||
p1p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
|
p1p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
|
||||||
}
|
}
|
||||||
@ -293,7 +293,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
|
|||||||
outp.append(cg.second.rawIpData(),4);
|
outp.append(cg.second.rawIpData(),4);
|
||||||
}
|
}
|
||||||
outp.encrypt(p2p->cryptKey());
|
outp.encrypt(p2p->cryptKey());
|
||||||
outp.hmacSet(p2p->macKey());
|
outp.macSet(p2p->macKey());
|
||||||
if (p2p->send(_r,outp.data(),outp.size(),now))
|
if (p2p->send(_r,outp.data(),outp.size(),now))
|
||||||
p2p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
|
p2p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
|
||||||
}
|
}
|
||||||
@ -617,7 +617,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
|
|||||||
Packet outp(supernode->address(),_r->identity.address(),Packet::VERB_WHOIS);
|
Packet outp(supernode->address(),_r->identity.address(),Packet::VERB_WHOIS);
|
||||||
addr.appendTo(outp);
|
addr.appendTo(outp);
|
||||||
outp.encrypt(supernode->cryptKey());
|
outp.encrypt(supernode->cryptKey());
|
||||||
outp.hmacSet(supernode->macKey());
|
outp.macSet(supernode->macKey());
|
||||||
|
|
||||||
uint64_t now = Utils::now();
|
uint64_t now = Utils::now();
|
||||||
if (supernode->send(_r,outp.data(),outp.size(),now)) {
|
if (supernode->send(_r,outp.data(),outp.size(),now)) {
|
||||||
@ -654,7 +654,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
|
|||||||
|
|
||||||
if (encrypt)
|
if (encrypt)
|
||||||
tmp.encrypt(peer->cryptKey());
|
tmp.encrypt(peer->cryptKey());
|
||||||
tmp.hmacSet(peer->macKey());
|
tmp.macSet(peer->macKey());
|
||||||
|
|
||||||
if (via->send(_r,tmp.data(),chunkSize,now)) {
|
if (via->send(_r,tmp.data(),chunkSize,now)) {
|
||||||
if (chunkSize < tmp.size()) {
|
if (chunkSize < tmp.size()) {
|
||||||
|
100
node/Utils.cpp
100
node/Utils.cpp
@ -181,36 +181,6 @@ const uint64_t Utils::crc64Table[256] = {
|
|||||||
0x536fa08fdfd90e51ULL,0x29b7d047efec8728ULL
|
0x536fa08fdfd90e51ULL,0x29b7d047efec8728ULL
|
||||||
};
|
};
|
||||||
|
|
||||||
const char Utils::base64EncMap[64] = {
|
|
||||||
0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,
|
|
||||||
0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,
|
|
||||||
0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,
|
|
||||||
0x59,0x5A,0x61,0x62,0x63,0x64,0x65,0x66,
|
|
||||||
0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,
|
|
||||||
0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,
|
|
||||||
0x77,0x78,0x79,0x7A,0x30,0x31,0x32,0x33,
|
|
||||||
0x34,0x35,0x36,0x37,0x38,0x39,0x2B,0x2F
|
|
||||||
};
|
|
||||||
|
|
||||||
const char Utils::base64DecMap[128] = {
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3F,
|
|
||||||
0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,
|
|
||||||
0x3C,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,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,0x00,0x00,0x00,0x00,0x00,
|
|
||||||
0x00,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
|
|
||||||
0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
|
|
||||||
0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,
|
|
||||||
0x31,0x32,0x33,0x00,0x00,0x00,0x00,0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
|
static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
|
||||||
static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
|
static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
|
||||||
|
|
||||||
@ -249,76 +219,6 @@ std::map<std::string,bool> Utils::listDirectory(const char *path)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::base64Encode(const void *data,unsigned int len)
|
|
||||||
{
|
|
||||||
if (!len)
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
std::string out;
|
|
||||||
unsigned int sidx = 0;
|
|
||||||
|
|
||||||
if (len > 1) {
|
|
||||||
while (sidx < (len - 2)) {
|
|
||||||
out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] >> 2) & 077]);
|
|
||||||
out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 1] >> 4) & 017) | ((((const unsigned char *)data)[sidx] << 4) & 077)]);
|
|
||||||
out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 2] >> 6) & 003) | ((((const unsigned char *)data)[sidx + 1] << 2) & 077)]);
|
|
||||||
out.push_back(base64EncMap[((const unsigned char *)data)[sidx + 2] & 077]);
|
|
||||||
sidx += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sidx < len) {
|
|
||||||
out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] >> 2) & 077]);
|
|
||||||
if (sidx < len - 1) {
|
|
||||||
out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 1] >> 4) & 017) | ((((const unsigned char *)data)[sidx] << 4) & 077)]);
|
|
||||||
out.push_back(base64EncMap[(((const unsigned char *)data)[sidx + 1] << 2) & 077]);
|
|
||||||
} else out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] << 4) & 077]);
|
|
||||||
}
|
|
||||||
while (out.length() < (((len + 2) / 3) * 4))
|
|
||||||
out.push_back('=');
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::base64Decode(const char *data,unsigned int len)
|
|
||||||
{
|
|
||||||
if (!len)
|
|
||||||
return std::string();
|
|
||||||
std::string out;
|
|
||||||
|
|
||||||
while ((len)&&(((const unsigned char *)data)[len-1] == '='))
|
|
||||||
--len;
|
|
||||||
|
|
||||||
for (unsigned idx=0;idx<len;idx++) {
|
|
||||||
unsigned char ch = ((const unsigned char *)data)[idx];
|
|
||||||
if ((ch > 47 && ch < 58) || (ch > 64 && ch < 91) || (ch > 96 && ch < 123) || ch == '+' || ch == '/' || ch == '=')
|
|
||||||
out.push_back(base64DecMap[ch]);
|
|
||||||
else return std::string();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned outLen = len - ((len + 3) / 4);
|
|
||||||
if ((!outLen)||((((outLen + 2) / 3) * 4) < len))
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
unsigned sidx = 0;
|
|
||||||
unsigned didx = 0;
|
|
||||||
if (outLen > 1) {
|
|
||||||
while (didx < outLen - 2) {
|
|
||||||
out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
|
|
||||||
out[didx + 1] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
|
|
||||||
out[didx + 2] = (((out[sidx + 2] << 6) & 255) | (out[sidx + 3] & 077));
|
|
||||||
sidx += 4;
|
|
||||||
didx += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (didx < outLen)
|
|
||||||
out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
|
|
||||||
if (++didx < outLen)
|
|
||||||
out[didx] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
|
|
||||||
|
|
||||||
return out.substr(0,outLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::hex(const void *data,unsigned int len)
|
std::string Utils::hex(const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
std::string r;
|
std::string r;
|
||||||
|
@ -87,13 +87,8 @@ public:
|
|||||||
p2 += 8;
|
p2 += 8;
|
||||||
len -= 8;
|
len -= 8;
|
||||||
}
|
}
|
||||||
|
while (len--)
|
||||||
while (len) {
|
diff |= (uint64_t)(*p1++ ^ *p2++);
|
||||||
diff |= (uint64_t)(*p1 ^ *p2);
|
|
||||||
++p1;
|
|
||||||
++p2;
|
|
||||||
--len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (diff == 0ULL);
|
return (diff == 0ULL);
|
||||||
}
|
}
|
||||||
@ -449,22 +444,6 @@ public:
|
|||||||
return writeFile(path,s.data(),(unsigned int)s.length());
|
return writeFile(path,s.data(),(unsigned int)s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param data Binary data to encode
|
|
||||||
* @param len Length of data
|
|
||||||
* @return Base64-encoded string
|
|
||||||
*/
|
|
||||||
static std::string base64Encode(const void *data,unsigned int len);
|
|
||||||
inline static std::string base64Encode(const std::string &data) { return base64Encode(data.data(),(unsigned int)data.length()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param data Base64-encoded string
|
|
||||||
* @param len Length of encoded string
|
|
||||||
* @return Decoded binary date
|
|
||||||
*/
|
|
||||||
static std::string base64Decode(const char *data,unsigned int len);
|
|
||||||
inline static std::string base64Decode(const std::string &data) { return base64Decode(data.data(),(unsigned int)data.length()); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split a string by delimiter, with optional escape and quote characters
|
* Split a string by delimiter, with optional escape and quote characters
|
||||||
*
|
*
|
||||||
@ -477,7 +456,7 @@ public:
|
|||||||
static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
|
static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tokenize a string
|
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
|
||||||
*
|
*
|
||||||
* @param str String to split
|
* @param str String to split
|
||||||
* @param delim Delimiters
|
* @param delim Delimiters
|
||||||
@ -772,8 +751,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint64_t crc64Table[256];
|
static const uint64_t crc64Table[256];
|
||||||
static const char base64EncMap[64];
|
|
||||||
static const char base64DecMap[128];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
Loading…
x
Reference in New Issue
Block a user