Move self-test so it can be called from CLI and always builds, implement Endpoint and Locator deserialization in Go as well as C++.

This commit is contained in:
Adam Ierymenko 2020-01-14 14:37:49 -08:00
parent cba7a5d4d7
commit b2f0b35608
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
20 changed files with 718 additions and 628 deletions

View file

@ -177,7 +177,3 @@ add_custom_command(
DEPENDS zt_osdep zt_core zt_go_native
)
add_custom_target(build_zerotier ALL DEPENDS zerotier)
add_executable(zerotier-selftest selftest.cpp)
target_link_libraries(zerotier-selftest ${libs} zt_core zt_osdep)
target_compile_features(zerotier-selftest PUBLIC cxx_std_11)

View file

@ -30,45 +30,47 @@ func Help() {
Usage: zerotier [-options] <command> [command args]
Global Options:
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <path> Use secret auth token from this file
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <path> Use secret auth token from this file
Commands:
help Show this help
version Print version
service Start in system service mode
status Show ZeroTier service status and config
peers Show VL1 peers
roots Show configured VL1 root servers
addroot <identity> Add VL1 root server
removeroot <identity|address> Remove VL1 root server
identity <command> [args] Identity management commands
new [c25519|p384] Create new identity (including secret)
getpublic <identity> Extract only public part of identity
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
networks List joined VL2 virtual networks
network <network ID> Show verbose network info
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Can IPs in global IP space be managed?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP and NAT-PMP
explicitaddresses <IP/port> [...] Set explicit external IPs to advertise
help Show this help
version Print version
selftest Run internal tests
service [mode] Start as service (default mode: node)
node Start in normal node mode (default)
root [options] Start in root server mode (see docs)
status Show ZeroTier status and config
peers Show VL1 peers
roots Show configured VL1 root servers
addroot <url|identity> [ip/port] [...] Add VL1 root server
removeroot <identity|address> Remove VL1 root server
identity <command> [args] Identity management commands
new [c25519|p384] Create identity (including secret)
getpublic <identity> Extract only public part of identity
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
networks List joined VL2 virtual networks
network <network ID> Show verbose network info
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Allow assignment of global IPs?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP/NAT-PMP
Most commands require a secret token to permit control of a running ZeroTier
service. The CLI will automatically try to read this token from the

View file

@ -0,0 +1,30 @@
/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2023-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
package cli
import (
"fmt"
"os"
"zerotier/pkg/zerotier"
)
func SelfTest() {
fmt.Print("Running ZeroTier core tests...\n\n")
if !zerotier.CSelfTest() {
fmt.Println("FAILED: at least one ZeroTier core test reported failure.")
os.Exit(1)
}
os.Exit(0)
}

View file

@ -110,6 +110,9 @@ func main() {
case "version":
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
os.Exit(0)
case "selftest":
cli.SelfTest()
os.Exit(0)
case "service":
cli.Service(basePath, authToken, cmdArgs)
case "status":

View file

@ -3,6 +3,7 @@ project(zt_go_native)
set(src
GoGlue.cpp
CoreTests.cpp
)
set(headers

View file

@ -11,41 +11,33 @@
*/
/****/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <stdexcept>
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include "node/Constants.hpp"
#include "node/Hashtable.hpp"
#include "node/RuntimeEnvironment.hpp"
#include "node/InetAddress.hpp"
#include "node/Utils.hpp"
#include "node/Identity.hpp"
#include "node/Buffer.hpp"
#include "node/Packet.hpp"
#include "node/Salsa20.hpp"
#include "node/AES.hpp"
#include "node/Locator.hpp"
#include "node/MAC.hpp"
#include "node/NetworkConfig.hpp"
#include "node/Peer.hpp"
#include "node/Dictionary.hpp"
#include "node/SHA512.hpp"
#include "node/C25519.hpp"
#include "node/ECC384.hpp"
#include "node/Poly1305.hpp"
#include "node/CertificateOfMembership.hpp"
#include "node/Node.hpp"
#include "node/IncomingPacket.hpp"
#include "GoGlue.h"
#include "osdep/OSUtils.hpp"
#include "../../node/Constants.hpp"
#include "../../node/InetAddress.hpp"
#include "../../node/Utils.hpp"
#include "../../node/Identity.hpp"
#include "../../node/Buffer.hpp"
#include "../../node/Salsa20.hpp"
#include "../../node/AES.hpp"
#include "../../node/Locator.hpp"
#include "../../node/NetworkConfig.hpp"
#include "../../node/Dictionary.hpp"
#include "../../node/SHA512.hpp"
#include "../../node/C25519.hpp"
#include "../../node/ECC384.hpp"
#include "../../node/Poly1305.hpp"
#include "../../osdep/OSUtils.hpp"
#ifdef __WINDOWS__
#include <tchar.h>
@ -53,10 +45,8 @@
using namespace ZeroTier;
//////////////////////////////////////////////////////////////////////////////
#define KNOWN_GOOD_IDENTITY "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define KNOWN_BAD_IDENTITY "9e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define KNOWN_GOOD_IDENTITY_0 "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define KNOWN_BAD_IDENTITY_0 "9e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
// These were generated with some Go code using the NIST P-384 elliptic curve. There
// are official P-384 test vectors but the format of these is funny and converting is
@ -153,9 +143,7 @@ static const uint8_t AES_GMAC_VECTOR_2_IV[12] = { 0x00,0x01,0x02,0x03,0x04,0x05,
static const uint8_t AES_GMAC_VECTOR_2_IN[541] = { 0xc8,0x36,0x38,0xe8,0x53,0xc8,0x86,0xa3,0xe3,0xad,0x9e,0x2a,0x91,0x47,0xb9,0x51,0xad,0xf7,0x78,0x89,0x9a,0xeb,0x80,0x41,0x67,0xa9,0x16,0xc4,0x93,0xcc,0x77,0x3d,0x8c,0xcf,0x4d,0xb5,0x0b,0xda,0xfd,0xc2,0x8c,0x83,0x5d,0x66,0x43,0x74,0x21,0xbd,0xc4,0xab,0x41,0xd8,0x40,0x53,0x34,0xe8,0x05,0xcb,0x89,0x45,0x09,0xb0,0xa4,0xa6,0x04,0x95,0x19,0x2c,0xab,0x94,0xe1,0x8d,0x7b,0x59,0x8b,0xb9,0x31,0xae,0x3c,0x25,0xd3,0x23,0xab,0x8f,0x95,0xa3,0x8b,0xa5,0xc1,0x66,0x8b,0x57,0xe4,0x88,0x70,0xc9,0xe0,0xa1,0x16,0x39,0xf8,0x12,0xb3,0xe5,0x95,0x38,0x3a,0x01,0x1d,0xcc,0xc0,0xc3,0xa9,0x1c,0x72,0xa7,0x46,0x79,0x51,0x05,0xb2,0x85,0x5a,0x97,0x16,0x97,0xa6,0x85,0xa4,0xf2,0x0b,0x3c,0x90,0x52,0xa3,0xe0,0xbe,0xad,0x06,0x1b,0x8e,0x04,0x22,0xeb,0x3a,0x48,0xb9,0x84,0x24,0x0b,0x24,0x42,0xd9,0xed,0x6b,0x5c,0xc1,0xb6,0x2e,0xa5,0xc0,0x07,0xfe,0x3e,0xbc,0x9a,0x92,0x26,0xb5,0xa6,0x5f,0x09,0x13,0x85,0x5a,0xcf,0x61,0x56,0x65,0x0f,0x4c,0x64,0x79,0xfa,0x0a,0xcf,0xc0,0x95,0x8d,0x4d,0xc6,0xbe,0xee,0xb3,0x67,0xd8,0xa7,0x40,0x90,0x61,0xe3,0xba,0xcb,0x18,0xe0,0x61,0x7b,0x33,0x86,0xf7,0xef,0x64,0xe5,0x36,0xf0,0x9c,0xb6,0x34,0xb1,0xe1,0x2a,0xd8,0xd8,0x5e,0x6b,0x61,0x92,0xa0,0x8e,0x04,0x7b,0xbf,0xa5,0x84,0x39,0x3a,0xe0,0x27,0xc7,0xb0,0x83,0x88,0x4f,0x3e,0x49,0x14,0xaa,0x34,0xde,0xb4,0xbb,0x4c,0xe4,0xbf,0xae,0x9a,0xf9,0x88,0x7a,0x1f,0x18,0xa0,0x8c,0x60,0xc0,0x5c,0x46,0xa1,0xd1,0x36,0x99,0x60,0x9b,0x73,0xa2,0x9a,0x0b,0x8d,0x6e,0x2f,0xe1,0x58,0x7a,0x39,0x71,0xed,0xfc,0x34,0xe4,0x98,0x57,0x7e,0x86,0xf1,0xe5,0x00,0x7d,0x1b,0x6a,0xfa,0xf8,0x6e,0x7b,0x12,0x44,0x04,0x60,0x02,0x81,0x12,0x09,0x00,0xb4,0x35,0x9e,0x03,0x73,0x79,0x9b,0x13,0xc5,0xd7,0x0e,0xce,0x49,0x87,0x48,0x1a,0x67,0x89,0x93,0xef,0xd1,0xdf,0x2d,0x48,0x6d,0x30,0xd5,0xec,0x49,0xfe,0x15,0x1b,0xa6,0x2b,0x6c,0x08,0x8e,0x39,0x73,0x68,0x87,0xa7,0x43,0x28,0x16,0x77,0x86,0xd1,0xcb,0x13,0xe4,0xd3,0xda,0x63,0xcd,0x3a,0x2a,0x35,0xd5,0xfa,0x36,0x67,0xc8,0x4c,0x6b,0xa1,0x8a,0xaf,0x7b,0x4c,0x43,0xb0,0x2f,0x4a,0xcc,0xc0,0x11,0xc6,0x30,0x8e,0xa3,0xd2,0x4a,0x1b,0x2a,0x4f,0xec,0x97,0x83,0xa6,0x4c,0xee,0x51,0xaf,0x06,0x0a,0x1d,0x80,0xd9,0xcf,0xb7,0x69,0x23,0x15,0x3a,0x26,0x04,0x34,0x33,0x76,0x30,0x9f,0xfb,0x56,0xb4,0x26,0xee,0xfa,0x54,0x6c,0x18,0xf9,0xd5,0x32,0x5d,0x03,0xcb,0x2c,0x20,0x30,0x0c,0xa0,0xbb,0xde,0x01,0x77,0x65,0xb0,0x18,0x30,0xd2,0x55,0x9f,0x9b,0xcf,0xb8,0x9b,0xb4,0xbc,0x0b,0x49,0x52,0x53,0x30,0x48,0xa5,0x12,0xe5,0x3b,0x47,0x84,0xff,0xf1,0x53,0x5d,0x5c,0x04,0x70,0x63,0x91,0xc3,0xc0,0xf0,0xea,0xcb,0x44,0x4f,0x8c,0x85,0x42,0x6a,0xc7,0xfa,0xc7,0xb5,0x30,0x03,0x12,0x65,0xca,0xba,0x4f,0x67,0xbb,0xef,0xb6,0xc6,0x3f,0x19,0xe2,0xb5,0x4b,0x8c,0xfc,0x9e,0x18,0xb0,0x33,0x89,0x6e,0xde,0x61,0x0a,0xe3,0x5e,0xa3,0x5d,0x2e,0x80,0x3e,0x53,0x67,0xfb,0x7b,0x7a,0xbf,0xd5,0xf4,0x47 };
static const uint8_t AES_GMAC_VECTOR_2_OUT[16] = { 0x67,0x39,0x4f,0x00,0x04,0x28,0xaf,0xe9,0xb4,0x2e,0xb5,0x3c,0x42,0x24,0x86,0xa3 };
//////////////////////////////////////////////////////////////////////////////
static int testCrypto()
extern "C" int ZT_TestCrypto()
{
static uint8_t buf1[16384],buf2[16384],buf3[16384];
static char hexbuf[1024];
@ -508,14 +496,14 @@ static int testCrypto()
return 0;
}
static int testIdentity()
extern "C" int ZT_TestIdentity()
{
Identity id;
Buffer<512> buf;
char buf2[1024];
std::cout << "[identity] Validate known-good identity... "; std::cout.flush();
if (!id.fromString(KNOWN_GOOD_IDENTITY)) {
if (!id.fromString(KNOWN_GOOD_IDENTITY_0)) {
std::cout << "FAIL (1)" ZT_EOL_S;
return -1;
}
@ -530,7 +518,7 @@ static int testIdentity()
std::cout << "PASS (" << ((double)(vet - vst) / 10.0) << "ms per validation)" ZT_EOL_S;
std::cout << "[identity] Validate known-bad identity... "; std::cout.flush();
if (!id.fromString(KNOWN_BAD_IDENTITY)) {
if (!id.fromString(KNOWN_BAD_IDENTITY_0)) {
std::cout << "FAIL (1)" ZT_EOL_S;
return -1;
}
@ -613,114 +601,7 @@ static int testIdentity()
return 0;
}
static int testCertificate()
{
char buf[4096];
Identity authority;
std::cout << "[certificate] Generating identity to act as authority... "; std::cout.flush();
authority.generate(Identity::C25519);
std::cout << authority.address().toString(buf) << ZT_EOL_S;
Identity idA,idB;
std::cout << "[certificate] Generating identities A and B... "; std::cout.flush();
idA.generate(Identity::C25519);
idB.generate(Identity::C25519);
std::cout << idA.address().toString(buf) << ", " << idB.address().toString(buf) << ZT_EOL_S;
std::cout << "[certificate] Generating certificates A and B...";
CertificateOfMembership cA(10000,100,1,idA.address());
CertificateOfMembership cB(10099,100,1,idB.address());
std::cout << ZT_EOL_S;
std::cout << "[certificate] Signing certificates A and B with authority...";
cA.sign(authority);
cB.sign(authority);
std::cout << ZT_EOL_S;
//std::cout << "[certificate] A: " << cA.toString() << ZT_EOL_S;
//std::cout << "[certificate] B: " << cB.toString() << ZT_EOL_S;
std::cout << "[certificate] A agrees with B and B with A... ";
if (cA.agreesWith(cB))
std::cout << "yes, ";
else {
std::cout << "FAIL" ZT_EOL_S;
return -1;
}
if (cB.agreesWith(cA))
std::cout << "yes." ZT_EOL_S;
else {
std::cout << "FAIL" ZT_EOL_S;
return -1;
}
std::cout << "[certificate] Generating two certificates that should not agree...";
cA = CertificateOfMembership(10000,100,1,idA.address());
cB = CertificateOfMembership(10101,100,1,idB.address());
std::cout << ZT_EOL_S;
std::cout << "[certificate] A agrees with B and B with A... ";
if (!cA.agreesWith(cB))
std::cout << "no, ";
else {
std::cout << "FAIL" ZT_EOL_S;
return -1;
}
if (!cB.agreesWith(cA))
std::cout << "no." ZT_EOL_S;
else {
std::cout << "FAIL" ZT_EOL_S;
return -1;
}
return 0;
}
static int testPacket()
{
unsigned char salsaKey[32];
Packet a,b;
a.burn();
b.burn();
for(unsigned int i=0;i<32;++i)
salsaKey[i] = (unsigned char)rand();
std::cout << "[packet] Testing Packet encoder/decoder... ";
a.reset(Address(),Address(),Packet::VERB_HELLO);
for(int i=0;i<32;++i)
a.append("supercalifragilisticexpealidocious",(unsigned int)strlen("supercalifragilisticexpealidocious"));
b = a;
if (a != b) {
std::cout << "FAIL (assign)" ZT_EOL_S;
return -1;
}
a.compress();
unsigned int complen = a.size();
a.uncompress();
std::cout << "(compressed: " << complen << ", decompressed: " << a.size() << ") ";
if (a != b) {
std::cout << "FAIL (compresssion)" ZT_EOL_S;
return -1;
}
a.armor(salsaKey,true);
if (!a.dearmor(salsaKey)) {
std::cout << "FAIL (encrypt-decrypt/verify)" ZT_EOL_S;
return -1;
}
std::cout << "PASS" ZT_EOL_S;
return 0;
}
static int testOther()
extern "C" int ZT_TestOther()
{
char buf[1024];
char buf2[4096];
@ -864,35 +745,3 @@ static int testOther()
return 0;
}
#ifdef __WINDOWS__
int __cdecl _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc,char **argv)
#endif
{
int r = 0;
#ifdef __WINDOWS__
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
#endif
std::cout << "[info] sizeof(void *) == " << sizeof(void *) << ZT_EOL_S;
std::cout << "[info] OSUtils::now() == " << OSUtils::now() << ZT_EOL_S;
std::cout << "[info] hardware concurrency == " << std::thread::hardware_concurrency() << ZT_EOL_S;
std::cout << "[info] sizeof(NetworkConfig) == " << sizeof(ZeroTier::NetworkConfig) << ZT_EOL_S;
srand((unsigned int)time(0));
r |= testOther();
r |= testCrypto();
r |= testPacket();
r |= testIdentity();
r |= testCertificate();
if (r)
std::cout << ZT_EOL_S << "SOMETHING FAILED!" ZT_EOL_S;
return r;
}

View file

@ -49,10 +49,10 @@ void ZT_GoNode_delete(ZT_GoNode *gn);
ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn);
/* This can be called more than once to start multiple listener threads */
int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port);
int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,int port);
/* Close all listener threads for a given local IP and port */
int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port);
int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,int port);
ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid);
@ -84,6 +84,11 @@ int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int target
int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
/* Core self-tests that output results to stdout and return non-zero on failure. */
int ZT_TestCrypto();
int ZT_TestIdentity();
int ZT_TestOther();
#ifdef __cplusplus
}
#endif

