Browse Source

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

Adam Ierymenko 5 years ago
parent
commit
b2f0b35608

+ 0 - 4
CMakeLists.txt

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

+ 39 - 37
go/cmd/zerotier/cli/help.go

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

+ 30 - 0
go/cmd/zerotier/cli/selftest.go

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

+ 3 - 0
go/cmd/zerotier/zerotier.go

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

+ 1 - 0
go/native/CMakeLists.txt

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

File diff suppressed because it is too large
+ 20 - 30
go/native/CoreTests.cpp


+ 7 - 2
go/native/GoGlue.h

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

+ 115 - 0
go/pkg/zerotier/endpoint.go

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

+ 117 - 17
go/pkg/zerotier/inetaddress.go

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

+ 43 - 4
go/pkg/zerotier/locator.go

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

+ 12 - 64
go/pkg/zerotier/node.go

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

+ 4 - 4
go/pkg/zerotier/root.go

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

+ 1 - 4
node/CMakeLists.txt

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

+ 185 - 0
node/Endpoint.cpp

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

+ 11 - 164
node/Endpoint.hpp

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

+ 62 - 0
node/InetAddress.cpp

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

+ 2 - 73
node/InetAddress.hpp

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

+ 1 - 3
node/Locator.cpp

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

+ 32 - 35
node/OS.hpp

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

+ 30 - 43
node/Packet.hpp

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

Some files were not shown because too many files changed in this diff