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

@ -37,14 +37,17 @@ Global Options:
Commands:
help Show this help
version Print version
service Start in system service mode
status Show ZeroTier service status and config
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 <identity> Add VL1 root server
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 new identity (including secret)
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
@ -56,7 +59,7 @@ Commands:
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?
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
@ -67,8 +70,7 @@ Commands:
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
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,72 +86,21 @@ 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
}
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
}
// 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
}
//////////////////////////////////////////////////////////////////////////////
fmt.Println()
if C.ZT_TestCrypto() != 0 {
return false
}
fmt.Println()
if C.ZT_TestIdentity() != 0 {
return false
}
return true
}
// Node is an instance of the ZeroTier core node and related C++ I/O code
type Node struct {

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

@ -41,7 +41,8 @@ public:
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
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,
/**