115
go/pkg/zerotier/endpoint.go Normal file
View file

@ -0,0 +1,115 @@
package zerotier
import (
"encoding/binary"
"errors"
)
// Endpoint types are the same as the enum values in Endpoint.hpp in the core.
const (
EndpointTypeNil = 0
EndpointTypeInetAddr = 1
EndpointTypeDnsName = 2
EndpointTypeZeroTier = 3
EndpointTypeUrl = 4
EndpointTypeEthernet = 5
EndpointTypeUnrecognized = 255
)
type Endpoint struct {
Type int
value, value2 interface{}
}
var (
ErrInvalidEndpoint = errors.New("invalid marshaled endpoint object")
)
func (ep *Endpoint) unmarshalZT(b []byte) (int, error) {
if len(b) == 0 {
return 0, ErrInvalidEndpoint
}
switch b[0] {
case EndpointTypeNil:
*ep = Endpoint{Type: EndpointTypeNil}
return 1, nil
case EndpointTypeInetAddr:
ina := new(InetAddress)
inlen, err := ina.unmarshalZT(b[1:])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeInetAddr,
value: ina,
}
return 1 + inlen, nil
case EndpointTypeDnsName:
zeroAt := 1
for i := 1; i < len(b); i++ {
if b[i] == 0 {
zeroAt = i
break
}
}
if zeroAt == 1 || (1 + zeroAt + 3) > len(b) {
return 0, ErrInvalidEndpoint
}
port := binary.BigEndian.Uint16(b[zeroAt+1:zeroAt+3])
*ep = Endpoint{
Type: EndpointTypeDnsName,
value: string(b[1:zeroAt]),
value2: &port,
}
return zeroAt + 3, nil
case EndpointTypeZeroTier:
if len(b) != 54 {
return 0, ErrInvalidEndpoint
}
a, err := NewAddressFromBytes(b[1:6])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeZeroTier,
value: a,
value2: append(make([]byte, 0, 48), b[6:54]...),
}
return 54, nil
case EndpointTypeUrl:
zeroAt := 1
for i := 1; i < len(b); i++ {
if b[i] == 0 {
zeroAt = i
break
}
}
if zeroAt == 1 {
return 0, ErrInvalidEndpoint
}
*ep = Endpoint{
Type: EndpointTypeUrl,
value: string(b[1:zeroAt]),
}
return zeroAt + 2, nil
case EndpointTypeEthernet:
if len(b) != 7 {
return 0, ErrInvalidEndpoint
}
m, err := NewMACFromBytes(b[1:7])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeEthernet,
value: m,
}
return 7, nil
default:
if len(b) < 2 {
return 0, ErrInvalidEndpoint
}
*ep = Endpoint{Type: EndpointTypeUnrecognized}
return 1 + int(b[1]), nil
}
}

