Browse Source

A bunch of CLI work

Adam Ierymenko 5 years ago
parent
commit
7fc78129f4

+ 6 - 0
go/cmd/zerotier/cli/common.go

@@ -14,6 +14,7 @@
 package cli
 
 import (
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"os"
@@ -43,3 +44,8 @@ func enabledDisabled(f bool) string {
 	}
 	return "DISABLED"
 }
+
+func jsonDump(obj interface{}) string {
+	j, _ := json.MarshalIndent(obj, "", "  ")
+	return string(j)
+}

+ 13 - 5
go/cmd/zerotier/cli/help.go

@@ -15,6 +15,7 @@ package cli
 
 import (
 	"fmt"
+
 	"zerotier/pkg/zerotier"
 )
 
@@ -26,7 +27,7 @@ Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, z
 func Help() {
 	fmt.Println(copyrightText)
 	fmt.Println(`
-Usage: zerotier [-options] <command> [-options] [command args]
+Usage: zerotier [-options] <command> [command args]
 
 Global Options:
   -j                                   Output raw JSON where applicable
@@ -40,11 +41,18 @@ Commands:
   status                               Show ZeroTier service status and config
   peers                                Show VL1 peers
   roots                                Show VL1 root servers
-  addroot <locator> [<name>]           Add a VL1 root
+  addroot <locator> [name]             Add a VL1 root
   removeroot <name>                    Remove a VL1 root
-  makelocator <secret> <address> [...] Make and sign a locator
-  makelocatordnskey                    Create a new secure DNS name and key
-  makelocatordns <key> <locator>       Make DNS TXT records for a locator
+  locator <command> [args]             Locator management commands
+    new <identity> <address> [...]     Create and sign a locator
+    newdnskey                          Create a secure DNS name and secret
+    getdns <key> <locator>             Create secure DNS TXT records
+  identity <command> [args]            Identity management commands
+    new                                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                             Show joined VL2 virtual networks
   join <network ID>                    Join a virtual network
   leave <network ID>                   Leave a virtual network

+ 2 - 2
go/cmd/zerotier/cli/makelocator.go → go/cmd/zerotier/cli/identity.go

@@ -13,6 +13,6 @@
 
 package cli
 
-// MakeLocator CLI command
-func MakeLocator(args []string) {
+// Identity command
+func Identity(args []string) {
 }

+ 135 - 0
go/cmd/zerotier/cli/locator.go

@@ -0,0 +1,135 @@
+/*
+ * 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 (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"zerotier/pkg/zerotier"
+)
+
+func locatorNew(args []string) {
+	if len(args) < 2 {
+		Help()
+		os.Exit(1)
+	}
+
+	identityData, err := ioutil.ReadFile(args[0])
+	if err != nil {
+		fmt.Printf("FATAL: unable to read identity: %s\n", err.Error())
+		os.Exit(1)
+	}
+	identity, err := zerotier.NewIdentityFromString(string(identityData))
+	if err != nil {
+		fmt.Printf("FATAL: invalid identity: %s\n", err.Error())
+		os.Exit(1)
+	}
+	if !identity.HasPrivate() {
+		fmt.Println("FATAL: identity does not contain secret key")
+		os.Exit(1)
+	}
+
+	var virt []*zerotier.Identity
+	var phys []*zerotier.InetAddress
+	for i := 1; i < len(args); i++ {
+		if strings.Contains(args[i], "/") {
+			a := zerotier.NewInetAddressFromString(args[i])
+			if a == nil {
+				fmt.Printf("FATAL: IP/port address '%s' is not valid\n", args[i])
+				os.Exit(1)
+			}
+			phys = append(phys, a)
+		} else {
+			a, err := zerotier.NewIdentityFromString(args[i])
+			if err != nil {
+				fmt.Printf("FATAL: identity (virtual address) '%s' is not valid: %s\n", args[i], err.Error())
+				os.Exit(1)
+			}
+			virt = append(virt, a)
+		}
+	}
+
+	loc, err := zerotier.NewLocator(identity, virt, phys)
+	if err != nil {
+		fmt.Printf("FATAL: internal error creating locator: %s\n", err.Error())
+		os.Exit(1)
+	}
+	fmt.Println(jsonDump(loc))
+}
+
+func locatorNewDNSKey(args []string) {
+	if len(args) != 0 {
+		Help()
+		os.Exit(0)
+	}
+
+	sk, err := zerotier.NewLocatorDNSSigningKey()
+	if err != nil {
+		fmt.Printf("FATAL: error creating secure DNS signing key: %s", err.Error())
+		os.Exit(1)
+	}
+	fmt.Println(jsonDump(sk))
+	os.Exit(0)
+}
+
+func locatorGetDNS(args []string) {
+	if len(args) < 2 {
+		Help()
+		os.Exit(1)
+	}
+
+	keyData, err := ioutil.ReadFile(args[0])
+	if err != nil {
+		fmt.Printf("FATAL: unable to read secure DNS key file: %s\n", err.Error())
+		os.Exit(1)
+	}
+	var sk zerotier.LocatorDNSSigningKey
+	err = json.Unmarshal(keyData, &sk)
+	if err != nil {
+		fmt.Printf("FATAL: DNS key file invalid: %s", err.Error())
+		os.Exit(1)
+	}
+
+	locData, err := ioutil.ReadFile(args[1])
+	if err != nil {
+		fmt.Printf("FATAL: unable to read locator: %s\n", err.Error())
+		os.Exit(1)
+	}
+	var loc zerotier.Locator
+	err = json.Unmarshal(locData, &loc)
+	if err != nil {
+		fmt.Printf("FATAL: locator invalid: %s", err.Error())
+		os.Exit(1)
+	}
+}
+
+// Locator CLI command
+func Locator(args []string) {
+	if len(args) > 0 {
+		switch args[0] {
+		case "new":
+			locatorNew(args[1:])
+		case "newdnskey":
+			locatorNewDNSKey(args[1:])
+		case "getdns":
+			locatorGetDNS(args[1:])
+		}
+	}
+	Help()
+	os.Exit(1)
+}

+ 0 - 18
go/cmd/zerotier/cli/makelocatordns.go

@@ -1,18 +0,0 @@
-/*
- * 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
-
-// MakeLocatorDNS CLI command
-func MakeLocatorDNS(args []string) {
-}

+ 0 - 18
go/cmd/zerotier/cli/makelocatordnskey.go

@@ -1,18 +0,0 @@
-/*
- * 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
-
-// MakeLocatorDNSKey CLI command
-func MakeLocatorDNSKey(args []string) {
-}

+ 2 - 3
go/cmd/zerotier/cli/peers.go

@@ -14,9 +14,9 @@
 package cli
 
 import (
-	"encoding/json"
 	"fmt"
 	"os"
+
 	"zerotier/pkg/zerotier"
 )
 
@@ -26,8 +26,7 @@ func Peers(basePath, authToken string, args []string, jsonOutput bool) {
 	apiGet(basePath, authToken, "/peer", &peers)
 
 	if jsonOutput {
-		j, _ := json.MarshalIndent(&peers, "", "  ")
-		fmt.Println(string(j))
+		fmt.Println(jsonDump(&peers))
 	} else {
 		fmt.Printf("<ztaddr>   <ver>   <role> <lat> <link> <lastTX> <lastRX> <path(s)>\n")
 		for _, peer := range peers {

+ 2 - 3
go/cmd/zerotier/cli/status.go

@@ -14,9 +14,9 @@
 package cli
 
 import (
-	"encoding/json"
 	"fmt"
 	"os"
+
 	"zerotier/pkg/zerotier"
 )
 
@@ -26,8 +26,7 @@ func Status(basePath, authToken string, args []string, jsonOutput bool) {
 	apiGet(basePath, authToken, "/status", &status)
 
 	if jsonOutput {
-		j, _ := json.MarshalIndent(&status, "", "  ")
-		fmt.Println(string(j))
+		fmt.Println(jsonDump(&status))
 	} else {
 		online := "ONLINE"
 		if !status.Online {

+ 6 - 7
go/cmd/zerotier/zerotier.go

@@ -21,6 +21,7 @@ import (
 	"path"
 	"runtime"
 	"strings"
+
 	"zerotier/cmd/zerotier/cli"
 	"zerotier/pkg/zerotier"
 )
@@ -112,7 +113,7 @@ func main() {
 	case "peers", "listpeers":
 		authTokenRequired(authToken)
 		cli.Peers(basePath, authToken, cmdArgs, *jflag)
-	case "roots":
+	case "roots", "listroots", "listmoons":
 		authTokenRequired(authToken)
 		cli.Roots(basePath, authToken, cmdArgs)
 	case "addroot":
@@ -121,12 +122,10 @@ func main() {
 	case "removeroot":
 		authTokenRequired(authToken)
 		cli.RemoveRoot(basePath, authToken, cmdArgs)
-	case "makelocator":
-		cli.MakeLocator(cmdArgs)
-	case "makelocatordnskey":
-		cli.MakeLocatorDNSKey(cmdArgs)
-	case "makelocatordns":
-		cli.MakeLocatorDNS(cmdArgs)
+	case "locator":
+		cli.Locator(cmdArgs)
+	case "identity":
+		cli.Identity(cmdArgs)
 	case "networks", "listnetworks":
 		authTokenRequired(authToken)
 		cli.Networks(basePath, authToken, cmdArgs)

+ 1 - 1
go/native/GoGlue.cpp

@@ -732,7 +732,7 @@ extern "C" int ZT_GoLocator_makeSecureDNSName(char *name,unsigned int nameBufSiz
 	const Str n(Locator::makeSecureDnsName(pub));
 	if (n.length() >= nameBufSize)
 		return -1;
-	Utils::scopy(name,sizeof(name),n.c_str());
+	Utils::scopy(name,nameBufSize,n.c_str());
 	return ZT_ECC384_PRIVATE_KEY_SIZE;
 }
 

+ 0 - 1
go/native/GoGlue.h

@@ -132,7 +132,6 @@ int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorS
 int ZT_GoLocator_makeSignedTxtRecords(
 	const uint8_t *locator,
 	unsigned int locatorSize,
-	int64_t ts,
 	const char *name,
 	const uint8_t *privateKey,
 	unsigned int privateKeySize,

+ 1 - 1
go/pkg/zerotier/identity.go

@@ -45,7 +45,7 @@ type Identity struct {
 // NewIdentityFromString generates a new identity from its string representation.
 // The private key is imported as well if it is present.
 func NewIdentityFromString(s string) (*Identity, error) {
-	ss := strings.Split(s, ":")
+	ss := strings.Split(strings.TrimSpace(s), ":")
 	if len(ss) < 3 {
 		return nil, ErrInvalidParameter
 	}

+ 1 - 1
go/pkg/zerotier/inetaddress.go

@@ -43,7 +43,7 @@ func (i *InetAddress) Less(i2 *InetAddress) bool {
 // NewInetAddressFromString parses an IP[/port] format address
 func NewInetAddressFromString(s string) *InetAddress {
 	i := new(InetAddress)
-	ss := strings.Split(s, "/")
+	ss := strings.Split(strings.TrimSpace(s), "/")
 	if len(ss) > 0 {
 		i.IP = net.ParseIP(ss[0])
 		i4 := i.IP.To4()

+ 19 - 0
go/pkg/zerotier/locator.go

@@ -151,6 +151,25 @@ func NewLocatorFromBytes(b []byte) (*Locator, error) {
 	return &loc, nil
 }
 
+// MakeTXTRecords creates secure DNS TXT records for this locator
+func (l *Locator) MakeTXTRecords(key *LocatorDNSSigningKey) ([]string, error) {
+	if key == nil || len(l.Bytes) == 0 || len(key.PrivateKey) == 0 {
+		return nil, ErrInvalidParameter
+	}
+	var results [256][256]C.char
+	cName := C.CString(key.SecureDNSName)
+	defer C.free(unsafe.Pointer(cName))
+	count := int(C.ZT_GoLocator_makeSignedTxtRecords((*C.uint8_t)(&l.Bytes[0]), C.uint(len(l.Bytes)), cName, (*C.uint8_t)(&key.PrivateKey[0]), C.uint(len(key.PrivateKey)), &results[0]))
+	if count > 0 {
+		var t []string
+		for i := 0; i < int(count); i++ {
+			t = append(t, C.GoString(&results[i][0]))
+		}
+		return t, nil
+	}
+	return nil, ErrInternal
+}
+
 // MarshalJSON marshals this Locator as its byte encoding
 func (l *Locator) MarshalJSON() ([]byte, error) {
 	return json.Marshal(l)

+ 11 - 18
go/pkg/zerotier/node.go

@@ -71,6 +71,9 @@ const (
 	// AFInet6 is the address family for IPv6
 	AFInet6 = C.AF_INET6
 
+	networkConfigOpUp     int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP
+	networkConfigOpUpdate int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE
+
 	defaultVirtualNetworkMTU = C.ZT_DEFAULT_MTU
 )
 
@@ -548,21 +551,11 @@ func (n *Node) Roots() []*Root {
 	if rl != nil {
 		for i := 0; i < int(rl.count); i++ {
 			root := (*C.ZT_Root)(unsafe.Pointer(uintptr(unsafe.Pointer(rl)) + C.sizeof_ZT_RootList))
-			id, err := NewIdentityFromString(C.GoString(root.identity))
-			if err == nil {
-				var addrs []InetAddress
-				for j := uintptr(0); j < uintptr(root.addressCount); j++ {
-					a := NewInetAddressFromSockaddr(unsafe.Pointer(uintptr(unsafe.Pointer(root.addresses)) + (j * C.sizeof_struct_sockaddr_storage)))
-					if a != nil && a.Valid() {
-						addrs = append(addrs, *a)
-					}
-				}
+			loc, _ := NewLocatorFromBytes(C.GoBytes(root.locator, C.int(root.locatorSize)))
+			if loc != nil {
 				roots = append(roots, &Root{
-					Name:      C.GoString(root.name),
-					Identity:  id,
-					Addresses: addrs,
-					Preferred: root.preferred != 0,
-					Online:    root.online != 0,
+					Name:    C.GoString(root.name),
+					Locator: loc,
 				})
 			}
 		}
@@ -584,11 +577,11 @@ func (n *Node) SetRoot(name string, locator *Locator) error {
 	}
 	var lb []byte
 	if locator != nil {
-		lb = locator.Bytes()
+		lb = locator.Bytes
 	}
 	var lbp unsafe.Pointer
 	if len(lb) > 0 {
-		lbp = &lb[0]
+		lbp = unsafe.Pointer(&lb[0])
 	}
 	cn := C.CString(name)
 	defer C.free(unsafe.Pointer(cn))
@@ -925,8 +918,8 @@ func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int,
 		node.networksLock.RUnlock()
 
 		if network != nil {
-			switch op {
-			case C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP, C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
+			switch int(op) {
+			case networkConfigOpUp, networkConfigOpUpdate:
 				ncc := (*C.ZT_VirtualNetworkConfig)(conf)
 				if network.networkConfigRevision() > uint64(ncc.netconfRevision) {
 					return

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

@@ -15,8 +15,6 @@ package zerotier
 
 // Root describes a root server used to find and establish communication with other nodes.
 type Root struct {
-	Name      string
-	Locator   *Locator
-	Preferred bool
-	Online    bool
+	Name    string
+	Locator *Locator
 }

+ 4 - 19
include/ZeroTierCore.h

@@ -532,29 +532,14 @@ typedef struct {
 	const char *name;
 
 	/**
-	 * Current public identity or NULL if not known (only possible with dynamic roots)
+	 * Serialized locator
 	 */
-	const char *identity;
+	const void *locator;
 
 	/**
-	 * Current physical address(es) of this root
+	 * The size of locator in bytes
 	 */
-	const struct sockaddr_storage *addresses;
-
-	/**
-	 * Number of physical addresses
-	 */
-	unsigned int addressCount;
-
-	/**
-	 * If true, this is the current preferred root
-	 */
-	int preferred;
-
-	/**
-	 * If true, this root server appears online
-	 */
-	int online;
+	unsigned int locatorSize;
 } ZT_Root;
 
 /**

+ 1 - 2
node/Str.hpp

@@ -104,8 +104,7 @@ public:
 					_l = ZT_STR_CAPACITY;
 					throw ZT_EXCEPTION_OUT_OF_BOUNDS;
 				}
-				_s[l++] = *s;
-				++s;
+				_s[l++] = *(s++);
 			}
 			_s[l] = 0;
 			_l = (uint8_t)l;

+ 19 - 50
node/Topology.hpp

@@ -360,63 +360,32 @@ public:
 	 */
 	inline ZT_RootList *apiRoots(const int64_t now) const
 	{
+		ScopedPtr< Buffer<65536> > lbuf(new Buffer<65536>());
 		Mutex::Lock l2(_roots_l);
-
-		// The memory allocated here has room for all roots plus the maximum size
-		// of their DNS names, identities, and up to 16 physical addresses. Most
-		// roots will have two: one V4 and one V6.
-		const unsigned int totalRoots = _roots.size();
-		ZT_RootList *rl = reinterpret_cast<ZT_RootList *>(malloc(sizeof(ZT_RootList) + (sizeof(ZT_Root) * totalRoots) + ((sizeof(struct sockaddr_storage) * ZT_MAX_PEER_NETWORK_PATHS) * totalRoots) + ((ZT_IDENTITY_STRING_BUFFER_LENGTH + 1024) * totalRoots)));
-		if (!rl) {
+		ZT_RootList *rl = (ZT_RootList *)malloc(sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size()) + (256 * _roots.size()) + (65536 * _roots.size()));
+		if (!rl)
 			return nullptr;
-		}
+		char *nptr = ((char *)rl) + sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size());
+		uint8_t *lptr = ((uint8_t *)nptr) + (256 * _roots.size());
 
 		unsigned int c = 0;
-		char *nameBufPtr = reinterpret_cast<char *>(rl) + sizeof(ZT_RootList) + (sizeof(ZT_Root) * totalRoots);
-		struct sockaddr_storage *addrBuf = reinterpret_cast<struct sockaddr_storage *>(nameBufPtr);
-		nameBufPtr += (sizeof(struct sockaddr_storage) * ZT_MAX_PEER_NETWORK_PATHS) * totalRoots;
-
-		_bestRoot_l.lock();
-		const Peer *const bestRootPtr = _bestRoot.ptr();
-		_bestRoot_l.unlock();
-
-		{
-			Str *k = (Str *)0;
-			Locator *v = (Locator *)0;
-			Hashtable< Str,Locator >::Iterator i(const_cast<Topology *>(this)->_roots);
-			while (i.next(k,v)) {
-				rl->roots[c].name = nameBufPtr;
-				const char *p = k->c_str();
-				while (*p)
-					*(nameBufPtr++) = *(p++);
-				*(nameBufPtr++) = (char)0;
-
-				if (v->id()) {
-					rl->roots[c].identity = nameBufPtr;
-					v->id().toString(false,nameBufPtr);
-					nameBufPtr += strlen(nameBufPtr) + 1;
-				}
-
-				rl->roots[c].addresses = addrBuf;
-				unsigned int ac = 0;
-				for(unsigned int j=(unsigned int)v->phy().size();(ac<j)&&(ac<16);++ac)
-					*(addrBuf++) = v->phy()[ac];
-				rl->roots[c].addressCount = ac;
-
-				_peers_l.lock();
-				const SharedPtr<Peer> *psptr = _peers.get(v->id().address());
-				if (psptr) {
-					rl->roots[c].preferred = (psptr->ptr() == bestRootPtr) ? 1 : 0;
-					rl->roots[c].online = (*psptr)->alive(now) ? 1 : 0;
-				}
-				_peers_l.unlock();
-
-				++c;
-			}
+		Str *k = (Str *)0;
+		Locator *v = (Locator *)0;
+		Hashtable< Str,Locator >::Iterator i(const_cast<Topology *>(this)->_roots);
+		while (i.next(k,v)) {
+			Utils::scopy(nptr,256,k->c_str());
+			rl->roots[c].name = nptr;
+			nptr += 256;
+			lbuf->clear();
+			v->serialize(*lbuf);
+			memcpy(lptr,lbuf->unsafeData(),lbuf->size());
+			rl->roots[c].locator = lptr;
+			rl->roots[c].locatorSize = lbuf->size();
+			lptr += 65536;
+			++c;
 		}
 
 		rl->count = c;
-
 		return rl;
 	}