diff --git a/attic/MIMC52.cpp b/attic/MIMC52.cpp index 97a35d841..aa7885e98 100644 --- a/attic/MIMC52.cpp +++ b/attic/MIMC52.cpp @@ -14,6 +14,7 @@ #include "MIMC52.hpp" #include "SHA512.hpp" #include "Utils.hpp" +#include "Salsa20.hpp" // This gets defined on any architecture whose FPU is not capable of doing the mulmod52() FPU trick. //#define ZT_MIMC52_NO_FPU @@ -98,60 +99,52 @@ ZT_INLINE uint64_t modpow52(uint64_t a,uint64_t e,const uint64_t p) noexcept } // anonymous namespace +#define ZT_MIMC52_ROUND_CONSTANT_COUNT 1048576 + uint64_t mimc52Delay(const void *const salt,const unsigned int saltSize,const unsigned long rounds) { - uint64_t hash[6]; - SHA384(hash,salt,saltSize); + uint64_t hash[8]; + SHA512(hash,salt,saltSize); #if __BYTE_ORDER == __LITTLE_ENDIAN - uint64_t p = s_mimc52Primes[hash[0] & 511U]; + const uint64_t p = s_mimc52Primes[hash[0] & 511U]; uint64_t x = hash[1] % p; #else - uint64_t p = s_mimc52Primes[Utils::swapBytes(hash[0]) & 511U]; + const uint64_t p = s_mimc52Primes[Utils::swapBytes(hash[0]) & 511U]; uint64_t x = Utils::swapBytes(hash[1]) % p; #endif - //Speck128<8> roundConstantGenerator(hash + 2); const uint64_t e = ((p * 2) - 1) / 3; const uint64_t m52 = 0xfffffffffffffULL; - const uint64_t rmin1 = rounds - 1; - const uint64_t sxx = hash[4]; -#pragma unroll 16 - for(unsigned long r=0;r roundConstantGenerator(hash + 2); const uint64_t m52 = 0xfffffffffffffULL; uint64_t y = proof & m52; - const uint64_t sxx = hash[4]; -#if !defined(ZT_MIMC52_NO_FPU) - double ii,of,pp = (double)p; +#ifndef ZT_MIMC52_NO_FPU + double dy,ii,of,pp = (double)p; uint64_t oi,one = 1; #endif -#pragma unroll 16 for(unsigned long r=0;r [command args] Global Options: - -j Output raw JSON where applicable - -p Use alternate base path - -t Use secret auth token from this file + -j Output raw JSON where applicable + -p Use alternate base path + -t Load secret auth token from a file + -T Set secret auth token on command line Commands: - help Show this help - version Print version - service Start as service - status Show node status, identity, and config - peers List all VL1 peers - roots List root peers - addroot Add root - removeroot
Remove a peer from the root list - join [fingerprint] Join a virtual network - leave Leave a virtual network - networks List VL2 virtual networks - network Show verbose network info - set [option] [value] Get or set a network config option - manageips Is IP management allowed? - manageroutes Is route management allowed? - globalips Allow assignment of global IPs? - globalroutes Can global IP space routes be set? - defaultroute Can default route be overridden? - set [option] [value] Get or set a service config option - port Primary P2P port - secondaryport Secondary P2P port (0 to disable) - blacklist cidr Toggle physical path blacklisting - blacklist if Toggle interface prefix blacklisting - portmap Toggle use of uPnP or NAT-PMP - identity [args] Identity management commands - new [c25519|p384] Create identity pair (default: c25519) - getpublic Extract only public part of identity - validate Locally validate an identity - sign Sign a file with an identity's key - verify Verify a signature - makeroot
... Make a root spec (see docs) + help Show this help + version Print version + service Start as service + status Show node status, identity, and config + peers List all VL1 peers + roots List root peers + addroot Add root from root spec file or URL + removeroot
Remove a peer from the root list + join [fingerprint] Join a virtual network + leave Leave a virtual network + networks List VL2 virtual networks + network Show verbose network info + set [option] [value] Get or set a network config option + manageips Is IP management allowed? + manageroutes Is route management allowed? + globalips Allow assignment of global IPs? + globalroutes Can global IP space routes be set? + defaultroute Can default route be overridden? + set [option] [value] Get or set a service config option + port Primary P2P port + secondaryport Secondary P2P port (0 to disable) + blacklist cidr Toggle physical path blacklisting + blacklist if Toggle interface prefix blacklisting + portmap Toggle use of uPnP or NAT-PMP + identity [args] Identity management commands + new [c25519|p384] Create identity pair (default: c25519) + getpublic Extract only public part of identity + validate Locally validate an identity + sign Sign a file with an identity's key + verify Verify a signature + makeroot
[...] Make a root spec (see docs) The 'service' command does not exit until the service receives a signal. This is typically run from launchd (Mac), systemd or init (Linux), a Windows diff --git a/go/cmd/zerotier/cli/identity.go b/go/cmd/zerotier/cli/identity.go index ba5cba9dd..5f5b837f2 100644 --- a/go/cmd/zerotier/cli/identity.go +++ b/go/cmd/zerotier/cli/identity.go @@ -102,6 +102,11 @@ func Identity(args []string) { } } + case "makeroot": + if len(args) >= 2 { + //id := readIdentity(args[1]) + } + } } Help() diff --git a/go/cmd/zerotier/cli/join.go b/go/cmd/zerotier/cli/join.go index 18733d923..ba5972dde 100644 --- a/go/cmd/zerotier/cli/join.go +++ b/go/cmd/zerotier/cli/join.go @@ -28,7 +28,7 @@ func Join(basePath, authToken string, args []string) { os.Exit(1) } - if len(args[0]) != 16 { + if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) os.Exit(1) } diff --git a/go/cmd/zerotier/cli/leave.go b/go/cmd/zerotier/cli/leave.go index 3d3988738..f561a78bf 100644 --- a/go/cmd/zerotier/cli/leave.go +++ b/go/cmd/zerotier/cli/leave.go @@ -17,6 +17,7 @@ import ( "fmt" "os" "strconv" + "zerotier/pkg/zerotier" ) // Leave CLI command @@ -26,7 +27,7 @@ func Leave(basePath, authToken string, args []string) { os.Exit(1) } - if len(args[0]) != 16 { + if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) os.Exit(1) } diff --git a/go/cmd/zerotier/cli/network.go b/go/cmd/zerotier/cli/network.go index cd2b8d086..26d12626d 100644 --- a/go/cmd/zerotier/cli/network.go +++ b/go/cmd/zerotier/cli/network.go @@ -28,7 +28,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { os.Exit(1) } - if len(args[0]) != 16 { + if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) os.Exit(1) } diff --git a/go/cmd/zerotier/cli/setroot.go b/go/cmd/zerotier/cli/setroot.go deleted file mode 100644 index 281ef7b4f..000000000 --- a/go/cmd/zerotier/cli/setroot.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c)2013-2020 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: 2024-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" -) - -// SetRoot CLI command, used for addroot and removeroot. -func SetRoot(basePath, authToken string, args []string, root bool) { - if len(args) < 1 || len(args) > 2 { - Help() - os.Exit(1) - } - - id := readIdentity(args[0]) - if id == nil { - fmt.Printf("ERROR: invalid identity '%s' (tried literal or reading as file)\n",args[0]) - os.Exit(1) - } - - var bootstrap *zerotier.InetAddress - if len(args) == 2 { - bootstrap = zerotier.NewInetAddressFromString(args[1]) - if bootstrap == nil || bootstrap.Nil() { - fmt.Printf("ERROR: invalid bootstrap address '%s'\n",args[1]) - os.Exit(1) - } - } - - var peer zerotier.PeerMutableFields - peer.Identity = id - peer.Bootstrap = bootstrap - peer.Root = &root - apiPost(basePath, authToken, "/peer/"+id.Address().String(), &peer, nil) - fmt.Printf("OK %s", id.String()) - os.Exit(0) -} diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index f9bddc176..3150dfd74 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -74,13 +74,14 @@ func main() { } else { runtime.GOMAXPROCS(1) } - debug.SetGCPercent(25) + debug.SetGCPercent(20) globalOpts := flag.NewFlagSet("global", flag.ContinueOnError) hflag := globalOpts.Bool("h", false, "") // support -h to be canonical with other Unix utilities jflag := globalOpts.Bool("j", false, "") pflag := globalOpts.String("p", "", "") tflag := globalOpts.String("t", "", "") + tTflag := globalOpts.String("T", "", "") err := globalOpts.Parse(os.Args[1:]) if err != nil { cli.Help() @@ -98,11 +99,6 @@ func main() { cmdArgs = args[1:] } - if *hflag { - cli.Help() - os.Exit(0) - } - basePath := zerotier.PlatformDefaultHomePath if len(*pflag) > 0 { basePath = *pflag @@ -110,7 +106,14 @@ func main() { var authToken string if len(*tflag) > 0 { - authToken = *tflag + at, err := ioutil.ReadFile(*tflag) + if err != nil || len(at) == 0 { + fmt.Println("FATAL: unable to read API authorization token from file '" + *tflag + "'") + os.Exit(1) + } + authToken = strings.TrimSpace(string(at)) + } else if len(*tTflag) > 0 { + authToken = strings.TrimSpace(*tTflag) } else { authToken = readAuthToken(basePath) } @@ -125,38 +128,36 @@ func main() { os.Exit(0) case "service": cli.Service(basePath, authToken, cmdArgs) - case "status": + case "status", "info": authTokenRequired(authToken) cli.Status(basePath, authToken, cmdArgs, *jflag) - case "peers", "listpeers": + case "peers", "listpeers", "lspeers": authTokenRequired(authToken) cli.Peers(basePath, authToken, cmdArgs, *jflag, false) - case "roots", "listroots": + case "roots", "listroots", "lsroots": authTokenRequired(authToken) cli.Peers(basePath, authToken, cmdArgs, *jflag, true) case "addroot": - authTokenRequired(authToken) - cli.SetRoot(basePath, authToken, cmdArgs, true) + // TODO case "removeroot": - authTokenRequired(authToken) - cli.SetRoot(basePath, authToken, cmdArgs, false) - case "identity": - cli.Identity(cmdArgs) - case "networks", "listnetworks": - authTokenRequired(authToken) - cli.Networks(basePath, authToken, cmdArgs, *jflag) - case "network": - authTokenRequired(authToken) - cli.Network(basePath, authToken, cmdArgs, *jflag) + // TODO case "join": authTokenRequired(authToken) cli.Join(basePath, authToken, cmdArgs) case "leave": authTokenRequired(authToken) cli.Leave(basePath, authToken, cmdArgs) + case "networks", "listnetworks": + authTokenRequired(authToken) + cli.Networks(basePath, authToken, cmdArgs, *jflag) + case "network": + authTokenRequired(authToken) + cli.Network(basePath, authToken, cmdArgs, *jflag) case "set": authTokenRequired(authToken) cli.Set(basePath, authToken, cmdArgs) + case "identity": + cli.Identity(cmdArgs) } cli.Help() diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index e94958834..281ab731c 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/hex" "encoding/json" + "errors" "fmt" "runtime" "strings" @@ -241,6 +242,31 @@ func (id *Identity) Verify(msg, sig []byte) bool { return C.ZT_Identity_verify(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 } +// MakeRoot generates a root spec consisting of a serialized identity and a root locator. +func (id *Identity) MakeRoot(addresses []InetAddress) ([]byte, error) { + if len(addresses) == 0 { + return nil, errors.New("at least one static address must be specified for a root") + } + + id.cIdentity() + if uintptr(id.cid) == 0 { + return nil, errors.New("error initializing ZT_Identity") + } + + ss := make([]C.sockaddr_storage, len(addresses)) + for i := range addresses { + if !makeSockaddrStorage(addresses[i].IP, addresses[i].Port, &ss[i]) { + return nil, errors.New("invalid address in address list") + } + } + var buf [8192]byte + rl := C.ZT_Identity_makeRootSpecification(id.cid, C.int64_t(TimeMs()), &ss[0], C.uint(len(ss)), &buf[0], 8192) + if rl <= 0 { + return nil, errors.New("unable to make root specification (does identity contain a secret key?)") + } + return buf[0:int(rl)], nil +} + // Equals performs a deep equality test between this and another identity func (id *Identity) Equals(id2 *Identity) bool { if id2 == nil { diff --git a/go/pkg/zerotier/localconfig.go b/go/pkg/zerotier/localconfig.go index 7f5165324..f69093aea 100644 --- a/go/pkg/zerotier/localconfig.go +++ b/go/pkg/zerotier/localconfig.go @@ -24,9 +24,6 @@ import ( type LocalConfigPhysicalPathConfiguration struct { // Blacklist flags this path as unusable for ZeroTier traffic Blacklist bool - - // TrustedPathID identifies a path for unencrypted/unauthenticated traffic - TrustedPathID uint64 } // LocalConfigVirtualAddressConfiguration contains settings for virtual addresses @@ -35,14 +32,6 @@ type LocalConfigVirtualAddressConfiguration struct { Try []InetAddress `json:"try,omitempty"` } -// ExternalAddress is an externally visible address -type ExternalAddress struct { - InetAddress - - // Permanent indicates that this address should be incorporated into this node's Locator - Permanent bool `json:"permanent"` -} - // LocalConfigSettings contains node settings type LocalConfigSettings struct { // PrimaryPort is the main UDP port and must be set. @@ -60,14 +49,14 @@ type LocalConfigSettings struct { // LogSizeMax is the maximum size of the infoLog in kilobytes or 0 for no limit and -1 to disable logging LogSizeMax int `json:"logSizeMax"` - // IP/port to bind for TCP access to control API (disabled if null) + // IP/port to bind for TCP access to control API (TCP API port disabled if null) APITCPBindAddress *InetAddress `json:"apiTCPBindAddress,omitempty"` // InterfacePrefixBlacklist are prefixes of physical network interface names that won't be used by ZeroTier (e.g. "lo" or "utun") InterfacePrefixBlacklist []string `json:"interfacePrefixBlacklist,omitempty"` // ExplicitAddresses are explicit IP/port addresses to advertise to other nodes, such as externally mapped ports on a router - ExplicitAddresses []ExternalAddress `json:"explicitAddresses,omitempty"` + ExplicitAddresses []InetAddress `json:"explicitAddresses,omitempty"` } // LocalConfig is the local.conf file and stores local settings for the node. @@ -97,22 +86,18 @@ func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool, isTotallyNewN lc.Virtual = make(map[Address]LocalConfigVirtualAddressConfiguration) lc.Network = make(map[NetworkID]NetworkLocalSettings) - if isTotallyNewNode { - lc.Settings.PrimaryPort = 793 - } else { - // For legacy reasons we keep nodes that already existed prior to 2.0 (upgraded nodes) - // at 9993 by default if there is no existing primary port configured. This is for - // principle of least surprise since some admins may have special firewall rules for - // this port. - lc.Settings.PrimaryPort = 9993 - } + lc.Settings.PrimaryPort = 9993 lc.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))] lc.Settings.PortSearch = true lc.Settings.PortMapping = true lc.Settings.LogSizeMax = 128 - if !isTotallyNewNode { + + if !isTotallyNewNode && runtime.GOOS != "darwin" && runtime.GOOS != "windows" { + // If this doesn't look like a new node and it's not a desktop OS, go ahead + // and bind the local TCP API port so as not to break scripts. lc.Settings.APITCPBindAddress = NewInetAddressFromString("127.0.0.1/9993") } + switch runtime.GOOS { case "windows": lc.Settings.InterfacePrefixBlacklist = []string{"loopback"} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 424a558cc..43fdd87de 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -47,6 +47,9 @@ var nullLogger = log.New(ioutil.Discard, "", 0) // Network status states const ( + NetworkIDStringLength = 16 + AddressStringLength = 10 + NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION NetworkStatusOK int = C.ZT_NETWORK_STATUS_OK NetworkStatusAccessDenied int = C.ZT_NETWORK_STATUS_ACCESS_DENIED diff --git a/node/AES.hpp b/node/AES.hpp index 09f307e4d..6ffdb4f05 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -261,7 +261,15 @@ public: }; /** - * Encryptor for GMAC-SIV + * Encryptor for AES-GMAC-SIV. + * + * Encryption requires two passes. The first pass starts after init + * with aad (if any) followed by update1() and finish1(). Then the + * update2() and finish2() methods must be used over the same data + * (but NOT AAD) again. + * + * This supports encryption of a maximum of 2^31 bytes of data per + * call to init(). */ class GMACSIVEncryptor { @@ -294,12 +302,12 @@ public: } /** - * Process AAD (additional authenticated data) that is not being encrypted + * Process AAD (additional authenticated data) that is not being encrypted. * - * This must be called prior to update1, finish1, etc. if there is AAD to include - * in the MAC that is not included in the plaintext. + * If such data exists this must be called before update1() and finish1(). * - * This currently only supports one chunk of AAD. Don't call multiple times per message. + * Note: current code only supports one single chunk of AAD. Don't call this + * multiple times per message. * * @param aad Additional authenticated data * @param len Length of AAD in bytes @@ -336,15 +344,22 @@ public: // Compute 128-bit GMAC tag. _gmac.finish(reinterpret_cast(tmp)); - // Truncate to 64 bits, concatenate after 64-bit message IV, and encrypt with AES. + // Shorten to 64 bits, concatenate with message IV, and encrypt with AES to + // yield the CTR IV and opaque IV/MAC blob. In ZeroTier's use of GMAC-SIV + // this get split into the packet ID (64 bits) and the MAC (64 bits) in each + // packet and then recombined on receipt for legacy reasons (but with no + // cryptographic or performance impact). _tag[1] = tmp[0] ^ tmp[1]; _ctr._aes.encrypt(_tag,_tag); - // Get CTR IV and 32-bit counter. The most significant bit of the 32-bit counter - // is masked to zero so the counter will never overflow, but the remaining bits - // are taken from the encrypted tag as they can count as additional bits of - // entropy for the CTR IV. We don't technically count these in figuring our - // worst case scenario bound, but they could be argued to add a little margin. + // Initialize CTR with 96-bit CTR nonce and 32-bit counter. The counter + // incorporates 31 more bits of entropy which should raise our security margin + // a bit, but this is not included in the worst case analysis of GMAC-SIV. + // The most significant bit of the counter is masked to zero to allow up to + // 2^31 bytes to be encrypted before the counter loops. Some CTR implementations + // increment the whole big-endian 128-bit integer in which case this could be + // used for more than 2^31 bytes, but ours does not for performance reasons + // and so 2^31 should be considered the input limit. tmp[0] = _tag[0]; tmp[1] = _tag[1] & ZT_CONST_TO_BE_UINT64(0xffffffff7fffffffULL); _ctr.init(reinterpret_cast(tmp),_output); @@ -386,7 +401,9 @@ public: }; /** - * Decryptor for GMAC-SIV + * Decryptor for AES-GMAC-SIV. + * + * GMAC-SIV decryption is single-pass. AAD (if any) must be processed first. */ class GMACSIVDecryptor { diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index edf5b427e..8fe366a90 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -32,7 +32,6 @@ set(core_headers OS.hpp Path.hpp Peer.hpp - PeerList.hpp Poly1305.hpp Protocol.hpp RuntimeEnvironment.hpp diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 05d4dccf9..5856badb9 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -37,7 +37,7 @@ namespace ZeroTier { * compatibility. * * Use of the append functions is faster than building and then encoding a - * dictionary. + * dictionary for creating outbound packets. */ class Dictionary { diff --git a/node/FCV.hpp b/node/FCV.hpp index f0ea0ed24..4fe9d615f 100644 --- a/node/FCV.hpp +++ b/node/FCV.hpp @@ -19,23 +19,15 @@ #include #include #include -#include -#include namespace ZeroTier { /** * FCV is a Fixed Capacity Vector * - * Attempts to resize, push, or access this vector beyond its capacity will - * silently fail. The [] operator is NOT bounds checked! - * * This doesn't implement everything in std::vector, just what we need. It * also adds a few special things for use in ZT core code. * - * Note that an FCV will be TriviallyCopyable IF and only if its contained - * type is TriviallyCopyable. There's a const static checker for this. - * * @tparam T Type to contain * @tparam C Maximum capacity of vector */ @@ -46,11 +38,11 @@ public: typedef T * iterator; typedef const T * const_iterator; - ZT_INLINE FCV() noexcept : _s(0) {} // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) - ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE FCV() noexcept : _s(0) {} + ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; } template - ZT_INLINE FCV(I i,I end) : // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE FCV(I i,I end) : _s(0) { while (i != end) { @@ -63,7 +55,7 @@ public: ZT_INLINE FCV &operator=(const FCV &v) { - if (&v != this) { + if (likely(&v != this)) { this->clear(); const unsigned int s = v._s; _s = s; @@ -85,25 +77,7 @@ public: } /** - * Clear without calling destructors (same as unsafeResize(0)) - */ - ZT_INLINE void unsafeClear() noexcept { _s = 0; } - - /** - * This does a straight copy of one vector's data to another - * - * @tparam C2 Inferred capacity of other vector - * @param v Other vector to copy to this one - */ - template - ZT_INLINE void unsafeAssign(const FCV &v) noexcept - { - _s = ((C2 > C)&&(v._s > C)) ? C : v._s; - Utils::copy(_m,v._m,_s * sizeof(T)); - } - - /** - * Move contents from this vector to another and clear this vector + * Move contents from this vector to another and clear this vector. * * @param v Target vector */ @@ -114,18 +88,29 @@ public: } ZT_INLINE iterator begin() noexcept { return reinterpret_cast(_m); } - ZT_INLINE const_iterator begin() const noexcept { return reinterpret_cast(_m); } ZT_INLINE iterator end() noexcept { return reinterpret_cast(_m) + _s; } + ZT_INLINE const_iterator begin() const noexcept { return reinterpret_cast(_m); } ZT_INLINE const_iterator end() const noexcept { return reinterpret_cast(_m) + _s; } - ZT_INLINE T &operator[](const unsigned int i) noexcept { return reinterpret_cast(_m)[i]; } - ZT_INLINE const T &operator[](const unsigned int i) const noexcept { return reinterpret_cast(_m)[i]; } + ZT_INLINE T &operator[](const unsigned int i) + { + if (likely(i < _s)) + return reinterpret_cast(_m)[i]; + throw std::out_of_range("i > capacity"); + } + ZT_INLINE const T &operator[](const unsigned int i) const + { + if (likely(i < _s)) + return reinterpret_cast(_m)[i]; + throw std::out_of_range("i > capacity"); + } + static constexpr unsigned int capacity() noexcept { return C; } ZT_INLINE unsigned int size() const noexcept { return _s; } ZT_INLINE bool empty() const noexcept { return (_s == 0); } + ZT_INLINE T *data() noexcept { return reinterpret_cast(_m); } ZT_INLINE const T *data() const noexcept { return reinterpret_cast(_m); } - static constexpr unsigned int capacity() noexcept { return C; } /** * Push a value onto the back of this vector @@ -136,8 +121,9 @@ public: */ ZT_INLINE void push_back(const T &v) { - if (_s < C) + if (likely(_s < C)) new (reinterpret_cast(_m) + _s++) T(v); + else throw std::out_of_range("capacity exceeded"); } /** @@ -147,7 +133,7 @@ public: */ ZT_INLINE T &push() { - if (_s < C) { + if (likely(_s < C)) { return *(new(reinterpret_cast(_m) + _s++) T()); } else { return *(reinterpret_cast(_m) + (C - 1)); @@ -161,7 +147,7 @@ public: */ ZT_INLINE T &push(const T &v) { - if (_s < C) { + if (likely(_s < C)) { return *(new(reinterpret_cast(_m) + _s++) T(v)); } else { T &tmp = *(reinterpret_cast(_m) + (C - 1)); @@ -175,7 +161,7 @@ public: */ ZT_INLINE void pop_back() { - if (_s != 0) + if (likely(_s != 0)) (reinterpret_cast(_m) + --_s)->~T(); } @@ -186,8 +172,8 @@ public: */ ZT_INLINE void resize(unsigned int ns) { - if (ns > C) - ns = C; + if (unlikely(ns > C)) + throw std::out_of_range("capacity exceeded"); unsigned int s = _s; while (s < ns) new(reinterpret_cast(_m) + s++) T(); @@ -196,16 +182,6 @@ public: _s = s; } - /** - * Resize without calling any constructors or destructors on T - * - * This must only be called if T is a primitive type or is TriviallyCopyable and - * safe to initialize from undefined contents. - * - * @param ns New size (clipped to C if larger than capacity) - */ - ZT_INLINE void unsafeResize(const unsigned int ns) noexcept { _s = (ns > C) ? C : ns; } - /** * This is a bounds checked auto-resizing variant of the [] operator * @@ -267,8 +243,12 @@ public: ZT_INLINE bool operator>=(const FCV &v) const noexcept { return !(*this < v); } private: - unsigned int _s; +#ifdef _MSC_VER uint8_t _m[sizeof(T) * C]; +#else + __attribute__((aligned(16))) uint8_t _m[sizeof(T) * C]; +#endif + unsigned int _s; }; } // namespace ZeroTier diff --git a/node/Identity.cpp b/node/Identity.cpp index b38cefcae..7751edaa4 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -631,11 +631,13 @@ const ZT_Fingerprint *ZT_Identity_fingerprint(const ZT_Identity *id) int ZT_Identity_makeRootSpecification(ZT_Identity *id,int64_t ts,struct sockaddr_storage *addrs,unsigned int addrcnt,void *rootSpecBuf,unsigned int rootSpecBufSize) { + if ((!id)||(!addrs)||(!addrcnt)||(!rootSpecBuf)) + return -1; ZeroTier::Vector endpoints; endpoints.reserve(addrcnt); for(unsigned int i=0;i(id),endpoints,rootSpecBuf,rootSpecBufSize); + endpoints.push_back(ZeroTier::Endpoint(ZeroTier::asInetAddress(addrs[i]))); + return ZeroTier::Locator::makeRootSpecification(*reinterpret_cast(id),ts,endpoints,rootSpecBuf,rootSpecBufSize); } ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id) diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 875c252ff..e091c69fd 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -261,9 +261,9 @@ public: { switch (as.ss.ss_family) { case AF_INET: - return Utils::ntoh((uint16_t) as.sa_in.sin_port); + return Utils::ntoh((uint16_t)as.sa_in.sin_port); case AF_INET6: - return Utils::ntoh((uint16_t) as.sa_in6.sin6_port); + return Utils::ntoh((uint16_t)as.sa_in6.sin6_port); default: return 0; } diff --git a/node/Membership.hpp b/node/Membership.hpp index 625ca9679..cfc503ce5 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -14,8 +14,6 @@ #ifndef ZT_MEMBERSHIP_HPP #define ZT_MEMBERSHIP_HPP -#include - #include "Constants.hpp" #include "Credential.hpp" #include "Containers.hpp" diff --git a/node/Meter.hpp b/node/Meter.hpp index f032d2f17..ef03205a8 100644 --- a/node/Meter.hpp +++ b/node/Meter.hpp @@ -44,7 +44,7 @@ public: * @param now Start time */ ZT_INLINE Meter() noexcept - {} // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init,hicpp-use-equals-default,modernize-use-equals-default) + {} /** * Add a measurement diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 5e5ea13b7..f05255387 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -14,13 +14,6 @@ #ifndef ZT_NETWORKCONFIG_HPP #define ZT_NETWORKCONFIG_HPP -#include -#include -#include -#include -#include -#include - #include "Constants.hpp" #include "InetAddress.hpp" #include "MulticastGroup.hpp" @@ -34,6 +27,10 @@ #include "Utils.hpp" #include "Trace.hpp" #include "TriviallyCopyable.hpp" +#include "Containers.hpp" + +#include +#include namespace ZeroTier { diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp index a4f1934ce..64f0a4c21 100644 --- a/node/NetworkController.hpp +++ b/node/NetworkController.hpp @@ -14,8 +14,6 @@ #ifndef ZT_NETWORKCONFIGMASTER_HPP #define ZT_NETWORKCONFIGMASTER_HPP -#include - #include "Constants.hpp" #include "Dictionary.hpp" #include "NetworkConfig.hpp" diff --git a/node/Node.cpp b/node/Node.cpp index 41490062f..3f1fb4a57 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -341,12 +341,10 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u ZT_ResultCode Node::addRoot(void *tPtr,const void *rdef,unsigned int rdeflen) { + if ((!rdef)||(rdeflen == 0)) + return ZT_RESULT_ERROR_BAD_PARAMETER; std::pair r(Locator::parseRootSpecification(rdef,rdeflen)); - if (r.first) { - RR->topology->addRoot(tPtr,r.first,r.second); - return ZT_RESULT_OK; - } - return ZT_RESULT_ERROR_BAD_PARAMETER; + return ((r.first)&&(RR->topology->addRoot(tPtr,r.first,r.second))) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; } ZT_ResultCode Node::removeRoot(void *tPtr,const ZT_Fingerprint *fp) diff --git a/node/Node.hpp b/node/Node.hpp index 41dca5b54..655fefcf8 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -130,7 +130,7 @@ public: m_uPtr, tPtr, localSocket, - reinterpret_cast(&addr), + &addr.as.ss, data, len, ttl) == 0); @@ -306,9 +306,9 @@ public: bool localControllerHasAuthorized(int64_t now,uint64_t nwid,const Address &addr) const; // Implementation of NetworkController::Sender interface - virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig); // NOLINT(cppcoreguidelines-explicit-virtual-functions,hicpp-use-override,modernize-use-override) - virtual void ncSendRevocation(const Address &destination,const Revocation &rev); // NOLINT(cppcoreguidelines-explicit-virtual-functions,hicpp-use-override,modernize-use-override) - virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); // NOLINT(cppcoreguidelines-explicit-virtual-functions,hicpp-use-override,modernize-use-override) + virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig); + virtual void ncSendRevocation(const Address &destination,const Revocation &rev); + virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); private: RuntimeEnvironment m_RR; diff --git a/node/Path.hpp b/node/Path.hpp index 519379cac..f6c6cada3 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -41,7 +41,7 @@ class Path friend class Defragmenter; public: - ZT_INLINE Path(const int64_t l,const InetAddress &r) noexcept : // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE Path(const int64_t l,const InetAddress &r) noexcept : m_localSocket(l), m_lastIn(0), m_lastOut(0), diff --git a/node/Peer.hpp b/node/Peer.hpp index ce3f4241b..f6fff9401 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -327,10 +327,10 @@ public: /** * Check whether a key is ephemeral - * + * * This is used to check whether a packet is received with forward secrecy enabled * or not. - * + * * @param k Key to check * @return True if this key is ephemeral, false if it's the long-lived identity key */ diff --git a/node/Protocol.hpp b/node/Protocol.hpp index f37b51ff0..c69be4a25 100644 --- a/node/Protocol.hpp +++ b/node/Protocol.hpp @@ -250,7 +250,7 @@ #define ZT_PROTO_PACKET_VERB_INDEX 27 #define ZT_PROTO_HELLO_NODE_META_INSTANCE_ID "i" -#define ZT_PROTO_HELLO_NODE_META_PREFERRED_SYMMETRIC "a" +#define ZT_PROTO_HELLO_NODE_META_PREFERRED_CIPHER_MODE "a" #define ZT_PROTO_HELLO_NODE_META_LOCATOR "l" #define ZT_PROTO_HELLO_NODE_META_SOFTWARE_VENDOR "s" #define ZT_PROTO_HELLO_NODE_META_COMPLIANCE "c" @@ -340,7 +340,7 @@ enum Verb * Dictionary fields (defines start with ZT_PROTO_HELLO_NODE_META_): * * INSTANCE_ID - a 64-bit unique value generated on each node start - * PREFERRED_SYMMETRIC - preferred symmetric encryption mode + * PREFERRED_CIPHER_MODE - preferred symmetric encryption mode * LOCATOR - signed record enumerating this node's trusted contact points * EPHEMERAL_PUBLIC - Ephemeral public key(s) * @@ -419,10 +419,10 @@ enum Verb /** * Relay-mediated NAT traversal or firewall punching initiation: - * <[1] flags (unused, currently 0)> + * <[1] flags> * <[5] ZeroTier address of other peer> * <[2] 16-bit number of endpoints where peer might be reached> - * <[...] endpoints to attempt> + * [<[...] endpoints to attempt>] * * Legacy packet format for pre-2.x peers: * <[1] flags (unused, currently 0)> diff --git a/node/Revocation.hpp b/node/Revocation.hpp index f7749280f..ee84d7ba1 100644 --- a/node/Revocation.hpp +++ b/node/Revocation.hpp @@ -14,11 +14,6 @@ #ifndef ZT_REVOCATION_HPP #define ZT_REVOCATION_HPP -#include -#include -#include -#include - #include "Constants.hpp" #include "Credential.hpp" #include "Address.hpp" diff --git a/node/Tag.hpp b/node/Tag.hpp index 81013b3b9..1b27a0327 100644 --- a/node/Tag.hpp +++ b/node/Tag.hpp @@ -14,11 +14,6 @@ #ifndef ZT_TAG_HPP #define ZT_TAG_HPP -#include -#include -#include -#include - #include "Constants.hpp" #include "Credential.hpp" #include "C25519.hpp" diff --git a/node/Topology.cpp b/node/Topology.cpp index 766d1bdd8..0bbb8642a 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -71,14 +71,15 @@ struct p_RootSortComparisonOperator } }; -void Topology::addRoot(void *const tPtr, const Identity &id, const Locator &loc) +bool Topology::addRoot(void *const tPtr, const Identity &id, const Locator &loc) { - if (id == RR->identity) - return; + if ((id == RR->identity) || (!id) || (!loc) || (!loc.verify(id)) || (!id.locallyValidate())) + return false; RWMutex::Lock l1(m_peers_l); m_roots[id] = loc; m_updateRootPeers(tPtr); m_writeRootList(tPtr); + return true; } bool Topology::removeRoot(void *const tPtr, const Fingerprint &fp) diff --git a/node/Topology.hpp b/node/Topology.hpp index dca1d1bdc..f107f61c6 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -185,11 +185,15 @@ public: /** * Add or update a root server and its locator * + * This also validates the identity and checks the locator signature, + * returning false if either of these is not valid. + * * @param tPtr Thread pointer * @param id Root identity * @param loc Root locator + * @return True if identity and locator are valid and root was added / updated */ - void addRoot(void *tPtr,const Identity &id,const Locator &loc); + bool addRoot(void *tPtr,const Identity &id,const Locator &loc); /** * Remove a root server's identity from the root server set diff --git a/node/TriviallyCopyable.hpp b/node/TriviallyCopyable.hpp index d6358b47a..c9283299c 100644 --- a/node/TriviallyCopyable.hpp +++ b/node/TriviallyCopyable.hpp @@ -53,62 +53,6 @@ public: Utils::zero(&obj); } - /** - * Copy a TriviallyCopyable object - * - * @tparam T Automatically inferred type of destination - * @param dest Destination TriviallyCopyable object - * @param src Source TriviallyCopyable object - */ - template - static ZT_INLINE void memoryCopy(T *dest,const T *src) noexcept - { - mustBeTriviallyCopyable(dest); - Utils::copy(dest,src); - } - - /** - * Copy a TriviallyCopyable object - * - * @tparam T Automatically inferred type of destination - * @param dest Destination TriviallyCopyable object - * @param src Source TriviallyCopyable object - */ - template - static ZT_INLINE void memoryCopy(T *dest,const T &src) noexcept - { - mustBeTriviallyCopyable(src); - Utils::copy(dest,&src); - } - - /** - * Copy a TriviallyCopyable object - * - * @tparam T Automatically inferred type of destination - * @param dest Destination TriviallyCopyable object - * @param src Source TriviallyCopyable object - */ - template - static ZT_INLINE void memoryCopy(T &dest,const T *src) noexcept - { - mustBeTriviallyCopyable(dest); - Utils::copy(&dest,src); - } - - /** - * Copy a TriviallyCopyable object - * - * @tparam T Automatically inferred type of destination - * @param dest Destination TriviallyCopyable object - * @param src Source TriviallyCopyable object - */ - template - static ZT_INLINE void memoryCopy(T &dest,const T &src) noexcept - { - mustBeTriviallyCopyable(dest); - Utils::copy(&dest,&src); - } - private: static ZT_INLINE void mustBeTriviallyCopyable(const TriviallyCopyable &) noexcept {} static ZT_INLINE void mustBeTriviallyCopyable(const TriviallyCopyable *) noexcept {}