View file

@ -13,15 +13,87 @@
package zerotier
//#include "../../native/GoGlue.h"
import "C"
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"net"
"strconv"
"strings"
"unsafe"
)
func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
var a net.IPNet
switch ss.ss_family {
case AFInet:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32)
return &a
case AFInet6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
a.IP = ip6[:]
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])), 128)
return &a
}
return nil
}
func sockaddrStorageToUDPAddr(ss *C.struct_sockaddr_storage) *net.UDPAddr {
var a net.UDPAddr
switch ss.ss_family {
case AFInet:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:]))
return &a
case AFInet6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
a.IP = ip6[:]
a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:]))
return &a
}
return nil
}
func sockaddrStorageToUDPAddr2(ss unsafe.Pointer) *net.UDPAddr {
return sockaddrStorageToUDPAddr((*C.struct_sockaddr_storage)(ss))
}
func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) bool {
C.memset(unsafe.Pointer(ss), 0, C.sizeof_struct_sockaddr_storage)
if len(ip) == 4 {
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
sa4.sin_family = AFInet
copy(((*[4]byte)(unsafe.Pointer(&sa4.sin_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:], uint16(port))
return true
}
if len(ip) == 16 {
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
sa6.sin6_family = AFInet6
copy(((*[16]byte)(unsafe.Pointer(&sa6.sin6_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:], uint16(port))
return true
}
return false
}
var ErrInvalidInetAddress = errors.New("invalid marshaled InetAddress object")
// InetAddress implements net.Addr but has a ZeroTier-like string representation
type InetAddress struct {
IP net.IP
@ -29,13 +101,13 @@ type InetAddress struct {
}
// Less returns true if this IP/port is lexicographically less than another
func (i *InetAddress) Less(i2 *InetAddress) bool {
c := bytes.Compare(i.IP, i2.IP)
func (ina *InetAddress) Less(i2 *InetAddress) bool {
c := bytes.Compare(ina.IP, i2.IP)
if c < 0 {
return true
}
if c == 0 {
return i.Port < i2.Port
return ina.Port < i2.Port
}
return false
}
@ -73,18 +145,18 @@ func NewInetAddressFromSockaddr(sa unsafe.Pointer) *InetAddress {
}
// Network returns "udp" to implement net.Addr
func (i *InetAddress) Network() string {
func (ina *InetAddress) Network() string {
return "udp"
}
// String returns this address in ZeroTier-canonical IP/port format
func (i *InetAddress) String() string {
return i.IP.String() + "/" + strconv.FormatInt(int64(i.Port), 10)
func (ina *InetAddress) String() string {
return ina.IP.String() + "/" + strconv.FormatInt(int64(ina.Port), 10)
}
// Family returns the address family (AFInet etc.) or 0 if none
func (i *InetAddress) Family() int {
switch len(i.IP) {
func (ina *InetAddress) Family() int {
switch len(ina.IP) {
case 4:
return AFInet
case 16:
@ -94,30 +166,58 @@ func (i *InetAddress) Family() int {
}
// Valid returns true if both the IP and port have valid values
func (i *InetAddress) Valid() bool {
return (len(i.IP) == 4 || len(i.IP) == 16) && (i.Port > 0 && i.Port < 65536)
func (ina *InetAddress) Valid() bool {
return (len(ina.IP) == 4 || len(ina.IP) == 16) && (ina.Port > 0 && ina.Port < 65536)
}
// MarshalJSON marshals this MAC as a string
func (i *InetAddress) MarshalJSON() ([]byte, error) {
s := i.String()
func (ina *InetAddress) MarshalJSON() ([]byte, error) {
s := ina.String()
return json.Marshal(&s)
}
// UnmarshalJSON unmarshals this MAC from a string
func (i *InetAddress) UnmarshalJSON(j []byte) error {
func (ina *InetAddress) UnmarshalJSON(j []byte) error {
var s string
err := json.Unmarshal(j, &s)
if err != nil {
return err
}
*i = *NewInetAddressFromString(s)
*ina = *NewInetAddressFromString(s)
return nil
}
func (ina *InetAddress) unmarshalZT(b []byte) (int, error) {
if len(b) <= 0 {
return 0, ErrInvalidInetAddress
}
switch b[0] {
case 0:
ina.IP = nil
ina.Port = 0
return 1, nil
case 4:
if len(b) != 7 {
return 0, ErrInvalidInetAddress
}
ina.IP = []byte{b[1], b[2], b[3], b[4]}
ina.Port = int(binary.BigEndian.Uint16(b[5:7]))
return 7, nil
case 6:
if len(b) != 19 {
return 0, ErrInvalidInetAddress
}
ina.IP = append(make([]byte, 0, 16), b[1:17]...)
ina.Port = int(binary.BigEndian.Uint16(b[17:19]))
return 19, nil
default:
return 0, ErrInvalidInetAddress
}
}
// key returns a short array suitable for use as a map[] key for this IP
func (i *InetAddress) key() (k [3]uint64) {
copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], i.IP)
k[2] = uint64(i.Port)
func (ina *InetAddress) key() (k [3]uint64) {
copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ina.IP)
k[2] = uint64(ina.Port)
return
}

View file

@ -1,7 +1,46 @@
package zerotier
type Locator struct {
Timestamp int64
Endpoints []InetAddress
Bytes []byte
import (
"encoding/binary"
"errors"
)
// Locator objects are signed collections of physical or virtual endpoints for a node.
type Locator []byte
var (
ErrInvalidLocator = errors.New("invalid marshaled locator object")
)
func (l Locator) Timestamp() int64 {
if len(l) >= 8 {
return int64(binary.BigEndian.Uint64(l))
}
return 0
}
// Endpoints obtains the endpoints described by this locator.
func (l Locator) Endpoints() (eps []Endpoint,err error) {
if len(l) <= (8 + 2) {
err = ErrInvalidLocator
return
}
endpointCount := int(binary.BigEndian.Uint16(l[8:10]))
eps = make([]Endpoint,endpointCount)
p := 10
for e:=0;e<endpointCount;e++ {
if p >= len(l) {
err = ErrInvalidLocator
return
}
var elen int
elen, err = eps[e].unmarshalZT(l[p:])
if err != nil {
return
}
p += elen
}
return
}

View file

@ -21,7 +21,6 @@ import "C"
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
@ -87,73 +86,22 @@ var (
nodesByUserPtrLock sync.RWMutex
)
func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
var a net.IPNet
switch ss.ss_family {
case AFInet:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32)
return &a
case AFInet6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
a.IP = ip6[:]
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])), 128)
return &a
// CSelfTest runs self-test functions from the core, sending results to stdout and returning true on success.
func CSelfTest() bool {
if C.ZT_TestOther() != 0 {
return false
}
return nil
}
func sockaddrStorageToUDPAddr(ss *C.struct_sockaddr_storage) *net.UDPAddr {
var a net.UDPAddr
switch ss.ss_family {
case AFInet:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:]))
return &a
case AFInet6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
a.IP = ip6[:]
a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:]))
return &a
fmt.Println()
if C.ZT_TestCrypto() != 0 {
return false
}
return nil
}
func sockaddrStorageToUDPAddr2(ss unsafe.Pointer) *net.UDPAddr {
return sockaddrStorageToUDPAddr((*C.struct_sockaddr_storage)(ss))
}
func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) bool {
C.memset(unsafe.Pointer(ss), 0, C.sizeof_struct_sockaddr_storage)
if len(ip) == 4 {
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
sa4.sin_family = AFInet
copy(((*[4]byte)(unsafe.Pointer(&sa4.sin_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:], uint16(port))
return true
fmt.Println()
if C.ZT_TestIdentity() != 0 {
return false
}
if len(ip) == 16 {
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
sa6.sin6_family = AFInet6
copy(((*[16]byte)(unsafe.Pointer(&sa6.sin6_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:], uint16(port))
return true
}
return false
return true
}
//////////////////////////////////////////////////////////////////////////////
// Node is an instance of the ZeroTier core node and related C++ I/O code
type Node struct {
networks map[NetworkID]*Network

View file

@ -3,12 +3,12 @@ package zerotier
// Root nodes are long-lived nodes at stable physical addresses that can help locate other nodes.
type Root struct {
// Identity is this root's address and public key(s).
Identity Identity
Identity Identity `json:"identity"`
// Locator describes the endpoints where this root may be found.
Locator Locator
Locator Locator `json:"locator"`
// URL is an optional URL where the latest Locator may be fetched.
// URL is an optional URL where the latest Root may be fetched.
// This is one method of locator update, while in-band mechanisms are the other.
URL string
URL string `json:"url"`
}

View file

@ -1,10 +1,6 @@
cmake_minimum_required (VERSION 2.8)
project(zt_core)
if(WIN32)
add_definitions(-DNOMINMAX)
endif(WIN32)
set(core_headers
Address.hpp
AtomicCounter.hpp
@ -55,6 +51,7 @@ set(core_src
C25519.cpp
Credential.cpp
ECC384.cpp
Endpoint.cpp
Identity.cpp
IncomingPacket.cpp
InetAddress.cpp

185
node/Endpoint.cpp Normal file
View file

@ -0,0 +1,185 @@
/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2023-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include "Endpoint.hpp"
namespace ZeroTier {
bool Endpoint::operator==(const Endpoint &ep) const
{
if (_t == ep._t) {
switch(_t) {
case INETADDR: return (*sockaddr() == *ep.sockaddr());
case DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
case URL: return (strcmp(_v.url,ep._v.url) == 0);
case ETHERNET: return (_v.eth == ep._v.eth);
default: return true;
}
}
return false;
}
bool Endpoint::operator<(const Endpoint &ep) const
{
if ((int)_t < (int)ep._t) {
return true;
} else if (_t == ep._t) {
int ncmp;
switch(_t) {
case INETADDR: return (*sockaddr() < *ep.sockaddr());
case DNSNAME:
ncmp = strcmp(_v.dns.name,ep._v.dns.name);
return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
case ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0));
case URL: return (strcmp(_v.url,ep._v.url) < 0);
case ETHERNET: return (_v.eth < ep._v.eth);
default: return false;
}
}
return false;
}
int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
{
int p;
switch(_t) {
case INETADDR:
data[0] = (uint8_t)INETADDR;
return 1 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
case DNSNAME:
data[0] = (uint8_t)DNSNAME;
p = 1;
for (;;) {
if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0)
break;
++p;
if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
return -1;
}
data[p++] = (uint8_t)(_v.dns.port >> 8U);
data[p++] = (uint8_t)_v.dns.port;
return p;
case ZEROTIER:
data[0] = (uint8_t)ZEROTIER;
data[1] = (uint8_t)(_v.zt.a >> 32U);
data[2] = (uint8_t)(_v.zt.a >> 24U);
data[3] = (uint8_t)(_v.zt.a >> 16U);
data[4] = (uint8_t)(_v.zt.a >> 8U);
data[5] = (uint8_t)_v.zt.a;
memcpy(data + 6,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
case URL:
data[0] = (uint8_t)URL;
p = 1;
for (;;) {
if ((data[p] = (uint8_t)_v.url[p-1]) == 0)
break;
++p;
if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
return -1;
}
return p;
case ETHERNET:
data[0] = (uint8_t)ETHERNET;
data[1] = (uint8_t)(_v.eth >> 40U);
data[2] = (uint8_t)(_v.eth >> 32U);
data[3] = (uint8_t)(_v.eth >> 24U);
data[4] = (uint8_t)(_v.eth >> 16U);
data[5] = (uint8_t)(_v.eth >> 8U);
data[6] = (uint8_t)_v.eth;
return 7;
default:
data[0] = (uint8_t)NIL;
return 1;
}
}
int Endpoint::unmarshal(const uint8_t *restrict data,const int len)
{
if (len <= 0)
return -1;
int p;
switch((Type)data[0]) {
case NIL:
_t = NIL;
return 1;
case INETADDR:
_t = INETADDR;
return reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+1,len-1);
case DNSNAME:
if (len < 4)
return -1;
_t = DNSNAME;
p = 1;
for (;;) {
if ((_v.dns.name[p-1] = (char)data[p]) == 0) {
++p;
break;
}
++p;
if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2)))
return -1;
}
_v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U);
_v.dns.port |= (uint16_t)data[p++];
return p;
case ZEROTIER:
if (len < (ZT_IDENTITY_HASH_SIZE + 6))
return -1;
_t = ZEROTIER;
_v.zt.a = ((uint64_t)data[1]) << 32U;
_v.zt.a |= ((uint64_t)data[2]) << 24U;
_v.zt.a |= ((uint64_t)data[3]) << 16U;
_v.zt.a |= ((uint64_t)data[4]) << 8U;
_v.zt.a |= (uint64_t)data[5];
memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
case URL:
if (len < 2)
return -1;
_t = URL;
p = 1;
for (;;) {
if ((_v.url[p-1] = (char)data[p]) == 0) {
++p;
break;
}
++p;
if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len))
return -1;
}
return p;
case ETHERNET:
if (len < 7)
return -1;
_t = ZEROTIER;
_v.eth = ((uint64_t)data[1]) << 40U;
_v.eth |= ((uint64_t)data[2]) << 32U;
_v.eth |= ((uint64_t)data[3]) << 24U;
_v.eth |= ((uint64_t)data[4]) << 16U;
_v.eth |= ((uint64_t)data[5]) << 8U;
_v.eth |= (uint64_t)data[6];
return 7;
default:
// Unrecognized endpoint types not yet specified must start with a byte
// length size so that older versions of ZeroTier can skip them.
if (len < 2)
return -1;
_t = UNRECOGNIZED;
return 1 + (int)data[1];
}
return false;
}
} // namespace ZeroTier

View file

@ -36,12 +36,13 @@ class Endpoint
public:
enum Type
{
NIL = 0, // NIL value
INETADDR = 1, // InetAddress (v4 or v6)
DNSNAME = 2, // DNS name and port that resolves to InetAddress
ZEROTIER = 3, // ZeroTier Address (for relaying and meshy behavior)
URL = 4, // URL for http/https/ws/etc. (not implemented yet)
ETHERNET = 5 // 48-bit LAN-local Ethernet address
NIL = 0, // NIL value
INETADDR = 1, // InetAddress (v4 or v6)
DNSNAME = 2, // DNS name and port that resolves to InetAddress
ZEROTIER = 3, // ZeroTier Address (for relaying and meshy behavior)
URL = 4, // URL for http/https/ws/etc. (not implemented yet)
ETHERNET = 5, // 48-bit LAN-local Ethernet address
UNRECOGNIZED = 255 // Unrecognized endpoint type encountered in stream
};
ZT_ALWAYS_INLINE Endpoint() { memset(reinterpret_cast<void *>(this),0,sizeof(Endpoint)); }
@ -61,170 +62,16 @@ public:
ZT_ALWAYS_INLINE Type type() const { return _t; }
ZT_ALWAYS_INLINE bool operator==(const Endpoint &ep) const
{
if (_t == ep._t) {
switch(_t) {
case INETADDR: return (*sockaddr() == *ep.sockaddr());
case DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
case URL: return (strcmp(_v.url,ep._v.url) == 0);
case ETHERNET: return (_v.eth == ep._v.eth);
default: return true;
}
}
return false;
}
bool operator==(const Endpoint &ep) const;
ZT_ALWAYS_INLINE bool operator!=(const Endpoint &ep) const { return (!(*this == ep)); }
ZT_ALWAYS_INLINE bool operator<(const Endpoint &ep) const
{
if ((int)_t < (int)ep._t) {
return true;
} else if (_t == ep._t) {
int ncmp;
switch(_t) {
case INETADDR: return (*sockaddr() < *ep.sockaddr());
case DNSNAME:
ncmp = strcmp(_v.dns.name,ep._v.dns.name);
return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
case ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0));
case URL: return (strcmp(_v.url,ep._v.url) < 0);
case ETHERNET: return (_v.eth < ep._v.eth);
default: return false;
}
}
return false;
}
bool operator<(const Endpoint &ep) const;
ZT_ALWAYS_INLINE bool operator>(const Endpoint &ep) const { return (ep < *this); }
ZT_ALWAYS_INLINE bool operator<=(const Endpoint &ep) const { return !(ep < *this); }
ZT_ALWAYS_INLINE bool operator>=(const Endpoint &ep) const { return !(*this < ep); }
// Marshal interface ///////////////////////////////////////////////////////
static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; }
inline int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
{
int p;
switch(_t) {
case INETADDR:
data[0] = (uint8_t)INETADDR;
return 1 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
case DNSNAME:
data[0] = (uint8_t)DNSNAME;
p = 1;
for (;;) {
if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0)
break;
++p;
if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
return -1;
}
data[p++] = (uint8_t)(_v.dns.port >> 8U);
data[p++] = (uint8_t)_v.dns.port;
return p;
case ZEROTIER:
data[0] = (uint8_t)ZEROTIER;
data[1] = (uint8_t)(_v.zt.a >> 32U);
data[2] = (uint8_t)(_v.zt.a >> 24U);
data[3] = (uint8_t)(_v.zt.a >> 16U);
data[4] = (uint8_t)(_v.zt.a >> 8U);
data[5] = (uint8_t)_v.zt.a;
memcpy(data + 6,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
case URL:
data[0] = (uint8_t)URL;
p = 1;
for (;;) {
if ((data[p] = (uint8_t)_v.url[p-1]) == 0)
break;
++p;
if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
return -1;
}
return p;
case ETHERNET:
data[0] = (uint8_t)ETHERNET;
data[1] = (uint8_t)(_v.eth >> 40U);
data[2] = (uint8_t)(_v.eth >> 32U);
data[3] = (uint8_t)(_v.eth >> 24U);
data[4] = (uint8_t)(_v.eth >> 16U);
data[5] = (uint8_t)(_v.eth >> 8U);
data[6] = (uint8_t)_v.eth;
return 7;
default:
data[0] = (uint8_t)NIL;
return 1;
}
}
inline int unmarshal(const uint8_t *restrict data,const int len)
{
if (len <= 0)
return -1;
int p;
switch((Type)data[0]) {
case NIL:
_t = NIL;
return 1;
case INETADDR:
_t = INETADDR;
return reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+1,len-1);
case DNSNAME:
if (len < 4)
return -1;
_t = DNSNAME;
p = 1;
for (;;) {
if ((_v.dns.name[p-1] = (char)data[p]) == 0) {
++p;
break;
}
++p;
if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2)))
return -1;
}
_v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U);
_v.dns.port |= (uint16_t)data[p++];
return p;
case ZEROTIER:
if (len < (ZT_IDENTITY_HASH_SIZE + 6))
return -1;
_t = ZEROTIER;
_v.zt.a = ((uint64_t)data[1]) << 32U;
_v.zt.a |= ((uint64_t)data[2]) << 24U;
_v.zt.a |= ((uint64_t)data[3]) << 16U;
_v.zt.a |= ((uint64_t)data[4]) << 8U;
_v.zt.a |= (uint64_t)data[5];
memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
case URL:
if (len < 2)
return -1;
_t = URL;
p = 1;
for (;;) {
if ((_v.url[p-1] = (char)data[p]) == 0) {
++p;
break;
}
++p;
if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len))
return -1;
}
return p;
case ETHERNET:
if (len < 7)
return -1;
_t = ZEROTIER;
_v.eth = ((uint64_t)data[1]) << 40U;
_v.eth |= ((uint64_t)data[2]) << 32U;
_v.eth |= ((uint64_t)data[3]) << 24U;
_v.eth |= ((uint64_t)data[4]) << 16U;
_v.eth |= ((uint64_t)data[5]) << 8U;
_v.eth |= (uint64_t)data[6];
return 7;
}
return false;
}
////////////////////////////////////////////////////////////////////////////
int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const;
int unmarshal(const uint8_t *restrict data,const int len);
private:
Type _t;

