From b9bf6d1242dbab424cde582631c7106ad563ac26 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 29 May 2020 06:30:02 -0700 Subject: [PATCH] Simplification of addRoot/removeRoot, and some code reformatting and other cleanup across multiple files. --- go/attic/endpoint.go | 145 ---------- go/attic/locator.go | 56 ---- go/cmd/zerotier-fuzz/zerotier-fuzz.go | 4 - go/cmd/zerotier/cli/identity.go | 1 - go/cmd/zerotier/zerotier.go | 67 ++--- go/pkg/zerotier/endpoint.go | 93 ++++++ go/pkg/zerotier/fingerprint.go | 42 +-- go/pkg/zerotier/node.go | 1 - go/pkg/zerotier/peer.go | 17 +- include/ZeroTierCore.h | 350 ++++++++++++++++------- node/Address.hpp | 9 +- node/CMakeLists.txt | 1 + node/Containers.hpp | 2 + node/Dictionary.cpp | 140 ++++++--- node/Dictionary.hpp | 70 +++-- node/Endpoint.cpp | 177 ++++++++++-- node/Endpoint.hpp | 97 ++++--- node/FCV.hpp | 105 +++++-- node/Fingerprint.hpp | 100 ++++--- node/Identity.cpp | 55 ++-- node/Identity.hpp | 8 +- node/Locator.cpp | 184 +++++------- node/Locator.hpp | 104 +++---- node/MAC.hpp | 72 ++++- node/Membership.hpp | 2 +- node/MulticastGroup.hpp | 2 - node/Network.cpp | 2 +- node/NetworkConfig.hpp | 45 ++- node/Node.cpp | 392 ++++++++++++++------------ node/Node.hpp | 178 +++++++----- node/Path.cpp | 2 +- node/Path.hpp | 33 ++- node/Peer.cpp | 68 ++--- node/Peer.hpp | 143 +++++----- node/Revocation.hpp | 59 ++-- node/SHA512.cpp | 15 +- node/SelfAwareness.hpp | 32 ++- node/Tests.cpp | 7 +- node/Topology.cpp | 62 ++-- node/Topology.hpp | 8 +- 40 files changed, 1680 insertions(+), 1270 deletions(-) delete mode 100644 go/attic/endpoint.go delete mode 100644 go/attic/locator.go delete mode 100644 go/cmd/zerotier-fuzz/zerotier-fuzz.go create mode 100644 go/pkg/zerotier/endpoint.go diff --git a/go/attic/endpoint.go b/go/attic/endpoint.go deleted file mode 100644 index f13473966..000000000 --- a/go/attic/endpoint.go +++ /dev/null @@ -1,145 +0,0 @@ -package attic - -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 -) - -// Endpoint wraps a variety of different ways of describing a node's physical network location. -type Endpoint struct { - // Type is this endpoint's type - Type int - - // Location is the X, Y, Z coordinate of this endpoint or 0,0,0 if unspecified. - Location [3]int - - value, value2 interface{} -} - -var ( - ErrInvalidEndpoint = errors.New("invalid marshaled endpoint object") -) - -func (ep *Endpoint) unmarshalZT(b []byte) (int, error) { - if len(b) < 7 { - return 0, ErrInvalidEndpoint - } - ep.Type = int(b[0]) - ep.Location[0] = int(binary.BigEndian.Uint16(b[1:3])) - ep.Location[1] = int(binary.BigEndian.Uint16(b[3:5])) - ep.Location[2] = int(binary.BigEndian.Uint16(b[5:7])) - ep.value = nil - ep.value2 = nil - switch ep.Type { - case EndpointTypeNil: - return 7, nil - case EndpointTypeInetAddr: - ina := new(InetAddress) - inlen, err := ina.unmarshalZT(b[7:]) - if err != nil { - return 0, err - } - ep.value = ina - return 7 + inlen, nil - case EndpointTypeDnsName: - stringEnd := 0 - for i := 7; i < len(b); i++ { - if b[i] == 0 { - stringEnd = i + 1 - break - } - } - if stringEnd == 0 || (stringEnd+2) > len(b) { - return 0, ErrInvalidEndpoint - } - ep.value = string(b[7:stringEnd]) - port := binary.BigEndian.Uint16(b[stringEnd : stringEnd+2]) - ep.value2 = &port - return stringEnd + 2, nil - case EndpointTypeZeroTier: - if len(b) < 60 { - return 0, ErrInvalidEndpoint - } - a, err := NewAddressFromBytes(b[7:12]) - if err != nil { - return 0, err - } - ep.value = a - ep.value2 = append(make([]byte, 0, 48), b[12:60]...) - return 60, nil - case EndpointTypeUrl: - stringEnd := 0 - for i := 7; i < len(b); i++ { - if b[i] == 0 { - stringEnd = i + 1 - break - } - } - if stringEnd == 0 { - return 0, ErrInvalidEndpoint - } - ep.value = string(b[7:stringEnd]) - return stringEnd, nil - case EndpointTypeEthernet: - if len(b) < 13 { - return 0, ErrInvalidEndpoint - } - m, err := NewMACFromBytes(b[7:13]) - if err != nil { - return 0, err - } - ep.value = m - return 13, nil - default: - if len(b) < 8 { - return 0, ErrInvalidEndpoint - } - ep.Type = EndpointTypeUnrecognized - return 8 + int(b[1]), nil - } -} - -// InetAddress gets the address associated with this endpoint or nil if it is not of this type. -func (ep *Endpoint) InetAddress() *InetAddress { - v, _ := ep.value.(*InetAddress) - return v -} - -// Address gets the address associated with this endpoint or nil if it is not of this type. -func (ep *Endpoint) Address() *Address { - v, _ := ep.value.(*Address) - return v -} - -// DNSName gets the DNS name and port associated with this endpoint or an empty string and -1 if it is not of this type. -func (ep *Endpoint) DNSName() (string, int) { - if ep.Type == EndpointTypeDnsName { - return ep.value.(string), int(*(ep.value2.(*uint16))) - } - return "", -1 -} - -// InetAddress gets the URL assocaited with this endpoint or an empty string if it is not of this type. -func (ep *Endpoint) URL() string { - if ep.Type == EndpointTypeUrl { - return ep.value.(string) - } - return "" -} - -// Ethernet gets the address associated with this endpoint or nil if it is not of this type. -func (ep *Endpoint) Ethernet() *MAC { - v, _ := ep.value.(*MAC) - return v -} diff --git a/go/attic/locator.go b/go/attic/locator.go deleted file mode 100644 index 989280c41..000000000 --- a/go/attic/locator.go +++ /dev/null @@ -1,56 +0,0 @@ -package attic - -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") -) - -// Timestamp returns this locator's timestamp in milliseconds since epoch. -func (l Locator) Timestamp() int64 { - if len(l) >= 8 { - return int64(binary.BigEndian.Uint64(l[0:8])) - } - return 0 -} - -// Nil returns true if this is a nil/empty locator. -func (l Locator) Nil() bool { - return len(l) < 8 || int64(binary.BigEndian.Uint64(l[0:8])) <= 0 -} - -// Endpoints obtains the endpoints described by this locator. -func (l Locator) Endpoints() (eps []Endpoint, err error) { - if len(l) < 8 { - err = ErrInvalidLocator - return - } - if int64(binary.BigEndian.Uint64(l[0:8])) > 0 { - if len(l) < 10 { - 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 -} diff --git a/go/cmd/zerotier-fuzz/zerotier-fuzz.go b/go/cmd/zerotier-fuzz/zerotier-fuzz.go deleted file mode 100644 index da29a2cad..000000000 --- a/go/cmd/zerotier-fuzz/zerotier-fuzz.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -func main() { -} diff --git a/go/cmd/zerotier/cli/identity.go b/go/cmd/zerotier/cli/identity.go index 5f5b837f2..643614f96 100644 --- a/go/cmd/zerotier/cli/identity.go +++ b/go/cmd/zerotier/cli/identity.go @@ -104,7 +104,6 @@ func Identity(args []string) { case "makeroot": if len(args) >= 2 { - //id := readIdentity(args[1]) } } diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index 3150dfd74..7058c3856 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -27,53 +27,32 @@ import ( "zerotier/pkg/zerotier" ) -func readAuthToken(basePath string) string { - data, _ := ioutil.ReadFile(path.Join(basePath, "authtoken.secret")) - if len(data) > 0 { - return string(data) - } +func getAuthTokenPaths(basePath string) (p []string) { + p = append(p,path.Join(basePath,"authtoken.secret")) userHome, _ := os.UserHomeDir() if len(userHome) > 0 { if runtime.GOOS == "darwin" { - data, _ = ioutil.ReadFile(userHome + "/Library/Application Support/ZeroTier/authtoken.secret") - if len(data) > 0 { - return string(data) - } - data, _ = ioutil.ReadFile(userHome + "/Library/Application Support/ZeroTier/One/authtoken.secret") - if len(data) > 0 { - return string(data) - } - } - data, _ = ioutil.ReadFile(path.Join(userHome, ".zerotierauth")) - if len(data) > 0 { - return string(data) - } - data, _ = ioutil.ReadFile(path.Join(userHome, ".zeroTierOneAuthToken")) - if len(data) > 0 { - return string(data) + p = append(p,path.Join(userHome,"Library","Application Support","ZeroTier","authtoken.secret")) + p = append(p,path.Join(userHome,"Library","Application Support","ZeroTier","One","authtoken.secret")) } + p = append(p,path.Join(userHome,".zerotierauth")) + p = append(p,path.Join(userHome,".zeroTierOneAuthToken")) } - return "" + return p } func authTokenRequired(authToken string) { if len(authToken) == 0 { - fmt.Println("FATAL: unable to read API authorization token from service path or user home ('sudo' may be needed)") + fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.") os.Exit(1) } } func main() { - // Reduce Go's threads to 1-2 depending on whether this is single core or - // multi-core. Note that I/O threads are in C++ and are separate and Go - // code only does service control and CLI stuff, so this reduces memory - // use and competition with I/O but shouldn't impact throughput. We also - // crank up the GC to reduce memory usage a little bit. - if runtime.NumCPU() >= 2 { - runtime.GOMAXPROCS(2) - } else { - runtime.GOMAXPROCS(1) - } + // Reduce Go's thread and memory footprint. This would slow things down if the Go code + // were doing a lot, but it's not. It just manages the core and is not directly involved + // in pushing a lot of packets around. If that ever changes this should be adjusted. + runtime.GOMAXPROCS(1) debug.SetGCPercent(20) globalOpts := flag.NewFlagSet("global", flag.ContinueOnError) @@ -103,19 +82,33 @@ func main() { if len(*pflag) > 0 { basePath = *pflag } + authTokenPaths := getAuthTokenPaths(basePath) var authToken string if len(*tflag) > 0 { at, err := ioutil.ReadFile(*tflag) if err != nil || len(at) == 0 { - fmt.Println("FATAL: unable to read API authorization token from file '" + *tflag + "'") + fmt.Println("FATAL: unable to read local service API authorization token from " + *tflag) os.Exit(1) } - authToken = strings.TrimSpace(string(at)) + authToken = string(at) } else if len(*tTflag) > 0 { - authToken = strings.TrimSpace(*tTflag) + authToken = *tTflag } else { - authToken = readAuthToken(basePath) + for _, p := range authTokenPaths { + tmp, _ := ioutil.ReadFile(p) + if len(tmp) > 0 { + authToken = string(tmp) + break; + } + } + if len(authToken) == 0 { + fmt.Println("FATAL: unable to read local service API authorization token from any of:") + for _, p := range authTokenPaths { + fmt.Println(" " + p) + } + os.Exit(1) + } } authToken = strings.TrimSpace(authToken) diff --git a/go/pkg/zerotier/endpoint.go b/go/pkg/zerotier/endpoint.go new file mode 100644 index 000000000..743f697a3 --- /dev/null +++ b/go/pkg/zerotier/endpoint.go @@ -0,0 +1,93 @@ +package zerotier + +// #include "../../native/GoGlue.h" +import "C" + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "unsafe" +) + +const ( + EndpointTypeNil = C.ZT_ENDPOINT_TYPE_NIL + EndpointTypeZeroTier = C.ZT_ENDPOINT_TYPE_ZEROTIER + EndpointTypeEthernet = C.ZT_ENDPOINT_TYPE_ETHERNET + EndpointTypeWifiDirect = C.ZT_ENDPOINT_TYPE_WIFI_DIRECT + EndpointTypeBluetooth = C.ZT_ENDPOINT_TYPE_BLUETOOTH + EndpointTypeIp = C.ZT_ENDPOINT_TYPE_IP + EndpointTypeIpUdp = C.ZT_ENDPOINT_TYPE_IP_UDP + EndpointTypeIpTcp = C.ZT_ENDPOINT_TYPE_IP_TCP + EndpointTypeIpHttp2 = C.ZT_ENDPOINT_TYPE_IP_HTTP2 +) + +type Endpoint struct { + cep C.ZT_Endpoint +} + +// Type returns this endpoint's type. +func (ep *Endpoint) Type() int { + return ep.cep._type +} + +// InetAddress gets this Endpoint as an InetAddress or nil if its type is not addressed by one. +func (ep *Endpoint) InetAddress() *InetAddress { + switch ep.cep._type { + case EndpointTypeIp, EndpointTypeIpUdp, EndpointTypeIpTcp, EndpointTypeIpHttp2: + ua := sockaddrStorageToUDPAddr(&(ep.cep.a.ss)) + return &InetAddress{IP: ua.IP, Port: ua.Port} + } + return nil +} + +func (ep *Endpoint) String() string { + switch ep.cep._type { + case EndpointTypeZeroTier: + fp := Fingerprint{Address: Address(ep.cep.a.fp.address), Hash: *((*[48]byte)(unsafe.Pointer(&ep.cep.a.fp.hash[0])))} + return fmt.Sprintf("%d/%s", ep.Type(), fp.String()) + case EndpointTypeEthernet, EndpointTypeWifiDirect, EndpointTypeBluetooth: + return fmt.Sprintf("%d/%s", ep.Type(), MAC(ep.cep.a.mac).String()) + case EndpointTypeIp, EndpointTypeIpUdp, EndpointTypeIpTcp, EndpointTypeIpHttp2: + return fmt.Sprintf("%d/%s", ep.Type(), ep.InetAddress().String()) + } + return fmt.Sprintf("%d", ep.Type()) +} + +func (ep *Endpoint) MarshalJSON() ([]byte, error) { + s := ep.String() + return json.Marshal(&s) +} + +func (ep *Endpoint) UnmarshalJSON(j []byte) error { + var s string + err := json.Unmarshal(j, &s) + if err != nil { + return err + } + + slashIdx := strings.IndexRune(s, '/') + if slashIdx < 0 { + ep.cep._type = C.uint(strconv.ParseUint(s, 10, 32)) + } else if slashIdx == 0 || slashIdx >= (len(s)-1) { + return ErrInvalidParameter + } else { + ep.cep._type = C.uint(strconv.ParseUint(s[0:slashIdx], 10, 32)) + s = s[slashIdx+1:] + } + + switch ep.cep._type { + case EndpointTypeNil: + return nil + case EndpointTypeZeroTier: + fp, err := NewFingerprintFromString(s) + if err != nil { + return err + } + ep.cep.a.fp.address = C.uint64_t(fp.Address) + copy(((*[48]byte)(unsafe.Pointer(&ep.cep.a.fp.hash[0])))[:], fp.Hash[:]) + return nil + } + return ErrInvalidParameter +} diff --git a/go/pkg/zerotier/fingerprint.go b/go/pkg/zerotier/fingerprint.go index 595aee84d..5988a1392 100644 --- a/go/pkg/zerotier/fingerprint.go +++ b/go/pkg/zerotier/fingerprint.go @@ -13,41 +13,51 @@ package zerotier -//#cgo CFLAGS: -O3 -//#include "../../native/GoGlue.h" +// #include "../../native/GoGlue.h" import "C" import ( "bytes" - "errors" + "fmt" "strings" "unsafe" ) type Fingerprint struct { - Address Address `json:"address"` - Hash [48]byte `json:"hash"` + Address Address `json:"address"` + Hash []byte `json:"hash"` } func NewFingerprintFromString(fps string) (*Fingerprint, error) { - fpb, err := Base32StdLowerCase.DecodeString(strings.TrimSpace(strings.ToLower(fps))) + if len(fps) < AddressStringLength { + return nil, ErrInvalidZeroTierAddress + } + ss := strings.Split(fps, "/") + if len(ss) < 1 || len(ss) > 2 { + return nil, ErrInvalidParameter + } + a, err := NewAddressFromString(ss[0]) if err != nil { return nil, err } - if len(fpb) != 53 { - return nil, errors.New("invalid fingerprint length") + if len(ss) == 2 { + h, err := Base32StdLowerCase.DecodeString(ss[1]) + if err != nil { + return nil, err + } + if len(h) != 48 { + return nil, ErrInvalidParameter + } + return &Fingerprint{Address: a, Hash: h}, nil } - var fp Fingerprint - fp.Address, _ = NewAddressFromBytes(fpb[0:5]) - copy(fp.Hash[:],fpb[5:]) - return &fp, nil + return &Fingerprint{Address: a, Hash: nil}, nil } func (fp *Fingerprint) String() string { - var tmp [53]byte - fp.Address.CopyTo(tmp[0:5]) - copy(tmp[5:],fp.Hash[:]) - return Base32StdLowerCase.EncodeToString(tmp[:]) + if len(fp.Hash) == 48 { + return fmt.Sprintf("%.10x/%s", uint64(fp.Address), Base32StdLowerCase.EncodeToString(fp.Hash)) + } + return fp.Address.String() } func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool { diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index a2a19b589..ef302cbbf 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -510,7 +510,6 @@ func (n *Node) Peers() []*Peer { p2.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)} p2.Latency = int(p.latency) p2.Root = p.root != 0 - p2.Bootstrap = NewInetAddressFromSockaddr(unsafe.Pointer(&p.bootstrap)) p2.Paths = make([]Path, 0, int(p.pathCount)) for j := 0; j < len(p2.Paths); j++ { diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go index 4baeaf971..755406200 100644 --- a/go/pkg/zerotier/peer.go +++ b/go/pkg/zerotier/peer.go @@ -15,12 +15,13 @@ package zerotier // Peer is another ZeroTier node type Peer struct { - Address Address `json:"address"` - Identity *Identity `json:"identity"` - Fingerprint Fingerprint `json:"fingerprint"` - Version [3]int `json:"version"` - Latency int `json:"latency"` - Root bool `json:"root"` - Bootstrap *InetAddress `json:"bootstrap,omitempty"` - Paths []Path `json:"paths,omitempty"` + Address Address `json:"address"` + Identity *Identity `json:"identity"` + Fingerprint Fingerprint `json:"fingerprint"` + Version [3]int `json:"version"` + Latency int `json:"latency"` + Root bool `json:"root"` + Paths []Path `json:"paths,omitempty"` + LocatorTimestamp int64 `json:"locatorTimestamp"` + LocatorEndpoints []Endpoint `json:"locatorEndpoints,omitempty"` } diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 5b62a8757..ce460e04d 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -153,11 +153,6 @@ extern "C" { */ #define ZT_MAX_PEER_NETWORK_PATHS 16 -/** - * Maximum number of path configurations that can be set - */ -#define ZT_MAX_CONFIGURABLE_PATHS 32 - /** * Maximum number of rules per capability object * @@ -175,15 +170,6 @@ extern "C" { */ #define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4 -/** - * Maximum size in bytes for a root specification - * - * A root specification is just a serialized identity followed by a serialized - * locator. This provides the maximum size of those plus a lot of extra margin - * for any future expansions, but could change in future versions. - */ -#define ZT_ROOT_SPEC_MAX_SIZE 8192 - /** * Packet characteristics flag: packet direction, 1 if inbound 0 if outbound */ @@ -274,32 +260,21 @@ extern "C" { /** * Identity type codes (must be the same as Identity.hpp). */ -enum ZT_Identity_Type +enum ZT_IdentityType { ZT_IDENTITY_TYPE_C25519 = 0, /* C25519/Ed25519 */ ZT_IDENTITY_TYPE_P384 = 1 /* Combined C25519/NIST-P-384 key */ }; /** - * A ZeroTier identity (opaque) + * ZeroTier identity (address plus keys) */ typedef void ZT_Identity; /** - * Full identity fingerprint with address and 384-bit hash of public key(s) + * Locator is a signed list of endpoints */ -typedef struct -{ - /** - * Short address (only least significant 40 bits are used) - */ - uint64_t address; - - /** - * 384-bit hash of identity public key(s) - */ - uint8_t hash[48]; -} ZT_Fingerprint; +typedef void ZT_Locator; /** * Credential type IDs @@ -333,6 +308,27 @@ enum ZT_EndpointType ZT_ENDPOINT_TYPE_IP_HTTP2 = 8 // IP/HTTP2 encapsulation }; +/** + * A string that contains endpoint type IDs indexed by endpoint type (can be used as a lookup array) + */ +#define ZT_ENDPOINT_TYPE_CHAR_INDEX "012345678" + +/** + * Full identity fingerprint with address and 384-bit hash of public key(s) + */ +typedef struct +{ + /** + * Short address (only least significant 40 bits are used) + */ + uint64_t address; + + /** + * 384-bit hash of identity public key(s) + */ + uint8_t hash[48]; +} ZT_Fingerprint; + /** * Flag indicating that VL1 tracing should be generated */ @@ -415,7 +411,7 @@ enum ZT_TraceCredentialRejectionReason }; #define ZT_TRACE_FIELD_TYPE "t" -#define ZT_TRACE_FIELD_CODE_LOCATION "@" +#define ZT_TRACE_FIELD_CODE_LOCATION "c" #define ZT_TRACE_FIELD_ENDPOINT "e" #define ZT_TRACE_FIELD_OLD_ENDPOINT "oe" #define ZT_TRACE_FIELD_NEW_ENDPOINT "ne" @@ -498,7 +494,12 @@ enum ZT_ResultCode /** * The requested operation was given a bad parameter or was called in an invalid state */ - ZT_RESULT_ERROR_BAD_PARAMETER = 1002 + ZT_RESULT_ERROR_BAD_PARAMETER = 1002, + + /** + * A credential or other object was supplied that failed cryptographic signature or integrity check + */ + ZT_RESULT_ERROR_INVALID_CREDENTIAL = 1003 }; /** @@ -1119,14 +1120,57 @@ typedef struct } ZT_InterfaceAddress; /** - * Physical network path to a peer + * Variant type for storing possible path endpoints or peer contact points. */ typedef struct { /** - * Address of endpoint + * Endpoint type, which determines what field in the union 'a' applies. */ - struct sockaddr_storage address; + enum ZT_EndpointType type; + + union { + /** + * Socket address generic buffer + */ + struct sockaddr_storage ss; + + /** + * Socket address header, for all ZT_ENDPOINT_TYPE_IP types + */ + struct sockaddr sa; + + /** + * IPv4 address, for all ZT_ENDPOINT_TYPE_IP types if family is AF_INET + */ + struct sockaddr_in sa_in; + + /** + * IPv6 address, for all ZT_ENDPOINT_TYPE_IP types if family is AF_INET6 + */ + struct sockaddr_in6 sa_in6; + + /** + * MAC address (least significant 48 bites) for ZT_ENDPOINT_TYPE_ETHERNET and other MAC addressed types + */ + uint64_t mac; + + /** + * ZeroTier node address and identity fingerprint for ZT_ENDPOINT_TYPE_ZEROTIER + */ + ZT_Fingerprint fp; + } value; +} ZT_Endpoint; + +/** + * Network path to a peer + */ +typedef struct +{ + /** + * Path endpoint + */ + ZT_Endpoint endpoint; /** * Time of last send in milliseconds or 0 for never @@ -1147,10 +1191,10 @@ typedef struct * Is path preferred? */ int preferred; -} ZT_PeerPhysicalPath; +} ZT_Path; /** - * Peer status result buffer + * Peer information */ typedef struct { @@ -1194,20 +1238,6 @@ typedef struct */ int root; - /** - * Number of bootstrap addresses - */ - unsigned int bootstrapAddressCount; - - /** - * Bootstrap addresses - * - * This is a memo-ized recently valid address that can be saved and used - * to attempt rapid reconnection with this peer. If the ss_family field - * is 0 this field is considered null/empty. - */ - struct sockaddr_storage bootstrap[ZT_MAX_PEER_NETWORK_PATHS]; - /** * Number of networks in which this peer is authenticated */ @@ -1224,9 +1254,28 @@ typedef struct unsigned int pathCount; /** - * Known network paths to peer (array size: pathCount) + * Known network paths to peer (array size: pathCount). + * + * These are direct paths only. Endpoints can also describe indirect paths, + * but those would not appear here. Right now those can only be relaying via + * a root. */ - ZT_PeerPhysicalPath *paths; + ZT_Path *paths; + + /** + * Timestamp of peer's locator or 0 if none on file + */ + int64_t locatorTimestamp; + + /** + * Number of endpoints in locator + */ + unsigned int locatorEndpointCount; + + /** + * Endpoints in peer's locator + */ + ZT_Endpoint *locatorEndpoints; } ZT_Peer; /** @@ -1567,6 +1616,16 @@ ZT_SDK_API void *ZT_getBuffer(); */ ZT_SDK_API void ZT_freeBuffer(void *b); +/** + * Free a query result buffer + * + * Use this to free the return values of listNetworks(), listPeers(), and + * other query functions that return allocated structures or buffers. + * + * @param qr Query result buffer + */ +ZT_SDK_API void ZT_freeQueryResult(void *qr); + /* ---------------------------------------------------------------------------------------------------------------- */ /** @@ -1584,7 +1643,12 @@ ZT_SDK_API void ZT_freeBuffer(void *b); * @param now Current clock in milliseconds * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now); +ZT_SDK_API enum ZT_ResultCode ZT_Node_new( + ZT_Node **node, + void *uptr, + void *tptr, + const struct ZT_Node_Callbacks *callbacks, + int64_t now); /** * Delete a node and free all resources it consumes @@ -1595,7 +1659,9 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,c * @param node Node to delete * @param tptr Thread pointer to pass to functions/callbacks resulting from this call */ -ZT_SDK_API void ZT_Node_delete(ZT_Node *node,void *tptr); +ZT_SDK_API void ZT_Node_delete( + ZT_Node *node, + void *tptr); /** * Process a packet received from the physical wire @@ -1684,7 +1750,12 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks( * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint,void *uptr,void *tptr); +ZT_SDK_API enum ZT_ResultCode ZT_Node_join( + ZT_Node *node, + uint64_t nwid, + const ZT_Fingerprint *controllerFingerprint, + void *uptr, + void *tptr); /** * Leave a network @@ -1702,7 +1773,11 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,const ZT_ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr); +ZT_SDK_API enum ZT_ResultCode ZT_Node_leave( + ZT_Node *node, + uint64_t nwid, + void **uptr, + void *tptr); /** * Subscribe to an Ethernet multicast group @@ -1730,7 +1805,12 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **u * @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed) * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); +ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe( + ZT_Node *node, + void *tptr, + uint64_t nwid, + uint64_t multicastGroup, + unsigned long multicastAdi); /** * Unsubscribe from an Ethernet multicast group (or all groups) @@ -1746,19 +1826,30 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tpt * @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed) * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); +ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe( + ZT_Node *node, + uint64_t nwid, + uint64_t multicastGroup, + unsigned long multicastAdi); /** * Add a root node or update its locator * + * ZeroTier does not take possession of the id or loc objects. The caller + * must still eventually delete them with ZT_Identity_delete() and + * ZT_Locator_delete(). + * * @param node Node instance * @param tptr Thread pointer to pass to functions/callbacks resulting from this call - * @param rdef Root definition (serialized identity and locator) - * @param rdeflen Length of root definition in bytes - * @param address If non-NULL will be filled with the ZeroTier address of the root (only defined if return is OK) - * @return OK (0) or error code if a fatal error condition has occurred + * @param id Identity of root to add + * @param loc Root locator + * @return OK (0) or error code if an error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address); +ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot( + ZT_Node *node, + void *tptr, + const ZT_Identity *id, + const ZT_Locator *loc); /** * Remove a root @@ -1769,10 +1860,13 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const voi * * @param node Node instance * @param tptr Thread pointer to pass to functions/callbacks resulting from this call - * @param fp Fingerprint of root (will be looked up by address only if hash is all zeroes) - * @return OK (0) or error code if a fatal error condition has occurred + * @param address ZeroTier address to remove + * @return OK (0) or error code if an error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,void *tptr,const ZT_Fingerprint *fp); +ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot( + ZT_Node *node, + void *tptr, + const uint64_t address); /** * Get this node's 40-bit ZeroTier address @@ -1799,7 +1893,9 @@ ZT_SDK_API const ZT_Identity *ZT_Node_identity(ZT_Node *node); * @param node Node instance * @param status Buffer to fill with current node status */ -ZT_SDK_API void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status); +ZT_SDK_API void ZT_Node_status( + ZT_Node *node, + ZT_NodeStatus *status); /** * Get a list of known peer nodes @@ -1822,7 +1918,9 @@ ZT_SDK_API ZT_PeerList *ZT_Node_peers(ZT_Node *node); * @param nwid 64-bit network ID * @return Network configuration or NULL if we are not a member of this network */ -ZT_SDK_API ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid); +ZT_SDK_API ZT_VirtualNetworkConfig *ZT_Node_networkConfig( + ZT_Node *node, + uint64_t nwid); /** * Enumerate and get status of all networks @@ -1841,17 +1939,10 @@ ZT_SDK_API ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node); * @param nwid Network ID * @param ptr New network-associated pointer */ -ZT_SDK_API void ZT_Node_setNetworkUserPtr(ZT_Node *node,uint64_t nwid,void *ptr); - -/** - * Free a query result buffer - * - * Use this to free the return values of listNetworks(), listPeers(), etc. - * - * @param node Node instance - * @param qr Query result buffer - */ -ZT_SDK_API void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); +ZT_SDK_API void ZT_Node_setNetworkUserPtr( + ZT_Node *node, + uint64_t nwid, + void *ptr); /** * Set external interface addresses where this node could be reached @@ -1860,7 +1951,10 @@ ZT_SDK_API void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); * @param addrs Addresses * @param addrCount Number of items in addrs[] */ -ZT_SDK_API void ZT_Node_setInterfaceAddresses(ZT_Node *node,const ZT_InterfaceAddress *addrs,unsigned int addrCount); +ZT_SDK_API void ZT_Node_setInterfaceAddresses( + ZT_Node *node, + const ZT_InterfaceAddress *addrs, + unsigned int addrCount); /** * Send a VERB_USER_MESSAGE to another ZeroTier node @@ -1876,7 +1970,13 @@ ZT_SDK_API void ZT_Node_setInterfaceAddresses(ZT_Node *node,const ZT_InterfaceAd * @param len Length of data in bytes * @return Boolean: non-zero on success, zero on failure */ -ZT_SDK_API int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); +ZT_SDK_API int ZT_Node_sendUserMessage( + ZT_Node *node, + void *tptr, + uint64_t dest, + uint64_t typeId, + const void *data, + unsigned int len); /** * Set a network controller instance for this node @@ -1893,7 +1993,9 @@ ZT_SDK_API int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,ui * @param networkConfigMasterInstance Instance of NetworkConfigMaster C++ class or NULL to disable * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterInstance); +ZT_SDK_API void ZT_Node_setController( + ZT_Node *node, + void *networkConfigMasterInstance); /* ---------------------------------------------------------------------------------------------------------------- */ @@ -1907,7 +2009,7 @@ ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterIns * @param type Type of identity to generate * @return New identity or NULL on error */ -ZT_SDK_API ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type); +ZT_SDK_API ZT_Identity *ZT_Identity_new(enum ZT_IdentityType type); /** * Create a new identity object from a string-serialized identity @@ -1938,7 +2040,12 @@ ZT_SDK_API int ZT_Identity_validate(const ZT_Identity *id); * @param signatureBufferLength Length of buffer (must be at least 96 bytes) * @return Length of signature in bytes or 0 on failure. */ -ZT_SDK_API unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength); +ZT_SDK_API unsigned int ZT_Identity_sign( + const ZT_Identity *id, + const void *data, + unsigned int len, + void *signature, + unsigned int signatureBufferLength); /** * Verify a signature @@ -1950,7 +2057,12 @@ ZT_SDK_API unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data, * @param sigLen Length of signature in bytes * @return Non-zero if signature is valid */ -ZT_SDK_API int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen); +ZT_SDK_API int ZT_Identity_verify( + const ZT_Identity *id, + const void *data, + unsigned int len, + const void *signature, + unsigned int sigLen); /** * Get identity type @@ -1958,7 +2070,7 @@ ZT_SDK_API int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigne * @param id Identity to query * @return Identity type code */ -ZT_SDK_API enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id); +ZT_SDK_API enum ZT_IdentityType ZT_Identity_type(const ZT_Identity *id); /** * Convert an identity to its string representation @@ -1969,7 +2081,11 @@ ZT_SDK_API enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id); * @param includePrivate If true include the private key if present * @return Pointer to buf or NULL on overflow or other error */ -ZT_SDK_API char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate); +ZT_SDK_API char *ZT_Identity_toString( + const ZT_Identity *id, + char *buf, + int capacity, + int includePrivate); /** * Check whether this identity object also holds a private key @@ -1995,19 +2111,6 @@ ZT_SDK_API uint64_t ZT_Identity_address(const ZT_Identity *id); */ ZT_SDK_API const ZT_Fingerprint *ZT_Identity_fingerprint(const ZT_Identity *id); -/** - * Make a root specification - * - * @param id Identity to sign root with (must have private key) - * @param ts Timestamp for root specification in milliseconds since epoch - * @param addrs Physical addresses for root - * @param addrcnt Number of physical addresses for root - * @param rootSpecBuf Buffer to receive result, should be at least ZT_ROOT_SPEC_MAX_SIZE bytes - * @param rootSpecBufSize Size of rootSpecBuf in bytes - * @return Bytes written to rootSpecBuf or -1 on error - */ -ZT_SDK_API int ZT_Identity_makeRootSpecification(ZT_Identity *id,int64_t ts,struct sockaddr_storage *addrs,unsigned int addrcnt,void *rootSpecBuf,unsigned int rootSpecBufSize); - /** * Delete an identity and free associated memory * @@ -2020,6 +2123,53 @@ ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id); /* ---------------------------------------------------------------------------------------------------------------- */ +/** + * Create and sign a new locator + * + * @param ts Locator timestamp + * @param endpoints List of endpoints to store in locator + * @param endpointCount Number of endpoints (maximum: 8) + * @param signer Identity to sign locator (must include private key) + * @return Locator or NULL on error (too many endpoints or identity does not have private key) + */ +ZT_SDK_API ZT_Locator *ZT_Locator_create(int64_t ts,const ZT_Endpoint *endpoints,unsigned int endpointCount,const ZT_Identity *signer); + +/** + * Decode a serialized locator + * + * @param data Data to deserialize + * @param len Length of data + * @return Locator or NULL if data is not valid + */ +ZT_SDK_API ZT_Locator *ZT_Locator_unmarshal(const void *data,unsigned int len); + +/** + * Get the number of endpoints in this locator + * + * @param loc Locator to query + * @return Number of endpoints + */ +ZT_SDK_API unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc); + +/** + * Get a pointer to an endpoint in a locator + * + * The returned pointer remains valid as long as the Locator is not deleted. + * + * @param ep Endpoint number from 0 to 1 - endpointCount() + * @return Endpoint or NULL if out of bounds + */ +ZT_SDK_API const ZT_Endpoint *ZT_Locator_endpoint(const unsigned int ep); + +/** + * Delete a locator + * + * @param loc Locator to delete + */ +ZT_SDK_API void ZT_Locator_delete(ZT_Locator *loc); + +/* ---------------------------------------------------------------------------------------------------------------- */ + /** * Get ZeroTier One version * diff --git a/node/Address.hpp b/node/Address.hpp index 06e35a258..c1b5386f3 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -35,7 +35,6 @@ public: explicit ZT_INLINE Address(const uint64_t a) noexcept : _a(a) {} explicit ZT_INLINE Address(const uint8_t b[5]) noexcept : _a(((uint64_t)b[0] << 32U) | ((uint64_t)b[1] << 24U) | ((uint64_t)b[2] << 16U) | ((uint64_t)b[3] << 8U) | (uint64_t)b[4]) {} - ZT_INLINE Address &operator=(const Address &a) noexcept { _a = a._a; return *this; } ZT_INLINE Address &operator=(const uint64_t a) noexcept { _a = a; return *this; } /** @@ -108,6 +107,7 @@ public: ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)_a; } ZT_INLINE operator bool() const noexcept { return (_a != 0); } + ZT_INLINE operator uint64_t() const noexcept { return _a; } ZT_INLINE bool operator==(const Address &a) const noexcept { return _a == a._a; } ZT_INLINE bool operator!=(const Address &a) const noexcept { return _a != a._a; } @@ -116,6 +116,13 @@ public: ZT_INLINE bool operator>=(const Address &a) const noexcept { return _a >= a._a; } ZT_INLINE bool operator<=(const Address &a) const noexcept { return _a <= a._a; } + ZT_INLINE bool operator==(const uint64_t a) const noexcept { return _a == a; } + ZT_INLINE bool operator!=(const uint64_t a) const noexcept { return _a != a; } + ZT_INLINE bool operator>(const uint64_t a) const noexcept { return _a > a; } + ZT_INLINE bool operator<(const uint64_t a) const noexcept { return _a < a; } + ZT_INLINE bool operator>=(const uint64_t a) const noexcept { return _a >= a; } + ZT_INLINE bool operator<=(const uint64_t a) const noexcept { return _a <= a; } + private: uint64_t _a; }; diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index 8fe366a90..e816c4deb 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -15,6 +15,7 @@ set(core_headers Defragmenter.hpp Dictionary.hpp ECC384.hpp + EphemeralKey.hpp Expect.hpp FCV.hpp Fingerprint.hpp diff --git a/node/Containers.hpp b/node/Containers.hpp index 99f92a1f9..da81884bd 100644 --- a/node/Containers.hpp +++ b/node/Containers.hpp @@ -139,6 +139,8 @@ class Vector : public std::vector< V,Utils::Mallocator > { public: ZT_INLINE Vector() {} + template + ZT_INLINE Vector(I begin,I end) : std::vector< V,Utils::Mallocator >(begin,end) {} }; template diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp index 389f54f2c..77bb9856b 100644 --- a/node/Dictionary.cpp +++ b/node/Dictionary.cpp @@ -12,65 +12,71 @@ /****/ #include "Dictionary.hpp" +#include "Identity.hpp" namespace ZeroTier { +static const FCV s_signatureFingerprint("@Si", 4); +static const FCV s_signatureData("@Ss", 4); + Dictionary::Dictionary() { } Vector &Dictionary::operator[](const char *k) { - return m_entries[s_toKey(k)]; + FCV key; + return m_entries[s_key(key, k)]; } const Vector &Dictionary::operator[](const char *k) const { static const Vector s_emptyEntry; - Map< uint64_t,Vector >::const_iterator e(m_entries.find(s_toKey(k))); + FCV key; + SortedMap, Vector >::const_iterator e(m_entries.find(s_key(key, k))); return (e == m_entries.end()) ? s_emptyEntry : e->second; } -void Dictionary::add(const char *k,bool v) +void Dictionary::add(const char *k, bool v) { Vector &e = (*this)[k]; e.resize(2); - e[0] = (uint8_t)(v ? '1' : '0'); + e[0] = (uint8_t) (v ? '1' : '0'); e[1] = 0; } -void Dictionary::add(const char *k,const Address &v) +void Dictionary::add(const char *k, const Address &v) { Vector &e = (*this)[k]; e.resize(ZT_ADDRESS_STRING_SIZE_MAX); - v.toString((char *)e.data()); + v.toString((char *) e.data()); } -void Dictionary::add(const char *k,const char *v) +void Dictionary::add(const char *k, const char *v) { - if ((v)&&(*v)) { + if ((v) && (*v)) { Vector &e = (*this)[k]; e.clear(); while (*v) - e.push_back((uint8_t)*(v++)); + e.push_back((uint8_t) *(v++)); } } -void Dictionary::add(const char *k,const void *data,unsigned int len) +void Dictionary::add(const char *k, const void *data, unsigned int len) { Vector &e = (*this)[k]; if (len != 0) { - e.assign((const uint8_t *)data,(const uint8_t *)data + len); + e.assign((const uint8_t *) data, (const uint8_t *) data + len); } else { e.clear(); } } -bool Dictionary::getB(const char *k,bool dfl) const +bool Dictionary::getB(const char *k, bool dfl) const { const Vector &e = (*this)[k]; if (!e.empty()) { - switch ((char)e[0]) { + switch ((char) e[0]) { case '1': case 't': case 'T': @@ -84,7 +90,7 @@ bool Dictionary::getB(const char *k,bool dfl) const return dfl; } -uint64_t Dictionary::getUI(const char *k,uint64_t dfl) const +uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const { uint8_t tmp[18]; uint64_t v = dfl; @@ -92,29 +98,79 @@ uint64_t Dictionary::getUI(const char *k,uint64_t dfl) const if (!e.empty()) { if (e.back() != 0) { const unsigned long sl = e.size(); - Utils::copy(tmp,e.data(),(sl > 17) ? 17 : sl); + Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl); tmp[17] = 0; - return Utils::unhex((const char *)tmp); + return Utils::unhex((const char *) tmp); } - return Utils::unhex((const char *)e.data()); + return Utils::unhex((const char *) e.data()); } return v; } -void Dictionary::getS(const char *k,char *v,const unsigned int cap) const +char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const { if (cap == 0) // sanity check return; const Vector &e = (*this)[k]; unsigned int i = 0; const unsigned int last = cap - 1; - for(;;) { - if ((i == last)||(i >= (unsigned int)e.size())) + for (;;) { + if ((i == last) || (i >= (unsigned int)e.size())) break; - v[i] = (char)e[i]; + v[i] = (char) e[i]; ++i; } v[i] = 0; + return v; +} + +bool Dictionary::sign(const Identity &signer) +{ + Vector data; + encode(data, true); + uint8_t sig[ZT_SIGNATURE_BUFFER_SIZE]; + const unsigned int siglen = signer.sign(data.data(), (unsigned int) data.size(), sig, ZT_SIGNATURE_BUFFER_SIZE); + if (siglen == 0) + return false; + + uint8_t fp[ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE]; + signer.fingerprint().address().copyTo(fp); + Utils::copy(fp + ZT_ADDRESS_LENGTH, signer.fingerprint().hash()); + + m_entries[s_signatureFingerprint].assign(fp, fp + ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE); + m_entries[s_signatureData].assign(sig, sig + siglen); + + return true; +} + +Fingerprint Dictionary::signer() const +{ + SortedMap, Vector >::const_iterator sigfp(m_entries.find(s_signatureFingerprint)); + Fingerprint fp; + if ((sigfp != m_entries.end()) && (sigfp->second.size() == (ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE))) { + fp.apiFingerprint()->address = Address(sigfp->second.data()).toInt(); + Utils::copy(fp.apiFingerprint()->hash, sigfp->second.data() + ZT_ADDRESS_LENGTH); + } + return fp; +} + +bool Dictionary::verify(const Identity &signer) const +{ + SortedMap< FCV, Vector >::const_iterator sigfp(m_entries.find(s_signatureFingerprint)); + if ( + (sigfp == m_entries.end()) || + (sigfp->second.size() != (ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE)) || + (Address(sigfp->second.data()) != signer.address()) || + (memcmp(sigfp->second.data() + ZT_ADDRESS_LENGTH,signer.fingerprint().hash(),ZT_FINGERPRINT_HASH_SIZE) != 0)) + return false; + + SortedMap< FCV, Vector >::const_iterator sig(m_entries.find(s_signatureData)); + if ((sig == m_entries.end()) || (sig->second.empty())) + return false; + + Vector data; + encode(data, true); + return signer.verify(data.data(),(unsigned int)data.size(),sig->second.data(),(unsigned int)sig->second.size()); } void Dictionary::clear() @@ -122,34 +178,33 @@ void Dictionary::clear() m_entries.clear(); } -void Dictionary::encode(Vector &out) const +void Dictionary::encode(Vector &out, const bool omitSignatureFields) const { - uint64_t str[2] = { 0,0 }; // second uint64_t being 0 means all strings always 0-terminated out.clear(); - for(Map< uint64_t,Vector >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) { - str[0] = ti->first; - s_appendKey(out,reinterpret_cast(str)); - for(Vector::const_iterator i(ti->second.begin());i!=ti->second.end();++i) - s_appendValueByte(out,*i); - out.push_back((uint8_t)'\n'); + for (SortedMap, Vector >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) { + if ((!omitSignatureFields) || ((ti->first != s_signatureFingerprint) && (ti->first != s_signatureData))) { + s_appendKey(out, ti->first.data()); + for (Vector::const_iterator i(ti->second.begin());i != ti->second.end();++i) + s_appendValueByte(out, *i); + out.push_back((uint8_t) '\n'); + } } + out.push_back(0); } -bool Dictionary::decode(const void *data,unsigned int len) +bool Dictionary::decode(const void *data, unsigned int len) { clear(); - - uint64_t k = 0; - unsigned int ki = 0; + FCV k; Vector *v = nullptr; bool escape = false; - for(unsigned int di=0;di(data)[di]; if (!c) break; if (v) { if (escape) { escape = false; - switch(c) { + switch (c) { case 48: v->push_back(0); break; @@ -167,9 +222,8 @@ bool Dictionary::decode(const void *data,unsigned int len) break; } } else { - if (c == (uint8_t)'\n') { - k = 0; - ki = 0; + if (c == (uint8_t) '\n') { + k.clear(); v = nullptr; } else if (c == 92) { // backslash escape = true; @@ -178,16 +232,18 @@ bool Dictionary::decode(const void *data,unsigned int len) } } } else { - if ((c < 33)||(c > 126)||(c == 92)) { + if ((c < 33) || (c > 126) || (c == 92)) { return false; - } else if (c == (uint8_t)'=') { + } else if (c == (uint8_t) '=') { + k.push_back(0); v = &m_entries[k]; + } else if (k.size() < 7) { + k.push_back(c); } else { - reinterpret_cast(&k)[ki & 7U] ^= c; + return false; } } } - return true; } diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 5856badb9..e74e3a2d5 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -18,16 +18,21 @@ #include "Utils.hpp" #include "Address.hpp" #include "Buf.hpp" +#include "FCV.hpp" +#include "SHA512.hpp" +#include "Fingerprint.hpp" #include "Containers.hpp" namespace ZeroTier { +class Identity; + /** * A simple key-value store for short keys * * This data structure is used for network configurations, node meta-data, * and other open-definition protocol objects. It consists of a key-value - * store with short (max: 8 characters) keys that map to strings, blobs, + * store with short (max: 7 characters) keys that map to strings, blobs, * or integers with the latter being by convention in hex format. * * If this seems a little odd, it is. It dates back to the very first alpha @@ -118,7 +123,7 @@ public: * @param v Buffer to hold string * @param cap Maximum size of string (including terminating null) */ - void getS(const char *k,char *v,unsigned int cap) const; + char *getS(const char *k,char *v,unsigned int cap) const; /** * Get an object supporting the marshal/unmarshal interface pattern @@ -133,11 +138,36 @@ public: const Vector &d = (*this)[k]; if (d.empty()) return false; - if (obj.unmarshal(d.data(),(unsigned int)d.size()) <= 0) - return false; - return true; + return (obj.unmarshal(d.data(),(unsigned int)d.size()) > 0); } + /** + * Sign this identity + * + * This adds two fields: + * "@Si" contains the fingerprint (address followed by hash) of the signer + * "@Ss" contains the signature + * + * @param signer Signing identity (must contain secret) + * @return True if signature was successful + */ + bool sign(const Identity &signer); + + /** + * Get the signer's fingerprint for this dictionary or a NIL fingerprint if not signed. + * + * @return Signer + */ + Fingerprint signer() const; + + /** + * Verify this identity's signature + * + * @param signer + * @return + */ + bool verify(const Identity &signer) const; + /** * Erase all entries in dictionary */ @@ -156,12 +186,10 @@ public: /** * Encode to a string in the supplied vector * - * This does not add a terminating zero. This must be pushed afterwords - * if the result is to be handled as a C string. - * * @param out String encoded dictionary + * @param omitSignatureFields If true omit the signature fields from encoding (default: false) */ - void encode(Vector &out) const; + void encode(Vector &out,bool omitSignatureFields = false) const; /** * Decode a string encoded dictionary @@ -347,31 +375,35 @@ private: break; } } + template ZT_INLINE static void s_appendKey(V &out,const char *const k) { - for(unsigned int i=0;i<8;++i) { + for(unsigned int i=0;i<7;++i) { const char kc = k[i]; - if (!kc) break; + if (kc == 0) + break; if ((kc >= 33)&&(kc <= 126)&&(kc != 61)&&(kc != 92)) // printable ASCII with no spaces, equals, or backslash out.push_back((uint8_t)kc); } out.push_back((uint8_t)'='); } - // This just packs up to 8 character bytes into a 64-bit word. There is no need - // for this to be portable in terms of endian-ness. It's just for fast key lookup. - static ZT_INLINE uint64_t s_toKey(const char *k) + ZT_INLINE static FCV &s_key(FCV &buf,const char *k) noexcept { - uint64_t key = 0; - for(int i=0;i<8;++i) { - if ((reinterpret_cast(&key)[i] = *(k++)) == 0) + buf.clear(); + for(unsigned int i=0;i<7;++i) { + const char kc = k[i]; + if (kc == 0) break; + if ((kc >= 33)&&(kc <= 126)&&(kc != 61)&&(kc != 92)) // printable ASCII with no spaces, equals, or backslash + buf.push_back(kc); } - return key; + buf.push_back(0); + return buf; } - Map< uint64_t,Vector > m_entries; + SortedMap< FCV,Vector > m_entries; }; } // namespace ZeroTier diff --git a/node/Endpoint.cpp b/node/Endpoint.cpp index 6590ddb79..0e5241003 100644 --- a/node/Endpoint.cpp +++ b/node/Endpoint.cpp @@ -16,39 +16,128 @@ namespace ZeroTier { +void Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept +{ + static const char *const s_endpointTypeChars = ZT_ENDPOINT_TYPE_CHAR_INDEX; + + static_assert(ZT_ENDPOINT_STRING_SIZE_MAX > (ZT_INETADDRESS_STRING_SIZE_MAX + 4), "overflow"); + static_assert(ZT_ENDPOINT_STRING_SIZE_MAX > (ZT_FINGERPRINT_STRING_SIZE_MAX + 4), "overflow"); + + switch (this->type) { + default: + s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_NIL]; + s[1] = 0; + break; + case ZT_ENDPOINT_TYPE_ZEROTIER: + s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER]; + s[1] = '/'; + zt().toString(s + 2); + break; + case ZT_ENDPOINT_TYPE_ETHERNET: + case ZT_ENDPOINT_TYPE_WIFI_DIRECT: + case ZT_ENDPOINT_TYPE_BLUETOOTH: + s[0] = s_endpointTypeChars[this->type]; + s[1] = '/'; + eth().toString(s + 2); + break; + case ZT_ENDPOINT_TYPE_IP: + case ZT_ENDPOINT_TYPE_IP_UDP: + case ZT_ENDPOINT_TYPE_IP_TCP: + case ZT_ENDPOINT_TYPE_IP_HTTP2: + s[0] = s_endpointTypeChars[this->type]; + s[1] = '/'; + ip().toString(s + 2); + break; + } +} + +bool Endpoint::fromString(const char *s) noexcept +{ + memoryZero(this); + if ((!s) || (!*s)) + return true; + + const char *start = strchr(s, '/'); + if (start) { + char tmp[16]; + for (unsigned int i = 0;i < 15;++i) { + if ((tmp[i] = s[i]) == 0) + break; + } + tmp[15] = 0; + this->type = (ZT_EndpointType)Utils::strToUInt(tmp); + + ++start; + Fingerprint tmpfp; + MAC tmpmac; + switch (this->type) { + case ZT_ENDPOINT_TYPE_NIL: + break; + case ZT_ENDPOINT_TYPE_ZEROTIER: + if (!tmpfp.fromString(start)) + return false; + this->value.fp = tmpfp; + break; + case ZT_ENDPOINT_TYPE_ETHERNET: + case ZT_ENDPOINT_TYPE_WIFI_DIRECT: + case ZT_ENDPOINT_TYPE_BLUETOOTH: + tmpmac.fromString(start); + this->value.mac = tmpmac.toInt(); + break; + case ZT_ENDPOINT_TYPE_IP: + case ZT_ENDPOINT_TYPE_IP_UDP: + case ZT_ENDPOINT_TYPE_IP_TCP: + case ZT_ENDPOINT_TYPE_IP_HTTP2: + if (!asInetAddress(this->value.ss).fromString(start)) + return false; + default: + this->type = ZT_ENDPOINT_TYPE_NIL; + return false; + } + } else { + if (Utils::strToUInt(s) != (unsigned int)ZT_ENDPOINT_TYPE_NIL) + return false; + } + + return true; +} + int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept { - switch(m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1]) { - default: + switch (this->type) { //case ZT_ENDPOINT_TYPE_NIL: - data[0] = 0; + default: + // NIL endpoints get serialized like NIL InetAddress instances. + data[0] = ZT_ENDPOINT_TYPE_NIL; return 1; case ZT_ENDPOINT_TYPE_ZEROTIER: data[0] = 16 + ZT_ENDPOINT_TYPE_ZEROTIER; - reinterpret_cast(m_value)->address().copyTo(data + 1); - Utils::copy(data + 1 + ZT_ADDRESS_LENGTH,reinterpret_cast(m_value)->hash()); + Address(this->value.fp.address).copyTo(data + 1); + Utils::copy(data + 1 + ZT_ADDRESS_LENGTH, this->value.fp.hash); return 1 + ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE; case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_BLUETOOTH: - data[0] = 16 + m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1]; - reinterpret_cast(m_value)->copyTo(data + 1); + data[0] = 16 + (uint8_t)this->type; + MAC(this->value.mac).copyTo(data + 1); return 7; case ZT_ENDPOINT_TYPE_IP_UDP: - return reinterpret_cast(m_value)->marshal(data); + // Default UDP mode gets serialized to look exactly like an InetAddress. + return asInetAddress(this->value.ss).marshal(data); case ZT_ENDPOINT_TYPE_IP: case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_HTTP2: - data[0] = 16 + m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1]; - return 1 + reinterpret_cast(m_value)->marshal(data + 1); + // Other IP types get serialized as new version Endpoint instances with type. + data[0] = 16 + (uint8_t)this->type; + return 1 + asInetAddress(this->value.ss).marshal(data + 1); } } -int Endpoint::unmarshal(const uint8_t *restrict data,int len) noexcept +int Endpoint::unmarshal(const uint8_t *restrict data, int len) noexcept { memoryZero(this); if (unlikely(len <= 0)) @@ -59,25 +148,25 @@ int Endpoint::unmarshal(const uint8_t *restrict data,int len) noexcept // This allows backward compatibility with old endpoint fields in the // protocol that were serialized InetAddress instances. if (data[0] < 16) { - switch(data[0]) { + switch (data[0]) { case 0: return 1; case 4: case 6: - m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (uint8_t)ZT_ENDPOINT_TYPE_IP_UDP; - return reinterpret_cast(m_value)->unmarshal(data,len); + this->type = ZT_ENDPOINT_TYPE_IP_UDP; + return asInetAddress(this->value.ss).unmarshal(data, len); } return -1; } - switch((m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (data[0] - 16))) { + switch ((this->type = (ZT_EndpointType)(data[0] - 16))) { case ZT_ENDPOINT_TYPE_NIL: return 1; case ZT_ENDPOINT_TYPE_ZEROTIER: if (len >= (1 + ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE)) { - reinterpret_cast(m_value)->apiFingerprint()->address = Address(data + 1).toInt(); - Utils::copy(reinterpret_cast(m_value)->apiFingerprint()->hash,data + 1 + ZT_ADDRESS_LENGTH); + this->value.fp.address = Address(data + 1).toInt(); + Utils::copy(this->value.fp.hash, data + 1 + ZT_ADDRESS_LENGTH); return 1 + ZT_ADDRESS_LENGTH + ZT_FINGERPRINT_HASH_SIZE; } return -1; @@ -86,7 +175,9 @@ int Endpoint::unmarshal(const uint8_t *restrict data,int len) noexcept case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_BLUETOOTH: if (len >= 7) { - reinterpret_cast(m_value)->setTo(data + 1); + MAC tmp; + tmp.setTo(data + 1); + this->value.mac = tmp.toInt(); return 7; } return -1; @@ -95,7 +186,7 @@ int Endpoint::unmarshal(const uint8_t *restrict data,int len) noexcept case ZT_ENDPOINT_TYPE_IP_UDP: case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_HTTP2: - return reinterpret_cast(m_value)->unmarshal(data + 1,len - 1); + return asInetAddress(this->value.ss).unmarshal(data + 1, len - 1); default: break; @@ -104,11 +195,55 @@ int Endpoint::unmarshal(const uint8_t *restrict data,int len) noexcept // Unrecognized types can still be passed over in a valid stream if they are // prefixed by a 16-bit size. This allows forward compatibility with future // endpoint types. - m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (uint8_t)ZT_ENDPOINT_TYPE_NIL; + this->type = ZT_ENDPOINT_TYPE_NIL; if (len < 3) return -1; - const int unrecLen = 1 + (int)Utils::loadBigEndian(data + 1); + const int unrecLen = 1 + (int) Utils::loadBigEndian(data + 1); return (unrecLen > len) ? -1 : unrecLen; } +bool Endpoint::operator==(const Endpoint &ep) const noexcept +{ + if (this->type == ep.type) { + switch(this->type) { + case ZT_ENDPOINT_TYPE_ZEROTIER: + return zt() == ep.zt(); + case ZT_ENDPOINT_TYPE_ETHERNET: + case ZT_ENDPOINT_TYPE_WIFI_DIRECT: + case ZT_ENDPOINT_TYPE_BLUETOOTH: + return this->value.mac == ep.value.mac; + case ZT_ENDPOINT_TYPE_IP: + case ZT_ENDPOINT_TYPE_IP_UDP: + case ZT_ENDPOINT_TYPE_IP_TCP: + case ZT_ENDPOINT_TYPE_IP_HTTP2: + return ip() == ep.ip(); + default: + return true; + } + } + return false; +} + +bool Endpoint::operator<(const Endpoint &ep) const noexcept +{ + if (this->type == ep.type) { + switch(this->type) { + case ZT_ENDPOINT_TYPE_ZEROTIER: + return zt() < ep.zt(); + case ZT_ENDPOINT_TYPE_ETHERNET: + case ZT_ENDPOINT_TYPE_WIFI_DIRECT: + case ZT_ENDPOINT_TYPE_BLUETOOTH: + return this->value.mac < ep.value.mac; + case ZT_ENDPOINT_TYPE_IP: + case ZT_ENDPOINT_TYPE_IP_UDP: + case ZT_ENDPOINT_TYPE_IP_TCP: + case ZT_ENDPOINT_TYPE_IP_HTTP2: + return ip() < ep.ip(); + default: + return true; + } + } + return (int)this->type < (int)ep.type; +} + } // namespace ZeroTier diff --git a/node/Endpoint.hpp b/node/Endpoint.hpp index 6f2f0412d..9d395c523 100644 --- a/node/Endpoint.hpp +++ b/node/Endpoint.hpp @@ -22,13 +22,17 @@ #include "Fingerprint.hpp" #include "MAC.hpp" -#define ZT_ENDPOINT_MARSHAL_SIZE_MAX 128 - -static_assert(ZT_ENDPOINT_MARSHAL_SIZE_MAX > (ZT_INETADDRESS_MARSHAL_SIZE_MAX + 1),"ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); -static_assert(ZT_ENDPOINT_MARSHAL_SIZE_MAX > (sizeof(ZT_Fingerprint) + 1),"ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); +#define ZT_ENDPOINT_STRING_SIZE_MAX 256 +#define ZT_ENDPOINT_MARSHAL_SIZE_MAX 192 namespace ZeroTier { +static_assert((ZT_ENDPOINT_MARSHAL_SIZE_MAX - 1) > ZT_INETADDRESS_MARSHAL_SIZE_MAX, "ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); +static_assert((ZT_ENDPOINT_MARSHAL_SIZE_MAX - 1) > sizeof(ZT_Fingerprint), "ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); +static_assert((ZT_ENDPOINT_MARSHAL_SIZE_MAX - 1) > sizeof(InetAddress), "ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); +static_assert((ZT_ENDPOINT_MARSHAL_SIZE_MAX - 1) > sizeof(MAC), "ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); +static_assert((ZT_ENDPOINT_MARSHAL_SIZE_MAX - 1) > sizeof(Fingerprint), "ZT_ENDPOINT_MARSHAL_SIZE_MAX not large enough"); + /** * Endpoint variant specifying some form of network endpoint. * @@ -38,18 +42,17 @@ namespace ZeroTier { * where InetAddress was used as long as only the UDP type is exchanged * with those nodes. */ -class Endpoint : public TriviallyCopyable +class Endpoint : public ZT_Endpoint, public TriviallyCopyable { public: - /** - * Endpoint type (defined in the API) - */ - typedef ZT_EndpointType Type; - /** * Create a NIL/empty endpoint */ - ZT_INLINE Endpoint() noexcept { memoryZero(this); } + ZT_INLINE Endpoint() noexcept + { memoryZero(this); } + + ZT_INLINE Endpoint(const ZT_Endpoint &ep) noexcept + { *this = ep; } /** * Create an endpoint for a type that uses an IP @@ -57,11 +60,11 @@ public: * @param a IP/port * @param et Endpoint type (default: IP_UDP) */ - ZT_INLINE Endpoint(const InetAddress &a,const Type et = ZT_ENDPOINT_TYPE_IP_UDP) noexcept + ZT_INLINE Endpoint(const InetAddress &inaddr, const ZT_EndpointType et = ZT_ENDPOINT_TYPE_IP_UDP) noexcept { - if (a) { - Utils::copy(m_value,&a); - m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (uint8_t)et; + if (inaddr) { + this->type = et; + Utils::copy(&(this->value.ss), &(inaddr.as.ss)); } else { memoryZero(this); } @@ -75,8 +78,8 @@ public: ZT_INLINE Endpoint(const Fingerprint &zt_) noexcept { if (zt_) { - Utils::copy(m_value,&zt_); - m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (uint8_t)ZT_ENDPOINT_TYPE_ZEROTIER; + this->type = ZT_ENDPOINT_TYPE_ZEROTIER; + this->value.fp = zt_; } else { memoryZero(this); } @@ -88,32 +91,28 @@ public: * @param eth_ Ethernet address * @param et Endpoint type (default: ETHERNET) */ - ZT_INLINE Endpoint(const MAC ð_,const Type et = ZT_ENDPOINT_TYPE_ETHERNET) noexcept + ZT_INLINE Endpoint(const MAC ð_, const ZT_EndpointType et = ZT_ENDPOINT_TYPE_ETHERNET) noexcept { if (eth_) { - Utils::copy(m_value,ð_); - m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] = (uint8_t)et; + this->type = et; + this->value.mac = eth_.toInt(); } else { memoryZero(this); } } - /** - * @return Endpoint type - */ - ZT_INLINE Type type() const noexcept { return (Type)m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1]; } - /** * @return True if endpoint type isn't NIL */ - ZT_INLINE operator bool() const noexcept { return (m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX-1] != (uint8_t)ZT_ENDPOINT_TYPE_NIL); } + ZT_INLINE operator bool() const noexcept + { return this->type != ZT_ENDPOINT_TYPE_NIL; } /** * @return True if this endpoint type has an InetAddress address type and thus ip() is valid */ ZT_INLINE bool isInetAddr() const noexcept { - switch(this->type()) { + switch (this->type) { case ZT_ENDPOINT_TYPE_IP: case ZT_ENDPOINT_TYPE_IP_UDP: case ZT_ENDPOINT_TYPE_IP_TCP: @@ -129,28 +128,58 @@ public: * * @return InetAddress instance */ - ZT_INLINE const InetAddress &ip() const noexcept { return *reinterpret_cast(m_value); } + ZT_INLINE const InetAddress &ip() const noexcept + { return asInetAddress(this->value.ss); } /** * Get MAC if this is an Ethernet, WiFi direct, or Bluetooth type (undefined otherwise) * * @return Ethernet MAC */ - ZT_INLINE const MAC ð() const noexcept { return *reinterpret_cast(m_value); } + ZT_INLINE MAC eth() const noexcept + { return MAC(this->value.mac); } /** * Get fingerprint if this is a ZeroTier endpoint type (undefined otherwise) * * @return ZeroTier fingerprint */ - ZT_INLINE const Fingerprint &zt() const noexcept { return *reinterpret_cast(m_value); } + ZT_INLINE Fingerprint zt() const noexcept + { return Fingerprint(this->value.fp); } + + void toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept; + + ZT_INLINE String toString() const + { + char tmp[ZT_ENDPOINT_STRING_SIZE_MAX]; + toString(tmp); + return String(tmp); + } + + bool fromString(const char *s) noexcept; + + static constexpr int marshalSizeMax() noexcept + { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; } - static constexpr int marshalSizeMax() noexcept { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; } int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept; - int unmarshal(const uint8_t *restrict data,int len) noexcept; -private: - uint8_t m_value[ZT_ENDPOINT_MARSHAL_SIZE_MAX]; // the last byte in this buffer is the type + int unmarshal(const uint8_t *restrict data, int len) noexcept; + + bool operator==(const Endpoint &ep) const noexcept; + + ZT_INLINE bool operator!=(const Endpoint &ep) const noexcept + { return !((*this) == ep); } + + bool operator<(const Endpoint &ep) const noexcept; + + ZT_INLINE bool operator>(const Endpoint &ep) const noexcept + { return (ep < *this); } + + ZT_INLINE bool operator<=(const Endpoint &ep) const noexcept + { return !(ep < *this); } + + ZT_INLINE bool operator>=(const Endpoint &ep) const noexcept + { return !(*this < ep); } }; } // namespace ZeroTier diff --git a/node/FCV.hpp b/node/FCV.hpp index 4fe9d615f..ad99822b7 100644 --- a/node/FCV.hpp +++ b/node/FCV.hpp @@ -31,18 +31,28 @@ namespace ZeroTier { * @tparam T Type to contain * @tparam C Maximum capacity of vector */ -template +template class FCV { public: - typedef T * iterator; - typedef const T * const_iterator; + typedef T *iterator; + typedef const T *const_iterator; - ZT_INLINE FCV() noexcept : _s(0) {} - ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; } + ZT_INLINE FCV() noexcept: _s(0) + {} + + ZT_INLINE FCV(const FCV &v) : _s(0) + { *this = v; } + + ZT_INLINE FCV(const T *const contents, const unsigned int len) : + _s(0) + { + for (unsigned int i = 0;i < len;++i) + push_back(contents[i]); + } template - ZT_INLINE FCV(I i,I end) : + ZT_INLINE FCV(I i, I end) : _s(0) { while (i != end) { @@ -51,7 +61,8 @@ public: } } - ZT_INLINE ~FCV() { this->clear(); } + ZT_INLINE ~FCV() + { this->clear(); } ZT_INLINE FCV &operator=(const FCV &v) { @@ -59,7 +70,7 @@ public: this->clear(); const unsigned int s = v._s; _s = s; - for (unsigned int i=0;i(_m) + i) T(*(reinterpret_cast(v._m) + i)); } return *this; @@ -72,7 +83,7 @@ public: { const unsigned int s = _s; _s = 0; - for(unsigned int i=0;i(_m) + i)->~T(); } @@ -83,14 +94,21 @@ public: */ ZT_INLINE void unsafeMoveTo(FCV &v) noexcept { - Utils::copy(v._m,_m,(v._s = _s) * sizeof(T)); + Utils::copy(v._m, _m, (v._s = _s) * sizeof(T)); _s = 0; } - ZT_INLINE iterator begin() 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 iterator begin() 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) { @@ -98,6 +116,7 @@ public: 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)) @@ -105,12 +124,20 @@ public: 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); } + static constexpr unsigned int capacity() noexcept + { return C; } - ZT_INLINE T *data() noexcept { return reinterpret_cast(_m); } - ZT_INLINE const T *data() const noexcept { return reinterpret_cast(_m); } + 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); } /** * Push a value onto the back of this vector @@ -122,7 +149,7 @@ public: ZT_INLINE void push_back(const T &v) { if (likely(_s < C)) - new (reinterpret_cast(_m) + _s++) T(v); + new(reinterpret_cast(_m) + _s++) T(v); else throw std::out_of_range("capacity exceeded"); } @@ -182,6 +209,14 @@ public: _s = s; } + /** + * Set the size of this vector without otherwise changing anything + * + * @param ns New size + */ + ZT_INLINE void unsafeSetSize(unsigned int ns) + { _s = ns; } + /** * This is a bounds checked auto-resizing variant of the [] operator * @@ -213,12 +248,12 @@ public: * @param end Ending iterator (must be greater than start) */ template - ZT_INLINE void assign(X start,const X &end) + ZT_INLINE void assign(X start, const X &end) { - const int l = std::min((int)std::distance(start,end),(int)C); + const int l = std::min((int) std::distance(start, end), (int) C); if (l > 0) { - this->resize((unsigned int)l); - for(int i=0;iresize((unsigned int) l); + for (int i = 0;i < l;++i) reinterpret_cast(_m)[i] = *(start++); } else { this->clear(); @@ -228,7 +263,7 @@ public: ZT_INLINE bool operator==(const FCV &v) const noexcept { if (_s == v._s) { - for(unsigned int i=0;i<_s;++i) { + for (unsigned int i = 0;i < _s;++i) { if (!(*(reinterpret_cast(_m) + i) == *(reinterpret_cast(v._m) + i))) return false; } @@ -236,11 +271,21 @@ public: } return false; } - ZT_INLINE bool operator!=(const FCV &v) const noexcept { return (!(*this == v)); } - ZT_INLINE bool operator<(const FCV &v) const noexcept { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); } - ZT_INLINE bool operator>(const FCV &v) const noexcept { return (v < *this); } - ZT_INLINE bool operator<=(const FCV &v) const noexcept { return !(v < *this); } - ZT_INLINE bool operator>=(const FCV &v) const noexcept { return !(*this < v); } + + ZT_INLINE bool operator!=(const FCV &v) const noexcept + { return (!(*this == v)); } + + ZT_INLINE bool operator<(const FCV &v) const noexcept + { return std::lexicographical_compare(begin(), end(), v.begin(), v.end()); } + + ZT_INLINE bool operator>(const FCV &v) const noexcept + { return (v < *this); } + + ZT_INLINE bool operator<=(const FCV &v) const noexcept + { return !(v < *this); } + + ZT_INLINE bool operator>=(const FCV &v) const noexcept + { return !(*this < v); } private: #ifdef _MSC_VER diff --git a/node/Fingerprint.hpp b/node/Fingerprint.hpp index d60fa1e9d..7790a26c7 100644 --- a/node/Fingerprint.hpp +++ b/node/Fingerprint.hpp @@ -19,59 +19,43 @@ #include "Address.hpp" #include "Utils.hpp" -#include - -#define ZT_FINGERPRINT_STRING_BUFFER_LENGTH 96 +#define ZT_FINGERPRINT_STRING_SIZE_MAX 128 namespace ZeroTier { -class Identity; - /** * Address and full hash of an identity's public keys. * * This is the same size as ZT_Fingerprint and should be cast-able back and forth. * This is checked in Tests.cpp. */ -class Fingerprint : public TriviallyCopyable +class Fingerprint : public ZT_Fingerprint, public TriviallyCopyable { - friend class Identity; - public: - /** - * Create an empty/nil fingerprint - */ - ZT_INLINE Fingerprint() noexcept { memoryZero(this); } + ZT_INLINE Fingerprint() noexcept + { memoryZero(this); } - /** - * Create a Fingerprint that is a copy of the external API companion structure - * - * @param apifp API fingerprint - */ - ZT_INLINE Fingerprint(const ZT_Fingerprint &apifp) noexcept { Utils::copy(&m_cfp,&apifp); } - - ZT_INLINE Address address() const noexcept { return Address(m_cfp.address); } - ZT_INLINE const uint8_t *hash() const noexcept { return m_cfp.hash; } - ZT_INLINE ZT_Fingerprint *apiFingerprint() noexcept { return &m_cfp; } - ZT_INLINE const ZT_Fingerprint *apiFingerprint() const noexcept { return &m_cfp; } + ZT_INLINE Fingerprint(const ZT_Fingerprint &fp) noexcept + { Utils::copy(this, &fp); } /** * @return True if hash is not all zero (missing/unspecified) */ - ZT_INLINE bool haveHash() const noexcept { return (!Utils::allZero(m_cfp.hash, sizeof(m_cfp.hash))); } + ZT_INLINE bool haveHash() const noexcept + { return (!Utils::allZero(this->hash, ZT_FINGERPRINT_HASH_SIZE)); } /** * Get a base32-encoded representation of this fingerprint * * @param s Base32 string */ - ZT_INLINE void toString(char s[ZT_FINGERPRINT_STRING_BUFFER_LENGTH]) const noexcept + ZT_INLINE void toString(char s[ZT_FINGERPRINT_STRING_SIZE_MAX]) const noexcept { - uint8_t tmp[48 + 5]; - address().copyTo(tmp); - Utils::copy<48>(tmp + 5, m_cfp.hash); - Utils::b32e(tmp,sizeof(tmp),s,ZT_FINGERPRINT_STRING_BUFFER_LENGTH); - s[ZT_FINGERPRINT_STRING_BUFFER_LENGTH-1] = 0; // sanity check, ensure always zero terminated + Address(this->address).toString(s); + if (haveHash()) { + s[ZT_ADDRESS_LENGTH_HEX] = '/'; + Utils::b32e(this->hash, ZT_FINGERPRINT_HASH_SIZE, s + (ZT_ADDRESS_LENGTH_HEX + 1), ZT_FINGERPRINT_STRING_SIZE_MAX - (ZT_ADDRESS_LENGTH_HEX + 1)); + } } /** @@ -80,32 +64,56 @@ public: * @param s String to decode * @return True if string appears to be valid and of the proper length (no other checking is done) */ - ZT_INLINE bool fromString(const char *s) noexcept + ZT_INLINE bool fromString(const char *const s) noexcept { - uint8_t tmp[48 + 5]; - if (Utils::b32d(s,tmp,sizeof(tmp)) != sizeof(tmp)) + if (!s) return false; - m_cfp.address = Address(tmp).toInt(); - Utils::copy<48>(m_cfp.hash, tmp + 5); + const int l = (int) strlen(s); + if (l < ZT_ADDRESS_LENGTH_HEX) + return false; + char a[ZT_ADDRESS_LENGTH_HEX + 1]; + Utils::copy(a, s); + a[ZT_ADDRESS_LENGTH_HEX] = 0; + this->address = Utils::hexStrToU64(a) & ZT_ADDRESS_MASK; + if (l > (ZT_ADDRESS_LENGTH_HEX + 1)) { + if (Utils::b32d(s + (ZT_ADDRESS_LENGTH_HEX + 1), this->hash, ZT_FINGERPRINT_HASH_SIZE) != ZT_FINGERPRINT_HASH_SIZE) + return false; + } else { + Utils::zero(this->hash); + } return true; } - ZT_INLINE void zero() noexcept { memoryZero(this); } - ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)m_cfp.address; } + ZT_INLINE void zero() noexcept + { memoryZero(this); } - ZT_INLINE operator bool() const noexcept { return (m_cfp.address != 0); } + ZT_INLINE unsigned long hashCode() const noexcept + { return (unsigned long)this->address; } - ZT_INLINE bool operator==(const Fingerprint &h) const noexcept { return ((m_cfp.address == h.m_cfp.address) && (memcmp(m_cfp.hash, h.m_cfp.hash, ZT_FINGERPRINT_HASH_SIZE) == 0)); } - ZT_INLINE bool operator!=(const Fingerprint &h) const noexcept { return !(*this == h); } - ZT_INLINE bool operator<(const Fingerprint &h) const noexcept { return ((m_cfp.address < h.m_cfp.address) || ((m_cfp.address == h.m_cfp.address) && (memcmp(m_cfp.hash, h.m_cfp.hash, ZT_FINGERPRINT_HASH_SIZE) < 0))); } - ZT_INLINE bool operator>(const Fingerprint &h) const noexcept { return (h < *this); } - ZT_INLINE bool operator<=(const Fingerprint &h) const noexcept { return !(h < *this); } - ZT_INLINE bool operator>=(const Fingerprint &h) const noexcept { return !(*this < h); } + ZT_INLINE operator bool() const noexcept + { return this->address != 0; } -private: - ZT_Fingerprint m_cfp; + ZT_INLINE bool operator==(const Fingerprint &h) const noexcept + { return ((this->address == h.address) && (memcmp(this->hash, h.hash, ZT_FINGERPRINT_HASH_SIZE) == 0)); } + + ZT_INLINE bool operator!=(const Fingerprint &h) const noexcept + { return !(*this == h); } + + ZT_INLINE bool operator<(const Fingerprint &h) const noexcept + { return ((this->address < h.address) || ((this->address == h.address) && (memcmp(this->hash, h.hash, ZT_FINGERPRINT_HASH_SIZE) < 0))); } + + ZT_INLINE bool operator>(const Fingerprint &h) const noexcept + { return (h < *this); } + + ZT_INLINE bool operator<=(const Fingerprint &h) const noexcept + { return !(h < *this); } + + ZT_INLINE bool operator>=(const Fingerprint &h) const noexcept + { return !(*this < h); } }; +static_assert(sizeof(Fingerprint) == sizeof(ZT_Fingerprint), "size mismatch"); + } // namespace ZeroTier #endif diff --git a/node/Identity.cpp b/node/Identity.cpp index 97e759fe2..88c67a142 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -157,7 +157,7 @@ bool Identity::generate(const Type t) address.setTo(digest + 59); } while (address.isReserved()); delete[] genmem; - m_fp.m_cfp.address = address.toInt(); // address comes from PoW hash for type 0 identities + m_fp.address = address; // address comes from PoW hash for type 0 identities m_computeHash(); } break; @@ -179,9 +179,11 @@ bool Identity::generate(const Type t) // If we passed PoW then check that the address is valid, otherwise loop // back around and run the whole process again. m_computeHash(); - m_fp.m_cfp.address = Address(m_fp.m_cfp.hash).toInt(); - if (!m_fp.address().isReserved()) + const Address addr(m_fp.hash); + if (!addr.isReserved()) { + m_fp.address = addr; break; + } } } break; @@ -195,7 +197,7 @@ bool Identity::generate(const Type t) bool Identity::locallyValidate() const noexcept { try { - if ((m_fp) && ((!m_fp.address().isReserved()))) { + if ((m_fp) && ((!Address(m_fp.address).isReserved()))) { switch (m_type) { case C25519: { uint8_t digest[64]; @@ -204,10 +206,10 @@ bool Identity::locallyValidate() const noexcept return false; identityV0ProofOfWorkFrankenhash(m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, digest, genmem); free(genmem); - return ((m_fp.address() == Address(digest + 59)) && (digest[0] < 17)); + return ((Address(digest + 59) == m_fp.address) && (digest[0] < 17)); } case P384: { - if (m_fp.address() != Address(m_fp.hash())) + if (Address(m_fp.hash) != m_fp.address) return false; return identityV1ProofOfWorkCriteria(m_pub, sizeof(m_pub)); } @@ -312,7 +314,7 @@ bool Identity::agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) con char *Identity::toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const { char *p = buf; - m_fp.address().toString(p); + Address(m_fp.address).toString(p); p += 10; *(p++) = ':'; @@ -363,8 +365,8 @@ bool Identity::fromString(const char *str) switch (fno++) { case 0: - m_fp.m_cfp.address = Utils::hexStrToU64(f) & ZT_ADDRESS_MASK; - if (m_fp.address().isReserved()) + m_fp.address = Utils::hexStrToU64(f) & ZT_ADDRESS_MASK; + if (Address(m_fp.address).isReserved()) return false; break; @@ -425,12 +427,12 @@ bool Identity::fromString(const char *str) return false; m_computeHash(); - return !((m_type == P384) && (m_fp.address() != Address(m_fp.hash()))); + return !((m_type == P384) && (Address(m_fp.hash) != m_fp.address)); } int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool includePrivate) const noexcept { - m_fp.address().copyTo(data); + Address(m_fp.address).copyTo(data); switch (m_type) { case C25519: @@ -467,7 +469,7 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept if (len < (1 + ZT_ADDRESS_LENGTH)) return -1; - m_fp.m_cfp.address = Address(data).toInt(); + m_fp.address = Address(data); unsigned int privlen; switch ((m_type = (Type) data[ZT_ADDRESS_LENGTH])) { @@ -498,7 +500,7 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept Utils::copy(m_pub, data + ZT_ADDRESS_LENGTH + 1); m_computeHash(); // this sets the address for P384 - if (m_fp.address() != Address(m_fp.hash())) // this sanity check is possible with V1 identities + if (Address(m_fp.hash) != m_fp.address) // this sanity check is possible with V1 identities return -1; privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; @@ -538,12 +540,12 @@ void Identity::m_computeHash() extern "C" { -ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type) +ZT_Identity *ZT_Identity_new(enum ZT_IdentityType type) { if ((type != ZT_IDENTITY_TYPE_C25519) && (type != ZT_IDENTITY_TYPE_P384)) return nullptr; try { - ZeroTier::Identity *const id = new ZeroTier::Identity(); // NOLINT(hicpp-use-auto,modernize-use-auto) + ZeroTier::Identity *const id = new ZeroTier::Identity(); id->generate((ZeroTier::Identity::Type) type); return reinterpret_cast(id); } catch (...) { @@ -556,7 +558,7 @@ ZT_Identity *ZT_Identity_fromString(const char *idStr) if (!idStr) return nullptr; try { - ZeroTier::Identity *const id = new ZeroTier::Identity(); // NOLINT(hicpp-use-auto,modernize-use-auto) + ZeroTier::Identity *const id = new ZeroTier::Identity(); if (!id->fromString(idStr)) { delete id; return nullptr; @@ -590,11 +592,11 @@ int ZT_Identity_verify(const ZT_Identity *id, const void *data, unsigned int len return reinterpret_cast(id)->verify(data, len, signature, sigLen) ? 1 : 0; } -enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id) +enum ZT_IdentityType ZT_Identity_type(const ZT_Identity *id) { if (!id) - return (ZT_Identity_Type) 0; - return (enum ZT_Identity_Type) reinterpret_cast(id)->type(); + return (ZT_IdentityType) 0; + return (enum ZT_IdentityType) reinterpret_cast(id)->type(); } char *ZT_Identity_toString(const ZT_Identity *id, char *buf, int capacity, int includePrivate) @@ -616,25 +618,14 @@ uint64_t ZT_Identity_address(const ZT_Identity *id) { if (!id) return 0; - return reinterpret_cast(id)->address().toInt(); + return reinterpret_cast(id)->address(); } const ZT_Fingerprint *ZT_Identity_fingerprint(const ZT_Identity *id) { if (!id) return nullptr; - return reinterpret_cast(id)->fingerprint().apiFingerprint(); -} - -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),ts,endpoints,rootSpecBuf,rootSpecBufSize); + return &(reinterpret_cast(id)->fingerprint()); } ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id) diff --git a/node/Identity.hpp b/node/Identity.hpp index f4a2fc02d..9e4acc8bf 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -53,8 +53,8 @@ public: */ enum Type { - C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default) - P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519/Ed25519 secondaries (2.x+) + C25519 = ZT_IDENTITY_TYPE_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default) + P384 = ZT_IDENTITY_TYPE_P384 // Type 1 -- NIST P-384 with linked Curve25519/Ed25519 secondaries (2.x+) }; /** @@ -62,7 +62,7 @@ public: */ static const Identity NIL; - ZT_INLINE Identity() noexcept // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE Identity() noexcept { Utils::memoryLock(this,sizeof(Identity)); memoryZero(this); @@ -76,7 +76,7 @@ public: * * @param str Identity in canonical string format */ - explicit ZT_INLINE Identity(const char *str) // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + explicit ZT_INLINE Identity(const char *str) { Utils::memoryLock(this,sizeof(Identity)); fromString(str); diff --git a/node/Locator.cpp b/node/Locator.cpp index cc6144368..63cbb9932 100644 --- a/node/Locator.cpp +++ b/node/Locator.cpp @@ -13,145 +13,99 @@ #include "Locator.hpp" +#include + namespace ZeroTier { +bool Locator::add(const Endpoint &ep) +{ + if (m_endpoints.size() < ZT_LOCATOR_MAX_ENDPOINTS) { + if (std::find(m_endpoints.begin(), m_endpoints.end(), ep) == m_endpoints.end()) + m_endpoints.push_back(ep); + return true; + } + return false; +} + bool Locator::sign(const int64_t ts, const Identity &id) noexcept { - uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX]; - if (!id.hasPrivate()) - return false; m_ts = ts; - if (m_endpointCount > 0) - std::sort(m_at, m_at + m_endpointCount); - const unsigned int signLen = marshal(signData, true); - m_signatureLength = id.sign(signData, signLen, m_signature, sizeof(m_signature)); - return (m_signatureLength > 0); + std::sort(m_endpoints.begin(), m_endpoints.end()); + + uint8_t signdata[ZT_LOCATOR_MARSHAL_SIZE_MAX]; + const unsigned int signlen = marshal(signdata, true); + + const unsigned int siglen = id.sign(signdata, signlen, m_signature.data(), m_signature.capacity()); + if (siglen == 0) + return false; + m_signature.unsafeSetSize(siglen); + + return true; } bool Locator::verify(const Identity &id) const noexcept { - if ((m_ts == 0) || (m_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS) || (m_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)) + if (m_ts <= 0) return false; - uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX]; - const unsigned int signLen = marshal(signData, true); - return id.verify(signData, signLen, m_signature, m_signatureLength); + uint8_t signdata[ZT_LOCATOR_MARSHAL_SIZE_MAX]; + const unsigned int signlen = marshal(signdata, true); + return id.verify(signdata, signlen, m_signature.data(), m_signature.size()); } int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], const bool excludeSignature) const noexcept { - if ((m_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS) || (m_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)) - return -1; - - data[0] = 0xff; // version byte, currently 0xff to never be the same as byte 0 of an identity for legacy compatibility reasons - Utils::storeBigEndian(data + 1, m_ts); - int p = 9; - - if (m_ts > 0) { - Utils::storeBigEndian(data + p, (uint16_t) m_endpointCount); - p += 2; - for (unsigned int i = 0;i < m_endpointCount;++i) { - int tmp = m_at[i].marshal(data + p); - if (tmp < 0) - return -1; - p += tmp; - } - - if (!excludeSignature) { - Utils::storeBigEndian(data + p, (uint16_t) m_signatureLength); - p += 2; - Utils::copy(data + p, m_signature, m_signatureLength); - p += (int) m_signatureLength; - } - - Utils::storeBigEndian(data + p, m_flags); - p += 2; + Utils::storeBigEndian(data, (uint64_t) m_ts); + Utils::storeBigEndian(data + 8, (uint16_t) m_endpoints.size()); + int p = 10; + for (Vector::const_iterator e(m_endpoints.begin());e != m_endpoints.end();++e) { + int l = e->marshal(data + p); + if (l <= 0) + return -1; + p += l; + } + Utils::storeAsIsEndian(data + p, 0); // length of meta-data, currently always 0 + p += 2; + if (!excludeSignature) { + Utils::storeBigEndian(data + p, (uint16_t) m_signature.size()); + p += 2; + Utils::copy(data + p, m_signature.data(), m_signature.size()); + p += (int) m_signature.size(); } - return p; } -int Locator::unmarshal(const uint8_t *restrict data, const int len) noexcept +int Locator::unmarshal(const uint8_t *data, const int len) noexcept { - if (len <= (1 + 8 + 2 + 48)) + if (unlikely(len < 10)) return -1; - - if (data[0] != 0xff) + m_ts = (int64_t) Utils::loadBigEndian(data); + unsigned int endpointCount = Utils::loadBigEndian(data + 8); + if (unlikely(endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)) return -1; - m_ts = Utils::loadBigEndian(data + 1); - int p = 9; - - if (m_ts > 0) { - const unsigned int ec = Utils::loadBigEndian(data + p); - p += 2; - if (ec > ZT_LOCATOR_MAX_ENDPOINTS) + int p = 10; + m_endpoints.resize(endpointCount); + m_endpoints.shrink_to_fit(); + for (unsigned int i = 0;i < endpointCount;++i) { + int l = m_endpoints[i].unmarshal(data + p, len - p); + if (l <= 0) return -1; - m_endpointCount = ec; - for (unsigned int i = 0;i < ec;++i) { - int tmp = m_at[i].unmarshal(data + p, len - p); - if (tmp < 0) - return -1; - p += tmp; - } - - if ((p + 2) > len) - return -1; - const unsigned int sl = Utils::loadBigEndian(data + p); - p += 2; - if (sl > ZT_SIGNATURE_BUFFER_SIZE) - return -1; - m_signatureLength = sl; - if ((p + (int)sl) > len) - return -1; - Utils::copy(m_signature, data + p, sl); - p += (int)sl; - - if ((p + 2) > len) - return -1; - m_flags = Utils::loadBigEndian(data + p); - p += 2; - } else { - m_ts = 0; + p += l; } - + if (unlikely((p + 2) > len)) + return -1; + p += 2 + (int) Utils::loadBigEndian(data + p); + if (unlikely((p + 2) > len)) + return -1; + unsigned int siglen = Utils::loadBigEndian(data + p); + p += 2; + if (unlikely((siglen > ZT_SIGNATURE_BUFFER_SIZE) || ((p + (int) siglen) > len))) + return -1; + m_signature.unsafeSetSize(siglen); + Utils::copy(m_signature.data(), data + p, siglen); + p += siglen; + if (unlikely(p > len)) + return -1; return p; } -int Locator::makeRootSpecification(const Identity &id,int64_t ts,const Vector &endpoints,void *rootSpecBuf,unsigned int rootSpecBufSize) -{ - if (endpoints.size() > ZT_LOCATOR_MAX_ENDPOINTS) - return -1; - if (rootSpecBufSize < (ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + 1)) - return -1; - - Locator loc; - for (Vector::const_iterator e(endpoints.begin());e!=endpoints.end();++e) - loc.add(*e); - if (!loc.sign(ts,id)) - return -1; - - uint8_t *buf = reinterpret_cast(rootSpecBuf); - int idl = id.marshal(buf,false); - if (idl <= 0) - return -1; - buf += idl; - int locl = loc.marshal(buf); - if (locl <= 0) - return -1; - return idl + locl; -} - -std::pair Locator::parseRootSpecification(const void *rootSpec,unsigned int rootSpecSize) -{ - std::pair rs; - int l = rs.first.unmarshal(reinterpret_cast(rootSpec),(int)rootSpecSize); - if (l <= 0) { - rs.first.zero(); - return rs; - } - l = rs.second.unmarshal(reinterpret_cast(rootSpec) + l,(int)rootSpecSize - l); - if (l <= 0) - rs.first.zero(); - return rs; -} - } // namespace ZeroTier diff --git a/node/Locator.hpp b/node/Locator.hpp index 97da69604..849ebe758 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -14,17 +14,16 @@ #ifndef ZT_LOCATOR_HPP #define ZT_LOCATOR_HPP -#include -#include -#include - #include "Constants.hpp" #include "Endpoint.hpp" #include "Identity.hpp" #include "TriviallyCopyable.hpp" +#include "SharedPtr.hpp" +#include "FCV.hpp" +#include "Containers.hpp" #define ZT_LOCATOR_MAX_ENDPOINTS 8 -#define ZT_LOCATOR_MARSHAL_SIZE_MAX (1 + 8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE) +#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + 2 + (ZT_LOCATOR_MAX_ENDPOINTS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE) namespace ZeroTier { @@ -34,45 +33,39 @@ namespace ZeroTier { * A locator contains long-lived endpoints for a node such as IP/port pairs, * URLs, or other nodes, and is signed by the node it describes. */ -class Locator : public TriviallyCopyable +class Locator { -public: - ZT_INLINE Locator() noexcept { memoryZero(this); } + friend class SharedPtr; - /** - * Zero the Locator data structure - */ - ZT_INLINE void clear() noexcept { memoryZero(this); } +public: + ZT_INLINE Locator() noexcept : + m_ts(0) + {} + + ZT_INLINE Locator(const Locator &loc) noexcept : + m_ts(loc.m_ts), + m_endpoints(loc.m_endpoints), + m_signature(loc.m_signature), + __refCount(0) + {} /** * @return Timestamp (a.k.a. revision number) set by Location signer */ - ZT_INLINE int64_t timestamp() const noexcept { return m_ts; } + ZT_INLINE int64_t timestamp() const noexcept + { return m_ts; } /** - * @return True if locator is signed + * @return Endpoints specified in locator */ - ZT_INLINE bool isSigned() const noexcept { return m_signatureLength > 0; } + ZT_INLINE const Vector &endpoints() const noexcept + { return m_endpoints; } /** - * @return Length of signature in bytes or 0 if none + * @return Signature data */ - ZT_INLINE unsigned int signatureLength() const noexcept { return m_signatureLength; } - - /** - * @return Pointer to signature bytes - */ - ZT_INLINE const uint8_t *signature() const noexcept { return m_signature; } - - /** - * @return Number of endpoints in this locator - */ - ZT_INLINE unsigned int endpointCount() const noexcept { return m_endpointCount; } - - /** - * @return Pointer to array of endpoints - */ - ZT_INLINE const Endpoint *endpoints() const noexcept { return m_at; } + ZT_INLINE const FCV &signature() const noexcept + { return m_signature; } /** * Add an endpoint to this locator @@ -83,13 +76,7 @@ public: * @param ep Endpoint to add * @return True if endpoint was added (or already present), false if locator is full */ - ZT_INLINE bool add(const Endpoint &ep) noexcept - { - if (m_endpointCount >= ZT_LOCATOR_MAX_ENDPOINTS) - return false; - m_at[m_endpointCount++] = ep; - return true; - } + bool add(const Endpoint &ep); /** * Sign this locator @@ -100,7 +87,7 @@ public: * @param id Identity that includes private key * @return True if signature successful */ - bool sign(int64_t ts,const Identity &id) noexcept; + bool sign(int64_t ts, const Identity &id) noexcept; /** * Verify this Locator's validity and signature @@ -110,40 +97,21 @@ public: */ bool verify(const Identity &id) const noexcept; - explicit ZT_INLINE operator bool() const noexcept { return m_ts != 0; } + explicit ZT_INLINE operator bool() const noexcept + { return m_ts > 0; } - static constexpr int marshalSizeMax() noexcept { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } - int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],bool excludeSignature = false) const noexcept; - int unmarshal(const uint8_t *restrict data,int len) noexcept; + static constexpr int marshalSizeMax() noexcept + { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } - /** - * Create a signed Locator and package it with the root's identity to make a root spec - * - * @param id Identity (must have secret) - * @param ts Timestamp - * @param endpoints Endpoints - * @param rootSpecBuf Buffer to store identity and locator into - * @param rootSpecBufSize Size of buffer - * @return Bytes written to buffer or -1 on error - */ - static int makeRootSpecification(const Identity &id,int64_t ts,const Vector &endpoints,void *rootSpecBuf,unsigned int rootSpecBufSize); + int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept; - /** - * Parse a root specification and decode the identity and locator - * - * @param rootSpec Root spec bytes - * @param rootSpecSize Size in bytes - * @return Identity and locator, with identity NULL if an error occurs - */ - static std::pair parseRootSpecification(const void *rootSpec,unsigned int rootSpecSize); + int unmarshal(const uint8_t *data, int len) noexcept; private: int64_t m_ts; - unsigned int m_endpointCount; - unsigned int m_signatureLength; - Endpoint m_at[ZT_LOCATOR_MAX_ENDPOINTS]; - uint16_t m_flags; - uint8_t m_signature[ZT_SIGNATURE_BUFFER_SIZE]; + Vector m_endpoints; + FCV m_signature; + std::atomic __refCount; }; } // namespace ZeroTier diff --git a/node/MAC.hpp b/node/MAC.hpp index 0651461f9..150d02b6d 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -14,14 +14,11 @@ #ifndef ZT_MAC_HPP #define ZT_MAC_HPP -#include -#include -#include - #include "Constants.hpp" #include "Utils.hpp" #include "Address.hpp" #include "TriviallyCopyable.hpp" +#include "Containers.hpp" namespace ZeroTier { @@ -32,10 +29,20 @@ class MAC : public TriviallyCopyable { public: ZT_INLINE MAC() noexcept : m_mac(0ULL) {} - ZT_INLINE MAC(const uint8_t a,const uint8_t b,const uint8_t c,const uint8_t d,const uint8_t e,const uint8_t f) noexcept : m_mac((((uint64_t)a) << 40U) | (((uint64_t)b) << 32U) | (((uint64_t)c) << 24U) | (((uint64_t)d) << 16U) | (((uint64_t)e) << 8U) | ((uint64_t)f) ) {} - explicit ZT_INLINE MAC(const uint64_t m) noexcept : m_mac(m) {} - explicit ZT_INLINE MAC(const uint8_t b[6]) noexcept { setTo(b); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) - ZT_INLINE MAC(const Address &ztaddr,const uint64_t nwid) noexcept { fromAddress(ztaddr,nwid); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + + ZT_INLINE MAC(const uint8_t a,const uint8_t b,const uint8_t c,const uint8_t d,const uint8_t e,const uint8_t f) noexcept : + m_mac((((uint64_t)a) << 40U) | (((uint64_t)b) << 32U) | (((uint64_t)c) << 24U) | (((uint64_t)d) << 16U) | (((uint64_t)e) << 8U) | ((uint64_t)f) ) + {} + + explicit ZT_INLINE MAC(const uint64_t m) noexcept : + m_mac(m) + {} + + explicit ZT_INLINE MAC(const uint8_t b[6]) noexcept + { setTo(b); } + + ZT_INLINE MAC(const Address &ztaddr,const uint64_t nwid) noexcept + { fromAddress(ztaddr,nwid); } /** * @return MAC in 64-bit integer @@ -47,11 +54,6 @@ public: */ ZT_INLINE void zero() noexcept { m_mac = 0ULL; } - /** - * @return True if MAC is non-zero - */ - ZT_INLINE operator bool() const noexcept { return (m_mac != 0ULL); } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) - /** * @param bits Raw MAC in big-endian byte order * @param len Length, must be >= 6 or result is zero @@ -135,7 +137,7 @@ public: * @param i Value from 0 to 5 (inclusive) * @return Byte at said position (address interpreted in big-endian order) */ - ZT_INLINE uint8_t operator[](unsigned int i) const noexcept { return (uint8_t)(m_mac >> (40 - (i * 8))); } + ZT_INLINE uint8_t operator[](unsigned int i) const noexcept { return (uint8_t)(m_mac >> (unsigned int)(40 - (i * 8))); } /** * @return 6, which is the number of bytes in a MAC, for container compliance @@ -144,6 +146,15 @@ public: ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)Utils::hash64(m_mac); } + ZT_INLINE operator bool() const noexcept { return (m_mac != 0ULL); } + ZT_INLINE operator uint64_t() const noexcept { return m_mac; } + + /** + * Convert this MAC to a standard format colon-separated hex string + * + * @param buf Buffer to store string + * @return Pointer to buf + */ ZT_INLINE char *toString(char buf[18]) const noexcept { buf[0] = Utils::HEXCHARS[(m_mac >> 44U) & 0xfU]; @@ -166,6 +177,32 @@ public: buf[17] = (char)0; return buf; } + ZT_INLINE String toString() const { char tmp[18]; return String(toString(tmp)); } + + /** + * Parse a MAC address in hex format with or without : separators and ignoring non-hex characters. + * + * @param s String to parse + */ + ZT_INLINE void fromString(const char *s) noexcept + { + m_mac = 0; + if (s) { + while (*s) { + uint64_t c; + const char hc = *s++; + if ((hc >= 48)&&(hc <= 57)) + c = (uint64_t)hc - 48; + else if ((hc >= 97)&&(hc <= 102)) + c = (uint64_t)hc - 87; + else if ((hc >= 65)&&(hc <= 70)) + c = (uint64_t)hc - 55; + else continue; + m_mac = (m_mac << 4U) | c; + } + m_mac &= 0xffffffffffffULL; + } + } ZT_INLINE MAC &operator=(const uint64_t m) noexcept { m_mac = m; return *this; } @@ -176,6 +213,13 @@ public: ZT_INLINE bool operator>(const MAC &m) const noexcept { return (m_mac > m.m_mac); } ZT_INLINE bool operator>=(const MAC &m) const noexcept { return (m_mac >= m.m_mac); } + ZT_INLINE bool operator==(const uint64_t m) const noexcept { return (m_mac == m); } + ZT_INLINE bool operator!=(const uint64_t m) const noexcept { return (m_mac != m); } + ZT_INLINE bool operator<(const uint64_t m) const noexcept { return (m_mac < m); } + ZT_INLINE bool operator<=(const uint64_t m) const noexcept { return (m_mac <= m); } + ZT_INLINE bool operator>(const uint64_t m) const noexcept { return (m_mac > m); } + ZT_INLINE bool operator>=(const uint64_t m) const noexcept { return (m_mac >= m); } + private: uint64_t m_mac; }; diff --git a/node/Membership.hpp b/node/Membership.hpp index cfc503ce5..e878045c1 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -132,7 +132,7 @@ public: return false; } else { // LEGACY: support networks run by old controllers. - if (localCom.issuedTo().address() != m_com.issuedTo().address()) + if (localCom.issuedTo().address != m_com.issuedTo().address) return false; } diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index d27ca2d13..c4876815b 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -20,8 +20,6 @@ #include "Utils.hpp" #include "TriviallyCopyable.hpp" -#include - namespace ZeroTier { /** diff --git a/node/Network.cpp b/node/Network.cpp index 3bf9c93e6..8e645efd6 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1022,7 +1022,7 @@ int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToD try { if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != m_id)) return 0; // invalid config that is not for us or not for this network - if ((!Utils::allZero(nconf.issuedToFingerprintHash,ZT_FINGERPRINT_HASH_SIZE)) && (memcmp(nconf.issuedToFingerprintHash,RR->identity.fingerprint().hash(),ZT_FINGERPRINT_HASH_SIZE) != 0)) + if ((!Utils::allZero(nconf.issuedToFingerprintHash,ZT_FINGERPRINT_HASH_SIZE)) && (memcmp(nconf.issuedToFingerprintHash,RR->identity.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE) != 0)) return 0; // full identity hash is present and does not match if (m_config == nconf) diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index f05255387..68adb20e5 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -70,16 +70,6 @@ namespace ZeroTier { */ #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL -/** - * Device that replicates multicasts - */ -#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000040000000000ULL - -/** - * Device that is allowed to remotely debug this network and query other peers for e.g. remote trace data - */ -#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_DIAGNOSTICIAN 0x0000080000000000ULL - // Fields for meta-data sent with network config requests // Protocol version (see Packet.hpp) @@ -156,7 +146,8 @@ namespace ZeroTier { */ struct NetworkConfig : TriviallyCopyable { - ZT_INLINE NetworkConfig() noexcept { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE NetworkConfig() noexcept + { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) /** * Write this network config to a dictionary for transport @@ -177,22 +168,26 @@ struct NetworkConfig : TriviallyCopyable /** * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network */ - ZT_INLINE bool enableBroadcast() const noexcept { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); } + ZT_INLINE bool enableBroadcast() const noexcept + { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); } /** * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns */ - ZT_INLINE bool ndpEmulation() const noexcept { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); } + ZT_INLINE bool ndpEmulation() const noexcept + { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); } /** * @return Network type is public (no access control) */ - ZT_INLINE bool isPublic() const noexcept { return (this->type == ZT_NETWORK_TYPE_PUBLIC); } + ZT_INLINE bool isPublic() const noexcept + { return (this->type == ZT_NETWORK_TYPE_PUBLIC); } /** * @return Network type is private (certificate access control) */ - ZT_INLINE bool isPrivate() const noexcept { return (this->type == ZT_NETWORK_TYPE_PRIVATE); } + ZT_INLINE bool isPrivate() const noexcept + { return (this->type == ZT_NETWORK_TYPE_PRIVATE); } /** * @param fromPeer Peer attempting to bridge other Ethernet peers onto network @@ -200,16 +195,20 @@ struct NetworkConfig : TriviallyCopyable */ ZT_INLINE bool permitsBridging(const Address &fromPeer) const noexcept { - for(unsigned int i=0;it = &t; RR->expect = &expect; @@ -49,6 +49,7 @@ struct _NodeObjects RR->sa = &sa; RR->topology = &topology; } + Trace t; Expect expect; VL2 vl2; @@ -59,12 +60,13 @@ struct _NodeObjects struct _sortPeerPtrsByAddress { - ZT_INLINE bool operator()(const SharedPtr &a,const SharedPtr &b) const { return (a->address() < b->address()); } + ZT_INLINE bool operator()(const SharedPtr &a, const SharedPtr &b) const + { return (a->address() < b->address()); } }; } // anonymous namespace -Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now) : +Node::Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now) : m_RR(this), RR(&m_RR), m_objects(nullptr), @@ -79,14 +81,16 @@ Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64 m_online(false) { // Load this node's identity. - uint64_t idtmp[2]; idtmp[0] = 0; idtmp[1] = 0; - Vector data(stateObjectGet(tPtr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp)); + uint64_t idtmp[2]; + idtmp[0] = 0; + idtmp[1] = 0; + Vector data(stateObjectGet(tPtr, ZT_STATE_OBJECT_IDENTITY_SECRET, idtmp)); bool haveIdentity = false; if (!data.empty()) { data.push_back(0); // zero-terminate string - if (RR->identity.fromString((const char *)data.data())) { - RR->identity.toString(false,RR->publicIdentityStr); - RR->identity.toString(true,RR->secretIdentityStr); + if (RR->identity.fromString((const char *) data.data())) { + RR->identity.toString(false, RR->publicIdentityStr); + RR->identity.toString(true, RR->secretIdentityStr); haveIdentity = true; } } @@ -94,16 +98,18 @@ Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64 // Generate a new identity if we don't have one. if (!haveIdentity) { RR->identity.generate(Identity::C25519); - RR->identity.toString(false,RR->publicIdentityStr); - RR->identity.toString(true,RR->secretIdentityStr); - idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0; - stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,RR->secretIdentityStr,(unsigned int)strlen(RR->secretIdentityStr)); - stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); + RR->identity.toString(false, RR->publicIdentityStr); + RR->identity.toString(true, RR->secretIdentityStr); + idtmp[0] = RR->identity.address(); + idtmp[1] = 0; + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_SECRET, idtmp, RR->secretIdentityStr, (unsigned int) strlen(RR->secretIdentityStr)); + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int) strlen(RR->publicIdentityStr)); } else { - idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0; - data = stateObjectGet(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp); - if ((data.empty())||(memcmp(data.data(),RR->publicIdentityStr,strlen(RR->publicIdentityStr)) != 0)) - stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); + idtmp[0] = RR->identity.address(); + idtmp[1] = 0; + data = stateObjectGet(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp); + if ((data.empty()) || (memcmp(data.data(), RR->publicIdentityStr, strlen(RR->publicIdentityStr)) != 0)) + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int) strlen(RR->publicIdentityStr)); } uint8_t tmph[ZT_SHA384_DIGEST_SIZE]; @@ -126,7 +132,7 @@ Node::~Node() m_networks_l.unlock(); if (m_objects) - delete (_NodeObjects *)m_objects; + delete (_NodeObjects *) m_objects; // Let go of cached Buf objects. If other nodes happen to be running in this // same process space new Bufs will be allocated as needed, but this is almost @@ -137,6 +143,7 @@ Node::~Node() void Node::shutdown(void *tPtr) { + postEvent(tPtr, ZT_EVENT_DOWN); if (RR->topology) RR->topology->saveAll(tPtr); } @@ -151,7 +158,7 @@ ZT_ResultCode Node::processWirePacket( volatile int64_t *nextBackgroundTaskDeadline) { m_now = now; - RR->vl1->onRemotePacket(tPtr,localSocket,(remoteAddress) ? InetAddress::NIL : *asInetAddress(remoteAddress),packetData,packetLength); + RR->vl1->onRemotePacket(tPtr, localSocket, (remoteAddress) ? InetAddress::NIL : *asInetAddress(remoteAddress), packetData, packetLength); return ZT_RESULT_OK; } @@ -170,7 +177,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame( m_now = now; SharedPtr nw(this->network(nwid)); if (nw) { - RR->vl2->onLocalEthernet(tPtr,nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength); + RR->vl2->onLocalEthernet(tPtr, nw, MAC(sourceMac), MAC(destMac), etherType, vlanId, frameData, frameLength); return ZT_RESULT_OK; } else { return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; @@ -179,20 +186,22 @@ ZT_ResultCode Node::processVirtualNetworkFrame( struct _processBackgroundTasks_eachPeer { - ZT_INLINE _processBackgroundTasks_eachPeer(const int64_t now_,Node *const parent_,void *const tPtr_) noexcept : + ZT_INLINE _processBackgroundTasks_eachPeer(const int64_t now_, Node *const parent_, void *const tPtr_) noexcept: now(now_), parent(parent_), tPtr(tPtr_), online(false), - rootsNotOnline() {} + rootsNotOnline() + {} + const int64_t now; Node *const parent; void *const tPtr; bool online; - Vector< SharedPtr > rootsNotOnline; - ZT_INLINE void operator()(const SharedPtr &peer,const bool isRoot) noexcept + Vector > rootsNotOnline; + ZT_INLINE void operator()(const SharedPtr &peer, const bool isRoot) noexcept { - peer->pulse(tPtr,now,isRoot); + peer->pulse(tPtr, now, isRoot); if (isRoot) { if (peer->directlyConnected(now)) { online = true; @@ -202,7 +211,8 @@ struct _processBackgroundTasks_eachPeer } } }; -ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline) + +ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline) { m_now = now; Mutex::Lock bl(m_backgroundTasksLock); @@ -212,7 +222,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 if ((now - m_lastPeerPulse) >= ZT_PEER_PULSE_INTERVAL) { m_lastPeerPulse = now; try { - _processBackgroundTasks_eachPeer pf(now,this,tPtr); + _processBackgroundTasks_eachPeer pf(now, this, tPtr); RR->topology->eachPeerWithRoot<_processBackgroundTasks_eachPeer &>(pf); if (pf.online != m_online) { @@ -228,7 +238,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 //for (Vector
::const_iterator r(pf.rootsNotOnline.begin()); r != pf.rootsNotOnline.end(); ++r) // RR->sw->requestWhois(tPtr,now,*r); } - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } @@ -238,8 +248,8 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 m_lastHousekeepingRun = now; { RWMutex::RLock l(m_networks_l); - for(Map< uint64_t,SharedPtr >::const_iterator i(m_networks.begin());i != m_networks.end();++i) - i->second->doPeriodicTasks(tPtr,now); + for (Map >::const_iterator i(m_networks.begin());i != m_networks.end();++i) + i->second->doPeriodicTasks(tPtr, now); } } @@ -251,7 +261,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 // optimization for network controllers to know whether to accept // or trust nodes without doing an extra cert check. m_localControllerAuthorizations_l.lock(); - for(Map::iterator i(m_localControllerAuthorizations.begin());i != m_localControllerAuthorizations.end();) { // NOLINT(hicpp-use-auto,modernize-use-auto) + for (Map::iterator i(m_localControllerAuthorizations.begin());i != m_localControllerAuthorizations.end();) { // NOLINT(hicpp-use-auto,modernize-use-auto) if ((i->second - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3)) m_localControllerAuthorizations.erase(i++); else ++i; @@ -260,40 +270,40 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 RR->topology->doPeriodicTasks(tPtr, now); RR->sa->clean(now); - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } *nextBackgroundTaskDeadline = now + ZT_TIMER_TASK_INTERVAL; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } return ZT_RESULT_OK; } -ZT_ResultCode Node::join(uint64_t nwid,const ZT_Fingerprint *controllerFingerprint,void *uptr,void *tptr) +ZT_ResultCode Node::join(uint64_t nwid, const ZT_Fingerprint *controllerFingerprint, void *uptr, void *tptr) { Fingerprint fp; if (controllerFingerprint) - Utils::copy(fp.apiFingerprint(),controllerFingerprint); + Utils::copy(fp.apiFingerprint(), controllerFingerprint); RWMutex::Lock l(m_networks_l); SharedPtr &nw = m_networks[nwid]; if (nw) return ZT_RESULT_OK; - nw.set(new Network(RR,tptr,nwid,fp,uptr,nullptr)); + nw.set(new Network(RR, tptr, nwid, fp, uptr, nullptr)); return ZT_RESULT_OK; } -ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr) +ZT_ResultCode Node::leave(uint64_t nwid, void **uptr, void *tptr) { ZT_VirtualNetworkConfig ctmp; m_networks_l.lock(); - Map< uint64_t,SharedPtr >::iterator nwi(m_networks.find(nwid)); // NOLINT(hicpp-use-auto,modernize-use-auto) + Map >::iterator nwi(m_networks.find(nwid)); // NOLINT(hicpp-use-auto,modernize-use-auto) if (nwi == m_networks.end()) { m_networks_l.unlock(); return ZT_RESULT_OK; @@ -306,53 +316,50 @@ ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr) *uptr = *nw->userPtr(); nw->externalConfig(&ctmp); - RR->node->configureVirtualNetworkPort(tptr,nwid,uptr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); + RR->node->configureVirtualNetworkPort(tptr, nwid, uptr, ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY, &ctmp); nw->destroy(); nw.zero(); uint64_t tmp[2]; - tmp[0] = nwid; tmp[1] = 0; - RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp); + tmp[0] = nwid; + tmp[1] = 0; + RR->node->stateObjectDelete(tptr, ZT_STATE_OBJECT_NETWORK_CONFIG, tmp); return ZT_RESULT_OK; } -ZT_ResultCode Node::multicastSubscribe(void *tPtr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) +ZT_ResultCode Node::multicastSubscribe(void *tPtr, uint64_t nwid, uint64_t multicastGroup, unsigned long multicastAdi) { const SharedPtr nw(this->network(nwid)); if (nw) { - nw->multicastSubscribe(tPtr,MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff))); + nw->multicastSubscribe(tPtr, MulticastGroup(MAC(multicastGroup), (uint32_t) (multicastAdi & 0xffffffff))); return ZT_RESULT_OK; } else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; } -ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) +ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid, uint64_t multicastGroup, unsigned long multicastAdi) { const SharedPtr nw(this->network(nwid)); if (nw) { - nw->multicastUnsubscribe(MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff))); + nw->multicastUnsubscribe(MulticastGroup(MAC(multicastGroup), (uint32_t) (multicastAdi & 0xffffffff))); return ZT_RESULT_OK; } else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; } -ZT_ResultCode Node::addRoot(void *tPtr,const void *rdef,unsigned int rdeflen,uint64_t *address) +ZT_ResultCode Node::addRoot(void *tPtr, const ZT_Identity *id, const ZT_Locator *loc) { - if ((!rdef)||(rdeflen == 0)) + if ((!id)||(!loc)) return ZT_RESULT_ERROR_BAD_PARAMETER; - std::pair r(Locator::parseRootSpecification(rdef,rdeflen)); - if (address) - *address = r.first.address().toInt(); - return ((r.first)&&(RR->topology->addRoot(tPtr,r.first,r.second))) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; + const SharedPtr locator(new Locator(*reinterpret_cast(loc))); + // SECURITY: locator credential validation happens in Topology.cpp in addRoot(). + return RR->topology->addRoot(tPtr, *reinterpret_cast(id), locator) ? ZT_RESULT_OK : ZT_RESULT_ERROR_INVALID_CREDENTIAL; } -ZT_ResultCode Node::removeRoot(void *tPtr,const ZT_Fingerprint *fp) +ZT_ResultCode Node::removeRoot(void *tPtr, const uint64_t address) { - if (fp) { - RR->topology->removeRoot(tPtr,Fingerprint(*fp)); - return ZT_RESULT_OK; - } - return ZT_RESULT_ERROR_BAD_PARAMETER; + RR->topology->removeRoot(tPtr, Address(address)); + return ZT_RESULT_OK; } uint64_t Node::address() const @@ -371,31 +378,45 @@ void Node::status(ZT_NodeStatus *status) const ZT_PeerList *Node::peers() const { - Vector< SharedPtr > peers; + Vector > peers; RR->topology->getAllPeers(peers); - std::sort(peers.begin(),peers.end(),_sortPeerPtrsByAddress()); + std::sort(peers.begin(), peers.end(), _sortPeerPtrsByAddress()); - char *buf = (char *)::malloc(sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size()) + (sizeof(Identity) * peers.size())); + const unsigned int bufSize = + sizeof(ZT_PeerList) + + (sizeof(ZT_Peer) * peers.size()) + + ((sizeof(ZT_Path) * ZT_MAX_PEER_NETWORK_PATHS) * peers.size()) + + (sizeof(Identity) * peers.size()) + + ((sizeof(ZT_Endpoint) * ZT_LOCATOR_MAX_ENDPOINTS) * peers.size()); + char *buf = (char *) malloc(bufSize); if (!buf) return nullptr; - ZT_PeerList *pl = (ZT_PeerList *)buf; // NOLINT(modernize-use-auto,hicpp-use-auto) - pl->peers = (ZT_Peer *)(buf + sizeof(ZT_PeerList)); - Identity *identities = (Identity *)(buf + sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size())); // NOLINT(modernize-use-auto,hicpp-use-auto) + Utils::zero(buf, bufSize); + ZT_PeerList *pl = reinterpret_cast(buf); + buf += sizeof(ZT_PeerList); + pl->peers = reinterpret_cast(buf); + buf += sizeof(ZT_Peer) * peers.size(); + ZT_Path *peerPath = reinterpret_cast(buf); + buf += (sizeof(ZT_Path) * ZT_MAX_PEER_NETWORK_PATHS) * peers.size(); + Identity *identities = reinterpret_cast(buf); + buf += sizeof(Identity) * peers.size(); + ZT_Endpoint *locatorEndpoint = reinterpret_cast(buf); const int64_t now = m_now; + pl->peerCount = 0; - for(Vector< SharedPtr >::iterator pi(peers.begin());pi!=peers.end();++pi) { // NOLINT(modernize-use-auto,modernize-loop-convert,hicpp-use-auto) - ZT_Peer *const p = &(pl->peers[pl->peerCount]); + for (Vector >::iterator pi(peers.begin());pi != peers.end();++pi) { + ZT_Peer *const p = pl->peers + pl->peerCount; p->address = (*pi)->address().toInt(); identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted - p->identity = &identities[pl->peerCount]; + p->identity = identities + pl->peerCount; p->fingerprint.address = p->address; - Utils::copy(p->fingerprint.hash,(*pi)->identity().fingerprint().hash()); + Utils::copy(p->fingerprint.hash, (*pi)->identity().fingerprint().hash); if ((*pi)->remoteVersionKnown()) { - p->versionMajor = (int)(*pi)->remoteVersionMajor(); - p->versionMinor = (int)(*pi)->remoteVersionMinor(); - p->versionRev = (int)(*pi)->remoteVersionRevision(); + p->versionMajor = (int) (*pi)->remoteVersionMajor(); + p->versionMinor = (int) (*pi)->remoteVersionMinor(); + p->versionRev = (int) (*pi)->remoteVersionRevision(); } else { p->versionMajor = -1; p->versionMinor = -1; @@ -404,25 +425,30 @@ ZT_PeerList *Node::peers() const p->latency = (*pi)->latency(); p->root = RR->topology->isRoot((*pi)->identity()) ? 1 : 0; - { - FCV bs((*pi)->bootstrap()); - p->bootstrapAddressCount = 0; - for (FCV::const_iterator i(bs.begin());i!=bs.end();++i) // NOLINT(modernize-loop-convert) - Utils::copy(&(p->bootstrap[p->bootstrapAddressCount++]),&(*i)); + p->networkCount = 0; + // TODO: enumerate network memberships + + Vector > paths; + (*pi)->getAllPaths(paths); + p->pathCount = (unsigned int) paths.size(); + p->paths = peerPath; + for (Vector >::iterator path(paths.begin());path != paths.end();++path) { + ZT_Path *const pp = peerPath++; + pp->endpoint.type = ZT_ENDPOINT_TYPE_IP_UDP; // only type supported right now + Utils::copy(&pp->endpoint.value.ss, &((*path)->address().as.ss)); + pp->lastSend = (*path)->lastOut(); + pp->lastReceive = (*path)->lastIn(); + pp->alive = (*path)->alive(now) ? 1 : 0; + pp->preferred = (p->pathCount == 0) ? 1 : 0; } - Vector< SharedPtr > paths; - (*pi)->getAllPaths(paths); - p->pathCount = 0; - for(Vector< SharedPtr >::iterator path(paths.begin());path!=paths.end();++path) { // NOLINT(modernize-use-auto,modernize-loop-convert,hicpp-use-auto) - Utils::copy(&(p->paths[p->pathCount].address),&((*path)->address())); - p->paths[p->pathCount].lastSend = (*path)->lastOut(); - p->paths[p->pathCount].lastReceive = (*path)->lastIn(); - // TODO - //p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address()); - p->paths[p->pathCount].alive = (*path)->alive(now) ? 1 : 0; - p->paths[p->pathCount].preferred = (p->pathCount == 0) ? 1 : 0; - ++p->pathCount; + const SharedPtr loc((*pi)->locator()); + if (loc) { + p->locatorTimestamp = loc->timestamp(); + p->locatorEndpointCount = (unsigned int)loc->endpoints().size(); + p->locatorEndpoints = locatorEndpoint; + for(Vector::const_iterator ep(loc->endpoints().begin());ep!=loc->endpoints().end();++ep) + *(locatorEndpoint++) = *ep; } ++pl->peerCount; @@ -435,7 +461,7 @@ ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const { SharedPtr nw(network(nwid)); if (nw) { - ZT_VirtualNetworkConfig *const nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig)); // NOLINT(modernize-use-auto,hicpp-use-auto) + ZT_VirtualNetworkConfig *const nc = (ZT_VirtualNetworkConfig *) ::malloc(sizeof(ZT_VirtualNetworkConfig)); nw->externalConfig(nc); return nc; } @@ -446,39 +472,33 @@ ZT_VirtualNetworkList *Node::networks() const { RWMutex::RLock l(m_networks_l); - char *const buf = (char *)::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * m_networks.size())); + char *const buf = (char *) ::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * m_networks.size())); if (!buf) return nullptr; - ZT_VirtualNetworkList *nl = (ZT_VirtualNetworkList *)buf; // NOLINT(modernize-use-auto,hicpp-use-auto) - nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList)); + ZT_VirtualNetworkList *nl = (ZT_VirtualNetworkList *) buf; // NOLINT(modernize-use-auto,hicpp-use-auto) + nl->networks = (ZT_VirtualNetworkConfig *) (buf + sizeof(ZT_VirtualNetworkList)); nl->networkCount = 0; - for(Map< uint64_t,SharedPtr >::const_iterator i(m_networks.begin());i != m_networks.end();++i) // NOLINT(modernize-use-auto,modernize-loop-convert,hicpp-use-auto) + for (Map >::const_iterator i(m_networks.begin());i != m_networks.end();++i) // NOLINT(modernize-use-auto,modernize-loop-convert,hicpp-use-auto) i->second->externalConfig(&(nl->networks[nl->networkCount++])); return nl; } -void Node::setNetworkUserPtr(uint64_t nwid,void *ptr) +void Node::setNetworkUserPtr(uint64_t nwid, void *ptr) { SharedPtr nw(network(nwid)); if (nw) *(nw->userPtr()) = ptr; } -void Node::freeQueryResult(void *qr) -{ - if (qr) - ::free(qr); -} - -void Node::setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int addrCount) +void Node::setInterfaceAddresses(const ZT_InterfaceAddress *addrs, unsigned int addrCount) { Mutex::Lock _l(m_localInterfaceAddresses_m); m_localInterfaceAddresses.clear(); - for(unsigned int i=0;i(&addrs[j].address)) == *(reinterpret_cast(&addrs[i].address))) { dupe = true; break; @@ -489,7 +509,7 @@ void Node::setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int a } } -int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len) +int Node::sendUserMessage(void *tptr, uint64_t dest, uint64_t typeId, const void *data, unsigned int len) { try { if (RR->identity.address().toInt() != dest) { @@ -503,7 +523,7 @@ int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *d */ return 1; } - } catch ( ... ) {} + } catch (...) {} return 0; } @@ -511,12 +531,12 @@ void Node::setController(void *networkControllerInstance) { RR->localNetworkController = reinterpret_cast(networkControllerInstance); if (networkControllerInstance) - RR->localNetworkController->init(RR->identity,this); + RR->localNetworkController->init(RR->identity, this); } // Methods used only within the core ---------------------------------------------------------------------------------- -Vector Node::stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) +Vector Node::stateObjectGet(void *const tPtr, ZT_StateObjectType type, const uint64_t id[2]) { Vector r; if (m_cb.stateGetFunction) { @@ -530,20 +550,20 @@ Vector Node::stateObjectGet(void *const tPtr,ZT_StateObjectType type,co id, &data, &freeFunc); - if ((l > 0)&&(data)&&(freeFunc)) { - r.assign(reinterpret_cast(data),reinterpret_cast(data) + l); + if ((l > 0) && (data) && (freeFunc)) { + r.assign(reinterpret_cast(data), reinterpret_cast(data) + l); freeFunc(data); } } return r; } -bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const int64_t localSocket,const InetAddress &remoteAddress) +bool Node::shouldUsePathForZeroTierTraffic(void *tPtr, const Identity &id, const int64_t localSocket, const InetAddress &remoteAddress) { { RWMutex::RLock l(m_networks_l); - for(Map< uint64_t,SharedPtr >::iterator i(m_networks.begin());i != m_networks.end();++i) { // NOLINT(hicpp-use-auto,modernize-use-auto,modernize-loop-convert) - for (unsigned int k = 0,j = i->second->config().staticIpCount; k < j; ++k) { + for (Map >::iterator i(m_networks.begin());i != m_networks.end();++i) { // NOLINT(hicpp-use-auto,modernize-use-auto,modernize-loop-convert) + for (unsigned int k = 0, j = i->second->config().staticIpCount;k < j;++k) { if (i->second->config().staticIps[k].containsAddress(remoteAddress)) return false; } @@ -556,7 +576,7 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const i m_uPtr, tPtr, id.address().toInt(), - (const ZT_Identity *)&id, + (const ZT_Identity *) &id, localSocket, reinterpret_cast(&remoteAddress)) != 0); } @@ -564,7 +584,7 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const i return true; } -bool Node::externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr) +bool Node::externalPathLookup(void *tPtr, const Identity &id, int family, InetAddress &addr) { if (m_cb.pathLookupFunction) { return (m_cb.pathLookupFunction( @@ -579,7 +599,7 @@ bool Node::externalPathLookup(void *tPtr,const Identity &id,int family,InetAddre return false; } -bool Node::localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const +bool Node::localControllerHasAuthorized(const int64_t now, const uint64_t nwid, const Address &addr) const { m_localControllerAuthorizations_l.lock(); const int64_t *const at = m_localControllerAuthorizations.get(p_LocalControllerAuth(nwid, addr)); @@ -591,7 +611,7 @@ bool Node::localControllerHasAuthorized(const int64_t now,const uint64_t nwid,co // Implementation of NetworkController::Sender ------------------------------------------------------------------------ -void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) +void Node::ncSendConfig(uint64_t nwid, uint64_t requestPacketId, const Address &destination, const NetworkConfig &nc, bool sendLegacyFormatConfig) { m_localControllerAuthorizations_l.lock(); m_localControllerAuthorizations[p_LocalControllerAuth(nwid, destination)] = now(); @@ -601,7 +621,7 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de SharedPtr n(network(nwid)); if (!n) return; - n->setConfiguration((void *)0,nc,true); + n->setConfiguration((void *) 0, nc, true); } else { Dictionary dconf; if (nc.toDictionary(dconf)) { @@ -647,12 +667,12 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de } } -void Node::ncSendRevocation(const Address &destination,const Revocation &rev) +void Node::ncSendRevocation(const Address &destination, const Revocation &rev) { if (destination == RR->identity.address()) { SharedPtr n(network(rev.networkId())); if (!n) return; - n->addCredential(nullptr,RR->identity,rev); + n->addCredential(nullptr, RR->identity, rev); } else { // TODO /* @@ -668,12 +688,12 @@ void Node::ncSendRevocation(const Address &destination,const Revocation &rev) } } -void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) +void Node::ncSendError(uint64_t nwid, uint64_t requestPacketId, const Address &destination, NetworkController::ErrorCode errorCode) { if (destination == RR->identity.address()) { SharedPtr n(network(nwid)); if (!n) return; - switch(errorCode) { + switch (errorCode) { case NetworkController::NC_ERROR_OBJECT_NOT_FOUND: case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR: n->setNotFound(); @@ -682,7 +702,8 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des n->setAccessDenied(); break; - default: break; + default: + break; } } else if (requestPacketId) { // TODO @@ -727,38 +748,44 @@ void *ZT_getBuffer() // wrapped in a SharedPtr<> to be passed into the core. try { return _ZT_BUFTOPTR(new ZeroTier::Buf()); - } catch ( ... ) { + } catch (...) { return nullptr; // can only happen on out of memory condition } } -ZT_SDK_API void ZT_freeBuffer(void *b) +void ZT_freeBuffer(void *b) { if (b) delete _ZT_PTRTOBUF(b); } -enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now) +void ZT_freeQueryResult(void *qr) { - *node = (ZT_Node *)0; + if (qr) + free(qr); +} + +enum ZT_ResultCode ZT_Node_new(ZT_Node **node, void *uptr, void *tptr, const struct ZT_Node_Callbacks *callbacks, int64_t now) +{ + *node = (ZT_Node *) 0; try { - *node = reinterpret_cast(new ZeroTier::Node(uptr,tptr,callbacks,now)); + *node = reinterpret_cast(new ZeroTier::Node(uptr, tptr, callbacks, now)); return ZT_RESULT_OK; } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; } catch (std::runtime_error &exc) { return ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -void ZT_Node_delete(ZT_Node *node,void *tPtr) +void ZT_Node_delete(ZT_Node *node, void *tPtr) { try { reinterpret_cast(node)->shutdown(tPtr); delete (reinterpret_cast(node)); - } catch ( ... ) {} + } catch (...) {} } enum ZT_ResultCode ZT_Node_processWirePacket( @@ -773,11 +800,11 @@ enum ZT_ResultCode ZT_Node_processWirePacket( volatile int64_t *nextBackgroundTaskDeadline) { try { - ZeroTier::SharedPtr buf((isZtBuffer) ? _ZT_PTRTOBUF(packetData) : new ZeroTier::Buf(packetData,packetLength & ZT_BUF_MEM_MASK)); - return reinterpret_cast(node)->processWirePacket(tptr,now,localSocket,remoteAddress,buf,packetLength,nextBackgroundTaskDeadline); + ZeroTier::SharedPtr buf((isZtBuffer) ? _ZT_PTRTOBUF(packetData) : new ZeroTier::Buf(packetData, packetLength & ZT_BUF_MEM_MASK)); + return reinterpret_cast(node)->processWirePacket(tptr, now, localSocket, remoteAddress, buf, packetLength, nextBackgroundTaskDeadline); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_OK; // "OK" since invalid packets are simply dropped, but the system is still up } } @@ -797,88 +824,88 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame( volatile int64_t *nextBackgroundTaskDeadline) { try { - ZeroTier::SharedPtr buf((isZtBuffer) ? _ZT_PTRTOBUF(frameData) : new ZeroTier::Buf(frameData,frameLength & ZT_BUF_MEM_MASK)); - return reinterpret_cast(node)->processVirtualNetworkFrame(tptr,now,nwid,sourceMac,destMac,etherType,vlanId,buf,frameLength,nextBackgroundTaskDeadline); + ZeroTier::SharedPtr buf((isZtBuffer) ? _ZT_PTRTOBUF(frameData) : new ZeroTier::Buf(frameData, frameLength & ZT_BUF_MEM_MASK)); + return reinterpret_cast(node)->processVirtualNetworkFrame(tptr, now, nwid, sourceMac, destMac, etherType, vlanId, buf, frameLength, nextBackgroundTaskDeadline); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline) +enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node, void *tptr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline) { try { - return reinterpret_cast(node)->processBackgroundTasks(tptr,now,nextBackgroundTaskDeadline); + return reinterpret_cast(node)->processBackgroundTasks(tptr, now, nextBackgroundTaskDeadline); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint,void *uptr,void *tptr) +enum ZT_ResultCode ZT_Node_join(ZT_Node *node, uint64_t nwid, const ZT_Fingerprint *controllerFingerprint, void *uptr, void *tptr) { try { - return reinterpret_cast(node)->join(nwid,controllerFingerprint,uptr,tptr); + return reinterpret_cast(node)->join(nwid, controllerFingerprint, uptr, tptr); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr) +enum ZT_ResultCode ZT_Node_leave(ZT_Node *node, uint64_t nwid, void **uptr, void *tptr) { try { - return reinterpret_cast(node)->leave(nwid,uptr,tptr); + return reinterpret_cast(node)->leave(nwid, uptr, tptr); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) +enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node, void *tptr, uint64_t nwid, uint64_t multicastGroup, unsigned long multicastAdi) { try { - return reinterpret_cast(node)->multicastSubscribe(tptr,nwid,multicastGroup,multicastAdi); + return reinterpret_cast(node)->multicastSubscribe(tptr, nwid, multicastGroup, multicastAdi); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) +enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node, uint64_t nwid, uint64_t multicastGroup, unsigned long multicastAdi) { try { - return reinterpret_cast(node)->multicastUnsubscribe(nwid,multicastGroup,multicastAdi); + return reinterpret_cast(node)->multicastUnsubscribe(nwid, multicastGroup, multicastAdi); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address) +enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node, void *tptr, const ZT_Identity *id, const ZT_Locator *loc) { try { - return reinterpret_cast(node)->addRoot(tptr,rdef,rdeflen,address); + return reinterpret_cast(node)->addRoot(tptr, id, loc); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,void *tptr,const ZT_Fingerprint *fp) +enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node, void *tptr, const uint64_t address) { try { - return reinterpret_cast(node)->removeRoot(tptr,fp); + return reinterpret_cast(node)->removeRoot(tptr, address); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; - } catch ( ... ) { + } catch (...) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } } @@ -890,31 +917,31 @@ uint64_t ZT_Node_address(ZT_Node *node) const ZT_Identity *ZT_Node_identity(ZT_Node *node) { - return (const ZT_Identity *)(&(reinterpret_cast(node)->identity())); + return (const ZT_Identity *) (&(reinterpret_cast(node)->identity())); } -void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status) +void ZT_Node_status(ZT_Node *node, ZT_NodeStatus *status) { try { reinterpret_cast(node)->status(status); - } catch ( ... ) {} + } catch (...) {} } ZT_PeerList *ZT_Node_peers(ZT_Node *node) { try { return reinterpret_cast(node)->peers(); - } catch ( ... ) { - return (ZT_PeerList *)0; + } catch (...) { + return (ZT_PeerList *) 0; } } -ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid) +ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node, uint64_t nwid) { try { return reinterpret_cast(node)->networkConfig(nwid); - } catch ( ... ) { - return (ZT_VirtualNetworkConfig *)0; + } catch (...) { + return (ZT_VirtualNetworkConfig *) 0; } } @@ -922,49 +949,42 @@ ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node) { try { return reinterpret_cast(node)->networks(); - } catch ( ... ) { - return (ZT_VirtualNetworkList *)0; + } catch (...) { + return (ZT_VirtualNetworkList *) 0; } } -void ZT_Node_setNetworkUserPtr(ZT_Node *node,uint64_t nwid,void *ptr) +void ZT_Node_setNetworkUserPtr(ZT_Node *node, uint64_t nwid, void *ptr) { try { - reinterpret_cast(node)->setNetworkUserPtr(nwid,ptr); - } catch ( ... ) {} + reinterpret_cast(node)->setNetworkUserPtr(nwid, ptr); + } catch (...) {} } -void ZT_Node_freeQueryResult(ZT_Node *node,void *qr) +void ZT_Node_setInterfaceAddresses(ZT_Node *node, const ZT_InterfaceAddress *addrs, unsigned int addrCount) { try { - reinterpret_cast(node)->freeQueryResult(qr); - } catch ( ... ) {} + reinterpret_cast(node)->setInterfaceAddresses(addrs, addrCount); + } catch (...) {} } -void ZT_Node_setInterfaceAddresses(ZT_Node *node,const ZT_InterfaceAddress *addrs,unsigned int addrCount) +int ZT_Node_sendUserMessage(ZT_Node *node, void *tptr, uint64_t dest, uint64_t typeId, const void *data, unsigned int len) { try { - reinterpret_cast(node)->setInterfaceAddresses(addrs,addrCount); - } catch ( ... ) {} -} - -int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len) -{ - try { - return reinterpret_cast(node)->sendUserMessage(tptr,dest,typeId,data,len); - } catch ( ... ) { + return reinterpret_cast(node)->sendUserMessage(tptr, dest, typeId, data, len); + } catch (...) { return 0; } } -void ZT_Node_setController(ZT_Node *node,void *networkControllerInstance) +void ZT_Node_setController(ZT_Node *node, void *networkControllerInstance) { try { reinterpret_cast(node)->setController(networkControllerInstance); - } catch ( ... ) {} + } catch (...) {} } -void ZT_version(int *major,int *minor,int *revision,int *build) +void ZT_version(int *major, int *minor, int *revision, int *build) { if (major) *major = ZEROTIER_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index 80da95e5b..99099b14a 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -26,20 +26,8 @@ #include "Buf.hpp" #include "Containers.hpp" -#include -#include -#include -#include -#include - -// Bit mask for "expecting reply" hash -#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255 -#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31 - namespace ZeroTier { -class Locator; - /** * Implementation of Node object as defined in CAPI * @@ -48,24 +36,18 @@ class Locator; class Node : public NetworkController::Sender { public: - Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now); - virtual ~Node(); - - /** - * Perform any operations that should be done prior to deleting a Node - * - * This is technically optional but recommended. - * - * @param tPtr Thread pointer to pass through to callbacks - */ - void shutdown(void *tPtr); - // Get rid of alignment warnings on 32-bit Windows #ifdef __WINDOWS__ void * operator new(size_t i) { return _mm_malloc(i,16); } void operator delete(void* p) { _mm_free(p); } #endif + Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now); + + virtual ~Node(); + + void shutdown(void *tPtr); + // Public API Functions --------------------------------------------------------------------------------------------- ZT_ResultCode processWirePacket( @@ -76,6 +58,7 @@ public: SharedPtr &packetData, unsigned int packetLength, volatile int64_t *nextBackgroundTaskDeadline); + ZT_ResultCode processVirtualNetworkFrame( void *tPtr, int64_t now, @@ -87,30 +70,80 @@ public: SharedPtr &frameData, unsigned int frameLength, volatile int64_t *nextBackgroundTaskDeadline); - ZT_ResultCode processBackgroundTasks(void *tPtr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline); - ZT_ResultCode join(uint64_t nwid,const ZT_Fingerprint *controllerFingerprint,void *uptr,void *tptr); - ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr); - ZT_ResultCode multicastSubscribe(void *tPtr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); - ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); - ZT_ResultCode addRoot(void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address); - ZT_ResultCode removeRoot(void *tptr,const ZT_Fingerprint *fp); + + ZT_ResultCode processBackgroundTasks( + void *tPtr, + int64_t now, + volatile int64_t *nextBackgroundTaskDeadline); + + ZT_ResultCode join( + uint64_t nwid, + const ZT_Fingerprint *controllerFingerprint, + void *uptr, + void *tptr); + + ZT_ResultCode leave( + uint64_t nwid, + void **uptr, + void *tptr); + + ZT_ResultCode multicastSubscribe( + void *tPtr, + uint64_t nwid, + uint64_t multicastGroup, + unsigned long multicastAdi); + + ZT_ResultCode multicastUnsubscribe( + uint64_t nwid, + uint64_t multicastGroup, + unsigned long multicastAdi); + + ZT_ResultCode addRoot( + void *tptr, + const ZT_Identity *id, + const ZT_Locator *loc); + + ZT_ResultCode removeRoot( + void *tptr, + const uint64_t address); + uint64_t address() const; - void status(ZT_NodeStatus *status) const; + + void status( + ZT_NodeStatus *status) const; + ZT_PeerList *peers() const; - ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; + + ZT_VirtualNetworkConfig *networkConfig( + uint64_t nwid) const; + ZT_VirtualNetworkList *networks() const; - void setNetworkUserPtr(uint64_t nwid,void *ptr); - void freeQueryResult(void *qr); - void setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int addrCount); - int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); - void setController(void *networkControllerInstance); + + void setNetworkUserPtr( + uint64_t nwid, + void *ptr); + + void setInterfaceAddresses( + const ZT_InterfaceAddress *addrs, + unsigned int addrCount); + + int sendUserMessage( + void *tptr, + uint64_t dest, + uint64_t typeId, + const void *data, + unsigned int len); + + void setController( + void *networkControllerInstance); // Internal functions ----------------------------------------------------------------------------------------------- /** * @return Most recent time value supplied to core via API */ - ZT_INLINE int64_t now() const noexcept { return m_now; } + ZT_INLINE int64_t now() const noexcept + { return m_now; } /** * Send packet to to the physical wire via callback @@ -123,7 +156,7 @@ public: * @param ttl TTL or 0 for default/max * @return True if send appears successful */ - ZT_INLINE bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0) noexcept + ZT_INLINE bool putPacket(void *tPtr, const int64_t localSocket, const InetAddress &addr, const void *data, unsigned int len, unsigned int ttl = 0) noexcept { return (m_cb.wirePacketSendFunction( reinterpret_cast(this), @@ -149,7 +182,7 @@ public: * @param data Ethernet frame data * @param len Ethernet frame length in bytes */ - ZT_INLINE void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) noexcept + ZT_INLINE void putFrame(void *tPtr, uint64_t nwid, void **nuptr, const MAC &source, const MAC &dest, unsigned int etherType, unsigned int vlanId, const void *data, unsigned int len) noexcept { m_cb.virtualNetworkFrameFunction( reinterpret_cast(this), @@ -194,7 +227,7 @@ public: * @param ev Event object * @param md Event data or NULL if none */ - ZT_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = nullptr) noexcept + ZT_INLINE void postEvent(void *tPtr, ZT_Event ev, const void *md = nullptr) noexcept { m_cb.eventCallback(reinterpret_cast(this), m_uPtr, tPtr, ev, md); } @@ -208,7 +241,7 @@ public: * @param op Config operation or event type * @param nc Network config info */ - ZT_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) noexcept + ZT_INLINE void configureVirtualNetworkPort(void *tPtr, uint64_t nwid, void **nuptr, ZT_VirtualNetworkConfigOperation op, const ZT_VirtualNetworkConfig *nc) noexcept { m_cb.virtualNetworkConfigFunction(reinterpret_cast(this), m_uPtr, tPtr, nwid, nuptr, op, nc); } @@ -216,7 +249,8 @@ public: /** * @return True if node appears online */ - ZT_INLINE bool online() const noexcept { return m_online; } + ZT_INLINE bool online() const noexcept + { return m_online; } /** * Get a state object @@ -226,7 +260,7 @@ public: * @param id Object ID * @return Vector containing data or empty vector if not found or empty */ - Vector stateObjectGet(void *tPtr,ZT_StateObjectType type,const uint64_t id[2]); + Vector stateObjectGet(void *tPtr, ZT_StateObjectType type, const uint64_t id[2]); /** * Store a state object @@ -237,10 +271,10 @@ public: * @param data Data to store * @param len Length of data */ - ZT_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) noexcept + ZT_INLINE void stateObjectPut(void *const tPtr, ZT_StateObjectType type, const uint64_t id[2], const void *const data, const unsigned int len) noexcept { if (m_cb.statePutFunction) - m_cb.statePutFunction(reinterpret_cast(this), m_uPtr, tPtr, type, id, data, (int)len); + m_cb.statePutFunction(reinterpret_cast(this), m_uPtr, tPtr, type, id, data, (int) len); } /** @@ -250,7 +284,7 @@ public: * @param type Object type to delete * @param id Object ID */ - ZT_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) noexcept + ZT_INLINE void stateObjectDelete(void *const tPtr, ZT_StateObjectType type, const uint64_t id[2]) noexcept { if (m_cb.statePutFunction) m_cb.statePutFunction(reinterpret_cast(this), m_uPtr, tPtr, type, id, nullptr, -1); @@ -267,7 +301,7 @@ public: * @param remoteAddress Remote address * @return True if path should be used */ - bool shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,int64_t localSocket,const InetAddress &remoteAddress); + bool shouldUsePathForZeroTierTraffic(void *tPtr, const Identity &id, int64_t localSocket, const InetAddress &remoteAddress); /** * Query callback for a physical address for a peer @@ -278,17 +312,19 @@ public: * @param addr Buffer to store address (result paramter) * @return True if addr was filled with something */ - bool externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr); + bool externalPathLookup(void *tPtr, const Identity &id, int family, InetAddress &addr); /** * @return This node's identity */ - ZT_INLINE const Identity &identity() const noexcept { return m_RR.identity; } + ZT_INLINE const Identity &identity() const noexcept + { return m_RR.identity; } /** * @return True if aggressive NAT-traversal mechanisms like scanning of <1024 ports are enabled */ - ZT_INLINE bool natMustDie() const noexcept { return m_natMustDie; } + ZT_INLINE bool natMustDie() const noexcept + { return m_natMustDie; } /** * Check whether a local controller has authorized a member on a network @@ -303,12 +339,14 @@ public: * @param addr Member address to check * @return True if member has been authorized */ - bool localControllerHasAuthorized(int64_t now,uint64_t nwid,const Address &addr) const; + 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); - virtual void ncSendRevocation(const Address &destination,const Revocation &rev); - virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); + 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; @@ -329,23 +367,33 @@ private: // weird place to put it. struct p_LocalControllerAuth { - uint64_t nwid,address; - ZT_INLINE p_LocalControllerAuth(const uint64_t nwid_, const Address &address_) noexcept: nwid(nwid_), address(address_.toInt()) {} - ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)(nwid + address); } - ZT_INLINE bool operator==(const p_LocalControllerAuth &a) const noexcept { return ((a.nwid == nwid) && (a.address == address)); } - ZT_INLINE bool operator!=(const p_LocalControllerAuth &a) const noexcept { return ((a.nwid != nwid) || (a.address != address)); } - ZT_INLINE bool operator<(const p_LocalControllerAuth &a) const noexcept { return ((a.nwid < nwid) || ((a.nwid == nwid) && (a.address < address))); } + uint64_t nwid, address; + ZT_INLINE p_LocalControllerAuth(const uint64_t nwid_, const Address &address_) noexcept: nwid(nwid_), address(address_.toInt()) + {} + + ZT_INLINE unsigned long hashCode() const noexcept + { return (unsigned long) (nwid + address); } + + ZT_INLINE bool operator==(const p_LocalControllerAuth &a) const noexcept + { return ((a.nwid == nwid) && (a.address == address)); } + + ZT_INLINE bool operator!=(const p_LocalControllerAuth &a) const noexcept + { return ((a.nwid != nwid) || (a.address != address)); } + + ZT_INLINE bool operator<(const p_LocalControllerAuth &a) const noexcept + { return ((a.nwid < nwid) || ((a.nwid == nwid) && (a.address < address))); } }; - Map m_localControllerAuthorizations; + + Map m_localControllerAuthorizations; Mutex m_localControllerAuthorizations_l; // Locally joined networks by network ID. - Map< uint64_t,SharedPtr > m_networks; + Map > m_networks; RWMutex m_networks_l; // These are local interface addresses that have been configured via the API // and can be pushed to other nodes. - Vector< ZT_InterfaceAddress > m_localInterfaceAddresses; + Vector m_localInterfaceAddresses; Mutex m_localInterfaceAddresses_m; // This is locked while running processBackgroundTasks(). diff --git a/node/Path.cpp b/node/Path.cpp index 86a03c136..015b4c8f4 100644 --- a/node/Path.cpp +++ b/node/Path.cpp @@ -17,7 +17,7 @@ namespace ZeroTier { -bool Path::send(const RuntimeEnvironment *const RR,void *const tPtr,const void *const data,const unsigned int len,const int64_t now) noexcept +bool Path::send(const RuntimeEnvironment *const RR, void *const tPtr, const void *const data, const unsigned int len, const int64_t now) noexcept { if (RR->node->putPacket(tPtr, m_localSocket, m_addr, data, len)) { m_lastOut = now; diff --git a/node/Path.hpp b/node/Path.hpp index f6c6cada3..147fb3a0f 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -26,7 +26,7 @@ namespace ZeroTier { class RuntimeEnvironment; -template +template class Defragmenter; /** @@ -37,11 +37,12 @@ class Path friend class SharedPtr; // Allow defragmenter to access fragment-in-flight info stored in Path for performance reasons. - template - friend class Defragmenter; + template + friend + class Defragmenter; public: - ZT_INLINE Path(const int64_t l,const InetAddress &r) noexcept : + ZT_INLINE Path(const int64_t l, const InetAddress &r) noexcept: m_localSocket(l), m_lastIn(0), m_lastOut(0), @@ -60,7 +61,7 @@ public: * @param now Current time * @return True if transport reported success */ - bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now) noexcept; + bool send(const RuntimeEnvironment *RR, void *tPtr, const void *data, unsigned int len, int64_t now) noexcept; /** * Explicitly update last sent time @@ -68,7 +69,7 @@ public: * @param now Time of send * @param bytes Bytes sent */ - ZT_INLINE void sent(const int64_t now,const unsigned int bytes) noexcept + ZT_INLINE void sent(const int64_t now, const unsigned int bytes) noexcept { m_lastOut.store(now); m_outMeter.log(now, bytes); @@ -80,7 +81,7 @@ public: * @param now Time of receive * @param bytes Bytes received */ - ZT_INLINE void received(const int64_t now,const unsigned int bytes) noexcept + ZT_INLINE void received(const int64_t now, const unsigned int bytes) noexcept { m_lastIn.store(now); m_inMeter.log(now, bytes); @@ -104,34 +105,40 @@ public: /** * @return Latency in milliseconds or -1 if unknown */ - ZT_INLINE int latency() const noexcept { return m_latency; } + ZT_INLINE int latency() const noexcept + { return m_latency; } /** * Check path aliveness * * @param now Current time */ - ZT_INLINE bool alive(const int64_t now) const noexcept { return ((now - m_lastIn.load()) < ZT_PATH_ALIVE_TIMEOUT); } + ZT_INLINE bool alive(const int64_t now) const noexcept + { return ((now - m_lastIn.load()) < ZT_PATH_ALIVE_TIMEOUT); } /** * @return Physical address */ - ZT_INLINE const InetAddress &address() const noexcept { return m_addr; } + ZT_INLINE const InetAddress &address() const noexcept + { return m_addr; } /** * @return Local socket as specified by external code */ - ZT_INLINE int64_t localSocket() const noexcept { return m_localSocket; } + ZT_INLINE int64_t localSocket() const noexcept + { return m_localSocket; } /** * @return Last time we received anything */ - ZT_INLINE int64_t lastIn() const noexcept { return m_lastIn.load(); } + ZT_INLINE int64_t lastIn() const noexcept + { return m_lastIn.load(); } /** * @return Last time we sent something */ - ZT_INLINE int64_t lastOut() const noexcept { return m_lastOut.load(); } + ZT_INLINE int64_t lastOut() const noexcept + { return m_lastOut.load(); } private: const int64_t m_localSocket; diff --git a/node/Peer.cpp b/node/Peer.cpp index cbc1da3a9..d8f1dddab 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -129,10 +129,6 @@ void Peer::received( // Re-prioritize paths to include the new one. m_prioritizePaths(now); - // Remember most recently learned paths for future bootstrap attempts on restart. - Endpoint pathEndpoint(path->address()); - m_bootstrap[pathEndpoint.type()] = pathEndpoint; - RR->t->learnedNewPath(tPtr, 0x582fabdd, packetId, m_id, path->address(), old); } else { path->sent(now,hello(tPtr,path->localSocket(),path->address(),now)); @@ -227,8 +223,6 @@ void Peer::pulse(void *const tPtr,const int64_t now,const bool isRoot) { RWMutex::Lock l(m_lock); - // Determine if we need to send a full HELLO because we are refreshing ephemeral - // keys or it's simply been too long. bool needHello = false; if ( (m_vProto >= 11) && ( ((now - m_ephemeralPairTimestamp) >= (ZT_SYMMETRIC_KEY_TTL / 2)) || ((m_ephemeralKeys[0])&&(m_ephemeralKeys[0]->odometer() >= (ZT_SYMMETRIC_KEY_TTL_MESSAGES / 2))) ) ) { m_ephemeralPair.generate(); @@ -237,9 +231,6 @@ void Peer::pulse(void *const tPtr,const int64_t now,const bool isRoot) needHello = true; } - // If we have no active paths and none queued to try, attempt any - // old paths we have cached in m_bootstrap or that external code - // supplies to the core via the optional API callback. if (m_tryQueue.empty()&&(m_alivePathCount == 0)) { InetAddress addr; if (RR->node->externalPathLookup(tPtr, m_id, -1, addr)) { @@ -248,27 +239,10 @@ void Peer::pulse(void *const tPtr,const int64_t now,const bool isRoot) sent(now,m_sendProbe(tPtr,-1,addr,nullptr,0,now)); } } - - if (!m_bootstrap.empty()) { - unsigned int tryAtIndex = (unsigned int)Utils::random() % (unsigned int)m_bootstrap.size(); - for(SortedMap< Endpoint::Type,Endpoint >::const_iterator i(m_bootstrap.begin());i != m_bootstrap.end();++i) { - if (tryAtIndex > 0) { - --tryAtIndex; - } else { - if ((i->second.isInetAddr())&&(!i->second.ip().ipsEqual(addr))) { - RR->t->tryingNewPath(tPtr, 0x0a009444, m_id, i->second.ip(), InetAddress::NIL, 0, 0, Identity::NIL); - sent(now,m_sendProbe(tPtr,-1,i->second.ip(),nullptr,0,now)); - break; - } - } - } - } } - // Sort paths and forget expired ones. m_prioritizePaths(now); - // Attempt queued endpoints if they don't overlap with paths. if (!m_tryQueue.empty()) { for(int k=0;k::const_iterator i(m_bootstrap.begin());i != m_bootstrap.end();++i) { // NOLINT(modernize-loop-convert,hicpp-use-auto,modernize-use-auto) - s = i->second.marshal(data + p); + if (m_locator) { + data[p++] = 1; + s = m_locator->marshal(data + p); if (s <= 0) - return -1; + return s; p += s; + } else { + data[p++] = 0; } Utils::storeBigEndian(data + p,(uint16_t)m_vProto); @@ -528,24 +499,19 @@ int Peer::unmarshal(const uint8_t *restrict data,const int len) noexcept Utils::burn(k,sizeof(k)); } - s = m_locator.unmarshal(data + p, len - p); - if (s < 0) - return s; - p += s; - - if (p >= len) - return -1; - const unsigned int bootstrapCount = data[p++]; - if (bootstrapCount > ZT_MAX_PEER_NETWORK_PATHS) - return -1; - m_bootstrap.clear(); - for(unsigned int i=0;iunmarshal(data + p, len - p); + m_locator.set(loc); if (s < 0) return s; p += s; - m_bootstrap[tmp.type()] = tmp; + } else { + return -1; } if ((p + 10) > len) diff --git a/node/Peer.hpp b/node/Peer.hpp index f6fff9401..5adb21f45 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -32,7 +32,7 @@ #include "SymmetricKey.hpp" #include "Containers.hpp" -#define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_ADDRESS_LENGTH + ZT_SYMMETRIC_KEY_SIZE + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + 1 + (ZT_MAX_PEER_NETWORK_PATHS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + (2*4) + 2) +#define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_ADDRESS_LENGTH + ZT_SYMMETRIC_KEY_SIZE + ZT_IDENTITY_MARSHAL_SIZE_MAX + 1 + ZT_LOCATOR_MARSHAL_SIZE_MAX + 1 + (ZT_MAX_PEER_NETWORK_PATHS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + (2*4) + 2) #define ZT_PEER_DEDUP_BUFFER_SIZE 1024 #define ZT_PEER_DEDUP_BUFFER_MASK 1023U @@ -47,6 +47,7 @@ class Topology; class Peer { friend class SharedPtr; + friend class Topology; public: @@ -73,22 +74,44 @@ public: /** * @return This peer's ZT address (short for identity().address()) */ - ZT_INLINE Address address() const noexcept { return m_id.address(); } + ZT_INLINE Address address() const noexcept + { return m_id.address(); } /** * @return This peer's identity */ - ZT_INLINE const Identity &identity() const noexcept { return m_id; } + ZT_INLINE const Identity &identity() const noexcept + { return m_id; } /** - * @return Copy of current locator + * @return Current locator or NULL if no locator is known */ - ZT_INLINE Locator locator() const noexcept + ZT_INLINE const SharedPtr &locator() const noexcept { RWMutex::RLock l(m_lock); return m_locator; } + /** + * Set or update peer locator + * + * This checks the locator's timestamp against the current locator and + * replace it if newer. + * + * SECURITY: note that this does NOT validate the locator's signature + * or structural validity. This MUST be done before calling this. + * + * @param loc Locator update + * @return New locator or previous if it was not replaced. + */ + ZT_INLINE SharedPtr setLocator(const SharedPtr &loc) noexcept + { + RWMutex::Lock l(m_lock); + if ((loc) && ((!m_locator) || (m_locator->timestamp() < loc->timestamp()))) + m_locator = loc; + return m_locator; + } + /** * Log receipt of an authenticated packet * @@ -117,7 +140,7 @@ public: * @param now Current time * @param bytes Number of bytes written */ - ZT_INLINE void sent(const int64_t now,const unsigned int bytes) noexcept + ZT_INLINE void sent(const int64_t now, const unsigned int bytes) noexcept { m_lastSend = now; m_outMeter.log(now, bytes); @@ -129,7 +152,7 @@ public: * @param now Current time * @param bytes Number of bytes relayed */ - ZT_INLINE void relayed(const int64_t now,const unsigned int bytes) noexcept + ZT_INLINE void relayed(const int64_t now, const unsigned int bytes) noexcept { m_relayedMeter.log(now, bytes); } @@ -163,10 +186,10 @@ public: * @param len Length in bytes * @param via Path over which to send data (may or may not be an already-learned path for this peer) */ - ZT_INLINE void send(void *tPtr,int64_t now,const void *data,unsigned int len,const SharedPtr &via) noexcept + ZT_INLINE void send(void *tPtr, int64_t now, const void *data, unsigned int len, const SharedPtr &via) noexcept { - via->send(RR,tPtr,data,len,now); - sent(now,len); + via->send(RR, tPtr, data, len, now); + sent(now, len); } /** @@ -180,7 +203,7 @@ public: * @param data Data to send * @param len Length in bytes */ - void send(void *tPtr,int64_t now,const void *data,unsigned int len) noexcept; + void send(void *tPtr, int64_t now, const void *data, unsigned int len) noexcept; /** * Send a HELLO to this peer at a specified physical address. @@ -191,7 +214,7 @@ public: * @param now Current time * @return Number of bytes sent */ - unsigned int hello(void *tPtr,int64_t localSocket,const InetAddress &atAddress,int64_t now); + unsigned int hello(void *tPtr, int64_t localSocket, const InetAddress &atAddress, int64_t now); /** * Ping this peer if needed and/or perform other periodic tasks. @@ -200,7 +223,7 @@ public: * @param now Current time * @param isRoot True if this peer is a root */ - void pulse(void *tPtr,int64_t now,bool isRoot); + void pulse(void *tPtr, int64_t now, bool isRoot); /** * Attempt to contact this peer at a given endpoint. @@ -210,7 +233,7 @@ public: * @param ep Endpoint to attempt to contact * @param bfg1024 Use BFG1024 brute force symmetric NAT busting algorithm if applicable */ - void contact(void *tPtr,int64_t now,const Endpoint &ep,bool breakSymmetricBFG1024); + void contact(void *tPtr, int64_t now, const Endpoint &ep, bool breakSymmetricBFG1024); /** * Reset paths within a given IP scope and address family @@ -225,35 +248,13 @@ public: * @param inetAddressFamily Family e.g. AF_INET * @param now Current time */ - void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now); - - /** - * @return All currently memorized bootstrap endpoints - */ - ZT_INLINE FCV bootstrap() const noexcept - { - RWMutex::RLock l(m_lock); - FCV r; - for(SortedMap::const_iterator i(m_bootstrap.begin());i != m_bootstrap.end();++i) // NOLINT(hicpp-use-auto,modernize-use-auto,modernize-loop-convert) - r.push_back(i->second); - return r; - } - - /** - * Set bootstrap endpoint - * - * @param ep Bootstrap endpoint - */ - ZT_INLINE void setBootstrap(const Endpoint &ep) noexcept - { - RWMutex::Lock l(m_lock); - m_bootstrap[ep.type()] = ep; - } + void resetWithinScope(void *tPtr, InetAddress::IpScope scope, int inetAddressFamily, int64_t now); /** * @return Time of last receive of anything, whether direct or relayed */ - ZT_INLINE int64_t lastReceive() const noexcept { return m_lastReceive; } + ZT_INLINE int64_t lastReceive() const noexcept + { return m_lastReceive; } /** * @return Average latency of all direct paths or -1 if no direct paths or unknown @@ -263,7 +264,7 @@ public: int ltot = 0; int lcnt = 0; RWMutex::RLock l(m_lock); - for(unsigned int i=0;i < m_alivePathCount;++i) { + for (unsigned int i = 0;i < m_alivePathCount;++i) { int lat = m_paths[i]->latency(); if (lat > 0) { ltot += lat; @@ -347,19 +348,28 @@ public: * @param vmin Minor version * @param vrev Revision */ - ZT_INLINE void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev) noexcept + ZT_INLINE void setRemoteVersion(unsigned int vproto, unsigned int vmaj, unsigned int vmin, unsigned int vrev) noexcept { - m_vProto = (uint16_t)vproto; - m_vMajor = (uint16_t)vmaj; - m_vMinor = (uint16_t)vmin; - m_vRevision = (uint16_t)vrev; + m_vProto = (uint16_t) vproto; + m_vMajor = (uint16_t) vmaj; + m_vMinor = (uint16_t) vmin; + m_vRevision = (uint16_t) vrev; } - ZT_INLINE unsigned int remoteVersionProtocol() const noexcept { return m_vProto; } - ZT_INLINE unsigned int remoteVersionMajor() const noexcept { return m_vMajor; } - ZT_INLINE unsigned int remoteVersionMinor() const noexcept { return m_vMinor; } - ZT_INLINE unsigned int remoteVersionRevision() const noexcept { return m_vRevision; } - ZT_INLINE bool remoteVersionKnown() const noexcept { return ((m_vMajor > 0) || (m_vMinor > 0) || (m_vRevision > 0)); } + ZT_INLINE unsigned int remoteVersionProtocol() const noexcept + { return m_vProto; } + + ZT_INLINE unsigned int remoteVersionMajor() const noexcept + { return m_vMajor; } + + ZT_INLINE unsigned int remoteVersionMinor() const noexcept + { return m_vMinor; } + + ZT_INLINE unsigned int remoteVersionRevision() const noexcept + { return m_vRevision; } + + ZT_INLINE bool remoteVersionKnown() const noexcept + { return ((m_vMajor > 0) || (m_vMinor > 0) || (m_vRevision > 0)); } /** * @return True if there is at least one alive direct path @@ -380,9 +390,12 @@ public: // NOTE: peer marshal/unmarshal only saves/restores the identity, locator, most // recent bootstrap address, and version information. - static constexpr int marshalSizeMax() noexcept { return ZT_PEER_MARSHAL_SIZE_MAX; } + static constexpr int marshalSizeMax() noexcept + { return ZT_PEER_MARSHAL_SIZE_MAX; } + int marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const noexcept; - int unmarshal(const uint8_t *restrict data,int len) noexcept; + + int unmarshal(const uint8_t *restrict data, int len) noexcept; /** * Rate limit gate for inbound WHOIS requests @@ -413,7 +426,7 @@ public: */ ZT_INLINE bool rateGateProbeRequest(const int64_t now) noexcept { - if((now - m_lastProbeReceived) > ZT_PEER_PROBE_RESPONSE_RATE_LIMIT) { + if ((now - m_lastProbeReceived) > ZT_PEER_PROBE_RESPONSE_RATE_LIMIT) { m_lastProbeReceived = now; return true; } @@ -432,12 +445,14 @@ public: ZT_INLINE bool deduplicateIncomingPacket(const uint64_t packetId) noexcept { // TODO: should take instance ID into account too, but this isn't fully wired. - return m_dedup[Utils::hash32((uint32_t)packetId) & ZT_PEER_DEDUP_BUFFER_MASK].exchange(packetId) == packetId; + return m_dedup[Utils::hash32((uint32_t) packetId) & ZT_PEER_DEDUP_BUFFER_MASK].exchange(packetId) == packetId; } private: void m_prioritizePaths(int64_t now); - unsigned int m_sendProbe(void *tPtr,int64_t localSocket,const InetAddress &atAddress,const uint16_t *ports,unsigned int numPorts,int64_t now); + + unsigned int m_sendProbe(void *tPtr, int64_t localSocket, const InetAddress &atAddress, const uint16_t *ports, unsigned int numPorts, int64_t now); + void m_deriveSecondaryIdentityKeys() noexcept; ZT_INLINE SharedPtr m_key() noexcept @@ -468,7 +483,7 @@ private: SharedPtr m_ephemeralKeys[2]; Identity m_id; - Locator m_locator; + SharedPtr m_locator; // the last time something was sent or received from this peer (direct or indirect). std::atomic m_lastReceive; @@ -500,24 +515,26 @@ private: // Direct paths sorted in descending order of preference. SharedPtr m_paths[ZT_MAX_PEER_NETWORK_PATHS]; - // For SharedPtr<> - std::atomic __refCount; - // Number of paths current alive (number of non-NULL entries in _paths). unsigned int m_alivePathCount; - // Remembered addresses by endpoint type (std::map is smaller for only a few keys). - SortedMap m_bootstrap; + // For SharedPtr<> + std::atomic __refCount; // Addresses recieved via PUSH_DIRECT_PATHS etc. that we are scheduled to try. struct p_TryQueueItem { - ZT_INLINE p_TryQueueItem() : target(), ts(0), breakSymmetricBFG1024(false) {} - ZT_INLINE p_TryQueueItem(const int64_t now, const Endpoint &t, const bool bfg) : target(t), ts(now), breakSymmetricBFG1024(bfg) {} + ZT_INLINE p_TryQueueItem() : target(), ts(0), breakSymmetricBFG1024(false) + {} + + ZT_INLINE p_TryQueueItem(const int64_t now, const Endpoint &t, const bool bfg) : target(t), ts(now), breakSymmetricBFG1024(bfg) + {} + Endpoint target; int64_t ts; bool breakSymmetricBFG1024; }; + List m_tryQueue; List::iterator m_tryQueuePtr; // loops over _tryQueue like a circular buffer diff --git a/node/Revocation.hpp b/node/Revocation.hpp index ee84d7ba1..1e745d7eb 100644 --- a/node/Revocation.hpp +++ b/node/Revocation.hpp @@ -40,9 +40,11 @@ class Revocation : public Credential friend class Credential; public: - static constexpr ZT_CredentialType credentialType() noexcept { return ZT_CREDENTIAL_TYPE_REVOCATION; } + static constexpr ZT_CredentialType credentialType() noexcept + { return ZT_CREDENTIAL_TYPE_REVOCATION; } - ZT_INLINE Revocation() noexcept { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE Revocation() noexcept + { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) /** * @param i ID (arbitrary for revocations, currently random) @@ -53,7 +55,7 @@ public: * @param tgt Target node whose credential(s) are being revoked * @param ct Credential type being revoked */ - ZT_INLINE Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const ZT_CredentialType ct) noexcept : // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + ZT_INLINE Revocation(const uint32_t i, const uint64_t nwid, const uint32_t cid, const uint64_t thr, const uint64_t fl, const Address &tgt, const ZT_CredentialType ct) noexcept: // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init) m_id(i), m_credentialId(cid), m_networkId(nwid), @@ -66,16 +68,35 @@ public: { } - ZT_INLINE uint32_t id() const noexcept { return m_id; } - ZT_INLINE uint32_t credentialId() const noexcept { return m_credentialId; } - ZT_INLINE uint64_t networkId() const noexcept { return m_networkId; } - ZT_INLINE int64_t threshold() const noexcept { return m_threshold; } - ZT_INLINE const Address &target() const noexcept { return m_target; } - ZT_INLINE const Address &signer() const noexcept { return m_signedBy; } - ZT_INLINE ZT_CredentialType typeBeingRevoked() const noexcept { return m_type; } - ZT_INLINE const uint8_t *signature() const noexcept { return m_signature; } - ZT_INLINE unsigned int signatureLength() const noexcept { return m_signatureLength; } - ZT_INLINE bool fastPropagate() const noexcept { return ((m_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); } + ZT_INLINE uint32_t id() const noexcept + { return m_id; } + + ZT_INLINE uint32_t credentialId() const noexcept + { return m_credentialId; } + + ZT_INLINE uint64_t networkId() const noexcept + { return m_networkId; } + + ZT_INLINE int64_t threshold() const noexcept + { return m_threshold; } + + ZT_INLINE const Address &target() const noexcept + { return m_target; } + + ZT_INLINE const Address &signer() const noexcept + { return m_signedBy; } + + ZT_INLINE ZT_CredentialType typeBeingRevoked() const noexcept + { return m_type; } + + ZT_INLINE const uint8_t *signature() const noexcept + { return m_signature; } + + ZT_INLINE unsigned int signatureLength() const noexcept + { return m_signatureLength; } + + ZT_INLINE bool fastPropagate() const noexcept + { return ((m_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); } /** * @param signer Signing identity, must have private key @@ -89,11 +110,15 @@ public: * @param RR Runtime environment to provide for peer lookup, etc. * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call */ - ZT_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const noexcept { return _verify(RR,tPtr,*this); } + ZT_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR, void *tPtr) const noexcept + { return _verify(RR, tPtr, *this); } - static constexpr int marshalSizeMax() noexcept { return ZT_REVOCATION_MARSHAL_SIZE_MAX; } - int marshal(uint8_t data[ZT_REVOCATION_MARSHAL_SIZE_MAX],bool forSign = false) const noexcept; - int unmarshal(const uint8_t *restrict data,int len) noexcept; + static constexpr int marshalSizeMax() noexcept + { return ZT_REVOCATION_MARSHAL_SIZE_MAX; } + + int marshal(uint8_t data[ZT_REVOCATION_MARSHAL_SIZE_MAX], bool forSign = false) const noexcept; + + int unmarshal(const uint8_t *restrict data, int len) noexcept; private: uint32_t m_id; diff --git a/node/SHA512.cpp b/node/SHA512.cpp index 0deaf2e69..ead124354 100644 --- a/node/SHA512.cpp +++ b/node/SHA512.cpp @@ -251,19 +251,24 @@ void HMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const void *msg,const u void KBKDFHMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const char label,const char context,const uint32_t iter,uint8_t out[ZT_SYMMETRIC_KEY_SIZE]) { uint8_t kbkdfMsg[13]; - uint8_t kbuf[48]; + Utils::storeBigEndian(kbkdfMsg,(uint32_t)iter); + kbkdfMsg[4] = (uint8_t)'Z'; kbkdfMsg[5] = (uint8_t)'T'; // preface our labels with something ZT-specific kbkdfMsg[6] = (uint8_t)label; kbkdfMsg[7] = 0; + kbkdfMsg[8] = (uint8_t)context; + + // Output key length: 384 bits (as 32-bit big-endian value) kbkdfMsg[9] = 0; kbkdfMsg[10] = 0; - kbkdfMsg[11] = 1; - kbkdfMsg[12] = 0; // key length: 256 bits as big-endian 32-bit value - HMACSHA384(key,&kbkdfMsg,sizeof(kbkdfMsg),kbuf); - Utils::copy(out,kbuf); + kbkdfMsg[11] = 0x01; + kbkdfMsg[12] = 0x80; + + static_assert(ZT_SYMMETRIC_KEY_SIZE == ZT_SHA384_DIGEST_SIZE,"sizeof(out) != ZT_SHA384_DIGEST_SIZE"); + HMACSHA384(key,&kbkdfMsg,sizeof(kbkdfMsg),out); } } // namespace ZeroTier diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp index 7bc7b931b..c9ad7104b 100644 --- a/node/SelfAwareness.hpp +++ b/node/SelfAwareness.hpp @@ -23,6 +23,7 @@ namespace ZeroTier { class Identity; + class RuntimeEnvironment; /** @@ -45,7 +46,7 @@ public: * @param trusted True if this peer is trusted as an authority to inform us of external address changes * @param now Current time */ - void iam(void *tPtr,const Identity &reporter,int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now); + void iam(void *tPtr, const Identity &reporter, int64_t receivedOnLocalSocket, const InetAddress &reporterPhysicalAddress, const InetAddress &myPhysicalAddress, bool trusted, int64_t now); /** * Clean up database periodically @@ -60,7 +61,7 @@ public: * @param now Current time * @return Map of count to IP/port representing how many endpoints reported each address */ - MultiMap externalAddresses(int64_t now) const; + MultiMap externalAddresses(int64_t now) const; private: struct p_PhySurfaceKey @@ -70,13 +71,21 @@ private: InetAddress reporterPhysicalAddress; InetAddress::IpScope scope; - ZT_INLINE p_PhySurfaceKey() noexcept {} - ZT_INLINE p_PhySurfaceKey(const Address &r, const int64_t rol, const InetAddress &ra, InetAddress::IpScope s) noexcept : reporter(r), receivedOnLocalSocket(rol), reporterPhysicalAddress(ra), scope(s) {} + ZT_INLINE p_PhySurfaceKey() noexcept + {} - ZT_INLINE unsigned long hashCode() const noexcept { return ((unsigned long)reporter.toInt() + (unsigned long)receivedOnLocalSocket + (unsigned long)scope); } + ZT_INLINE p_PhySurfaceKey(const Address &r, const int64_t rol, const InetAddress &ra, InetAddress::IpScope s) noexcept: reporter(r), receivedOnLocalSocket(rol), reporterPhysicalAddress(ra), scope(s) + {} + + ZT_INLINE unsigned long hashCode() const noexcept + { return ((unsigned long) reporter.toInt() + (unsigned long) receivedOnLocalSocket + (unsigned long) scope); } + + ZT_INLINE bool operator==(const p_PhySurfaceKey &k) const noexcept + { return ((reporter == k.reporter) && (receivedOnLocalSocket == k.receivedOnLocalSocket) && (reporterPhysicalAddress == k.reporterPhysicalAddress) && (scope == k.scope)); } + + ZT_INLINE bool operator!=(const p_PhySurfaceKey &k) const noexcept + { return (!(*this == k)); } - ZT_INLINE bool operator==(const p_PhySurfaceKey &k) const noexcept { return ((reporter == k.reporter) && (receivedOnLocalSocket == k.receivedOnLocalSocket) && (reporterPhysicalAddress == k.reporterPhysicalAddress) && (scope == k.scope)); } - ZT_INLINE bool operator!=(const p_PhySurfaceKey &k) const noexcept { return (!(*this == k)); } ZT_INLINE bool operator<(const p_PhySurfaceKey &k) const noexcept { if (reporter < k.reporter) { @@ -102,12 +111,15 @@ private: uint64_t ts; bool trusted; - ZT_INLINE p_PhySurfaceEntry() noexcept : mySurface(), ts(0), trusted(false) {} - ZT_INLINE p_PhySurfaceEntry(const InetAddress &a, const uint64_t t) noexcept : mySurface(a), ts(t), trusted(false) {} + ZT_INLINE p_PhySurfaceEntry() noexcept: mySurface(), ts(0), trusted(false) + {} + + ZT_INLINE p_PhySurfaceEntry(const InetAddress &a, const uint64_t t) noexcept: mySurface(a), ts(t), trusted(false) + {} }; const RuntimeEnvironment *RR; - Map< p_PhySurfaceKey,p_PhySurfaceEntry > m_phy; + Map m_phy; Mutex m_phy_l; }; diff --git a/node/Tests.cpp b/node/Tests.cpp index 8505b77c3..243c71151 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -13,6 +13,7 @@ #include "Tests.h" +//#define ZT_ENABLE_TESTS #ifdef ZT_ENABLE_TESTS #include "Constants.hpp" @@ -40,12 +41,6 @@ #include "Fingerprint.hpp" #include "Containers.hpp" -#include -#include -#include -#include -#include - #ifdef __UNIX_LIKE__ #include #include diff --git a/node/Topology.cpp b/node/Topology.cpp index 0bbb8642a..821d9bcf2 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -31,14 +31,16 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr) : if ((l > 0)&&(id)) { if ((drem -= l) <= 0) break; - Locator loc; - l = loc.unmarshal(dptr, drem); - if ((l > 0)&&(loc)) { - m_roots[id] = loc; + Locator *const loc = new Locator(); + l = loc->unmarshal(dptr, drem); + if (l > 0) { + m_roots[id].set(loc); dptr += l; ZT_SPEW("loaded root %s", id.address().toString().c_str()); if ((drem -= l) <= 0) break; + } else { + delete loc; } } } @@ -71,9 +73,9 @@ struct p_RootSortComparisonOperator } }; -bool Topology::addRoot(void *const tPtr, const Identity &id, const Locator &loc) +bool Topology::addRoot(void *const tPtr, const Identity &id, const SharedPtr &loc) { - if ((id == RR->identity) || (!id) || (!loc) || (!loc.verify(id)) || (!id.locallyValidate())) + if ((id == RR->identity) || (!id) || (!loc) || (!loc->verify(id)) || (!id.locallyValidate())) return false; RWMutex::Lock l1(m_peers_l); m_roots[id] = loc; @@ -82,20 +84,17 @@ bool Topology::addRoot(void *const tPtr, const Identity &id, const Locator &loc) return true; } -bool Topology::removeRoot(void *const tPtr, const Fingerprint &fp) +bool Topology::removeRoot(void *const tPtr, Address address) { - const bool hashIsZero = !fp.haveHash(); RWMutex::Lock l1(m_peers_l); for(Vector< SharedPtr >::const_iterator r(m_rootPeers.begin());r!=m_rootPeers.end();++r) { - if ((*r)->address() == fp.address()) { - if ((hashIsZero)||(fp == (*r)->identity().fingerprint())) { - Map::iterator rr(m_roots.find((*r)->identity())); - if (rr != m_roots.end()) { - m_roots.erase(rr); - m_updateRootPeers(tPtr); - m_writeRootList(tPtr); - return true; - } + if ((*r)->address() == address) { + Map< Identity,SharedPtr >::iterator rr(m_roots.find((*r)->identity())); + if (rr != m_roots.end()) { + m_roots.erase(rr); + m_updateRootPeers(tPtr); + m_writeRootList(tPtr); + return true; } } } @@ -174,11 +173,11 @@ void Topology::m_writeRootList(void *tPtr) uint8_t *const roots = (uint8_t *)malloc((ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + 2) * m_roots.size()); if (roots) { // sanity check int p = 0; - for (Map::const_iterator r(m_roots.begin());r!=m_roots.end();++r) { + for (Map< Identity,SharedPtr >::const_iterator r(m_roots.begin());r!=m_roots.end();++r) { int pp = r->first.marshal(roots + p, false); if (pp > 0) { p += pp; - pp = r->second.marshal(roots + p); + pp = r->second->marshal(roots + p); if (pp > 0) p += pp; } @@ -195,16 +194,23 @@ void Topology::m_updateRootPeers(void *tPtr) { // assumes m_peers_l is locked for write Vector< SharedPtr > rp; - for (Map::iterator r(m_roots.begin());r!=m_roots.end();++r) { - Map< Address,SharedPtr >::iterator p(m_peers.find(r->first.address())); - if ((p == m_peers.end())||(p->second->identity() != r->first)) { - SharedPtr np(new Peer(RR)); - np->init(r->first); - m_peers[r->first.address()] = np; - rp.push_back(np); - } else { - rp.push_back(p->second); + for (Map< Identity,SharedPtr >::iterator r(m_roots.begin());r!=m_roots.end();++r) { + Map< Address,SharedPtr >::iterator pp(m_peers.find(r->first.address())); + SharedPtr p; + if (pp != m_peers.end()) + p = pp->second; + + if (!p) + m_loadCached(tPtr, r->first.address(), p); + + if ((!p) || (p->identity() != r->first)) { + p.set(new Peer(RR)); + p->init(r->first); + m_peers[r->first.address()] = p; } + + p->setLocator(r->second); + rp.push_back(p); } m_rootPeers.swap(rp); std::sort(m_rootPeers.begin(), m_rootPeers.end(), p_RootSortComparisonOperator()); diff --git a/node/Topology.hpp b/node/Topology.hpp index f107f61c6..dfa0e91d3 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -193,16 +193,16 @@ public: * @param loc Root locator * @return True if identity and locator are valid and root was added / updated */ - bool addRoot(void *tPtr,const Identity &id,const Locator &loc); + bool addRoot(void *tPtr,const Identity &id,const SharedPtr &loc); /** * Remove a root server's identity from the root server set * * @param tPtr Thread pointer - * @param fp Root identity + * @param address Root address * @return True if root found and removed, false if not found */ - bool removeRoot(void *tPtr,const Fingerprint &fp); + bool removeRoot(void *tPtr, Address address); /** * Sort roots in ascending order of apparent latency @@ -250,7 +250,7 @@ private: RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers Map< uint64_t,SharedPtr > m_paths; Map< Address,SharedPtr > m_peers; - Map< Identity,Locator > m_roots; + Map< Identity,SharedPtr > m_roots; Vector< SharedPtr > m_rootPeers; };