View file

@ -313,6 +313,68 @@ bool InetAddress::isNetwork() const
return false;
}
int InetAddress::marshal(uint8_t data[19]) const
{
unsigned int port;
switch(ss_family) {
case AF_INET:
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in *>(this)->sin_port);
data[0] = 4;
data[1] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[0];
data[2] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[1];
data[3] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[2];
data[4] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[3];
data[5] = (uint8_t)(port >> 8U);
data[6] = (uint8_t)port;
return 7;
case AF_INET6:
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port);
data[0] = 6;
for(int i=0;i<16;++i)
data[i+1] = reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr[i];
data[17] = (uint8_t)(port >> 8U);
data[18] = (uint8_t)port;
return 19;
default:
data[0] = 0;
return 1;
}
}
int InetAddress::unmarshal(const uint8_t *restrict data,const int len)
{
if (len <= 0)
return -1;
switch(data[0]) {
case 0:
return 1;
case 4:
if (len < 7)
return -1;
memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_port))[sizeof(reinterpret_cast<sockaddr_in *>(this)->sin_port)-2] = data[5];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_port))[sizeof(reinterpret_cast<sockaddr_in *>(this)->sin_port)-1] = data[6];
return 7;
case 6:
if (len < 19)
return -1;
memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
for(int i=0;i<16;i++)
(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port))[sizeof(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port)-2] = data[17];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port))[sizeof(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port)-1] = data[18];
return 19;
default:
return -1;
}
}
bool InetAddress::operator==(const InetAddress &a) const
{
if (ss_family == a.ss_family) {

View file

@ -463,80 +463,9 @@ public:
*/
explicit ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); }
// Marshal interface ///////////////////////////////////////////////////////
static ZT_ALWAYS_INLINE int marshalSizeMax() { return 19; }
inline int marshal(uint8_t data[19]) const
{
unsigned int port;
switch(ss_family) {
case AF_INET:
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in *>(this)->sin_port);
data[0] = 4;
data[1] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[0];
data[2] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[1];
data[3] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[2];
data[4] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[3];
data[5] = (uint8_t)(port >> 8U);
data[6] = (uint8_t)port;
return 7;
case AF_INET6:
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port);
data[0] = 6;
for(int i=0;i<16;++i)
data[i+1] = reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr[i];
data[17] = (uint8_t)(port >> 8U);
data[18] = (uint8_t)port;
return 19;
default:
data[0] = 0;
return 1;
}
}
inline int unmarshal(const uint8_t *restrict data,const int len)
{
#ifdef ZT_NO_TYPE_PUNNING
uint16_t tmp;
#endif
if (len <= 0)
return -1;
switch(data[0]) {
case 0:
return 1;
case 4:
if (len < 7)
return -1;
memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
#ifdef ZT_NO_TYPE_PUNNING
memcpy(&tmp,data + 5,2);
reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
#else
reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 5));
#endif
return 7;
case 6:
if (len < 19)
return -1;
memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
for(int i=0;i<16;i++)
(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
#ifdef ZT_NO_TYPE_PUNNING
memcpy(&tmp,data + 17,2);
reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
#else
reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 17));
#endif
return 19;
default:
return -1;
}
}
////////////////////////////////////////////////////////////////////////////
int marshal(uint8_t data[19]) const;
int unmarshal(const uint8_t *restrict data,const int len);
template<unsigned int C>
inline void serialize(Buffer<C> &b) const

View file

@ -66,14 +66,12 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
int Locator::unmarshal(const uint8_t *restrict data,const int len)
{
if (len <= (8 + 48))
if (len <= (8 + 2 + 48))
return -1;
_ts = (int64_t)Utils::readUInt64(data);
int p = 8;
if ((p + 2) > len)
return -1;
unsigned int ec = (int)data[p++];
ec <<= 8U;
ec |= data[p++];

View file

@ -27,13 +27,33 @@
// Also makes sure __BYTE_ORDER is defined reasonably.
//
// Hack: make sure __GCC__ is defined on old GCC compilers
#ifndef __GCC__
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
#define __GCC__
#endif
#endif
#if defined(_WIN32) || defined(_WIN64)
#ifndef __WINDOWS__
#define __WINDOWS__
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#undef __UNIX_LIKE__
#undef __BSD__
#if !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
#pragma warning(disable : 4290)
#pragma warning(disable : 4996)
#pragma warning(disable : 4101)
#else
#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
#endif
#include <WinSock2.h>
#include <Windows.h>
#endif
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
#ifndef __LINUX__
#define __LINUX__
@ -69,30 +89,13 @@
#define __BIG_ENDIAN _BIG_ENDIAN
#endif
#endif
#if defined(_WIN32) || defined(_WIN64)
#ifndef __WINDOWS__
#define __WINDOWS__
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#pragma warning(disable : 4290)
#pragma warning(disable : 4996)
#pragma warning(disable : 4101)
#undef __UNIX_LIKE__
#undef __BSD__
#include <WinSock2.h>
#include <Windows.h>
#endif
#ifdef __NetBSD__
#ifndef RTF_MULTICAST
#define RTF_MULTICAST 0x20000000
#endif
#endif
// Define ZT_NO_TYPE_PUNNING to disable reckless casts on anything other than x86 and x86_64.
// Avoid unaligned type casts on all but x86/x64 architecture.
#if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
#ifndef ZT_NO_TYPE_PUNNING
#define ZT_NO_TYPE_PUNNING
@ -112,16 +115,6 @@
#include <endian.h>
#endif
#ifdef __WINDOWS__
#define ZT_PATH_SEPARATOR '\\'
#define ZT_PATH_SEPARATOR_S "\\"
#define ZT_EOL_S "\r\n"
#else
#define ZT_PATH_SEPARATOR '/'
#define ZT_PATH_SEPARATOR_S "/"
#define ZT_EOL_S "\n"
#endif
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
#define ZT_ALWAYS_INLINE __attribute__((always_inline))
#ifndef restrict
@ -146,12 +139,6 @@
#endif
#endif
#if defined(__WINDOWS__) && !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
#else
#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
#endif
#if __cplusplus > 199711L
#ifndef __CPP11__
#define __CPP11__
@ -169,6 +156,16 @@
#define ZT_INVALID_SOCKET -1
#endif
#ifdef __WINDOWS__
#define ZT_PATH_SEPARATOR '\\'
#define ZT_PATH_SEPARATOR_S "\\"
#define ZT_EOL_S "\r\n"
#else
#define ZT_PATH_SEPARATOR '/'
#define ZT_PATH_SEPARATOR_S "/"
#define ZT_EOL_S "\n"
#endif
#ifndef ZT_ALWAYS_INLINE
#define ZT_ALWAYS_INLINE inline
#endif

View file

@ -234,6 +234,36 @@
#define ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS (ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI + 4)
#define ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS (ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS + 1)
/**
* Signed locator for this node
*/
#define ZT_PROTO_NODE_META_LOCATOR "L"
/**
* Dictionary mapping identity hash to timestamp to request newer locators for other nodes if known
*/
#define ZT_PROTO_NODE_META_REFRESH_LOCATORS_IF_NEWER "lt"
/**
* Dictionary mapping identity hash to locator to supply newer revisions of requested locators
*/
#define ZT_PROTO_NODE_META_REFRESH_LOCATORS "lr"
/**
* Ephemeral C25519 public key
*/
#define ZT_PROTO_NODE_META_EPHEMERAL_KEY_C25519 "e0"
/**
* Ephemeral NIST P-384 public key
*/
#define ZT_PROTO_NODE_META_EPHEMERAL_KEY_P384 "e1"
/**
* Addresses of ZeroTier nodes to whom this node will relay or one entry for 0000000000 if promiscuous.
*/
#define ZT_PROTO_NODE_META_WILL_RELAY_TO "r"
// ---------------------------------------------------------------------------
namespace ZeroTier {
@ -801,50 +831,7 @@ public:
*/
VERB_PUSH_DIRECT_PATHS = 0x10,
/**
* An acknowledgment of receipt of a series of recent packets from another
* peer. This is used to calculate relative throughput values and to detect
* packet loss. Only VERB_FRAME and VERB_EXT_FRAME packets are counted.
*
* ACK response format:
* <[4] 32-bit number of bytes received since last ACK>
*
* Upon receipt of this packet, the local peer will verify that the correct
* number of bytes were received by the remote peer. If these values do
* not agree that could be an indicator of packet loss.
*
* Additionally, the local peer knows the interval of time that has
* elapsed since the last received ACK. With this information it can compute
* a rough estimate of the current throughput.
*
* This is sent at a maximum rate of once per every ZT_PATH_ACK_INTERVAL
*/
VERB_ACK = 0x12,
/**
* A packet containing timing measurements useful for estimating path quality.
* Composed of a list of <packet ID:internal sojourn time> pairs for an
* arbitrary set of recent packets. This is used to sample for latency and
* packet delay variance (PDV, "jitter").
*
* QoS record format:
*
* <[8] 64-bit packet ID of previously-received packet>
* <[1] 8-bit packet sojourn time>
* <...repeat until end of max 1400 byte packet...>
*
* The number of possible records per QoS packet is: (1400 * 8) / 72 = 155
* This packet should be sent very rarely (every few seconds) as it can be
* somewhat large if the connection is saturated. Future versions might use
* a bloom table to probabilistically determine these values in a vastly
* more space-efficient manner.
*
* Note: The 'internal packet sojourn time' is a slight misnomer as it is a
* measure of the amount of time between when a packet was received and the
* egress time of its tracking QoS packet.
*
* This is sent at a maximum rate of once per every ZT_PATH_QOS_INTERVAL
*/
VERB_QOS_MEASUREMENT = 0x13,
/**