diff --git a/go/cmd/zerotier/cli/common.go b/go/cmd/zerotier/cli/common.go index 6b2c99799..8923f2adc 100644 --- a/go/cmd/zerotier/cli/common.go +++ b/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) +} diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index 6acf9fb62..5e8b5dd6c 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/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] [-options] [command args] +Usage: zerotier [-options] [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 [] Add a VL1 root + addroot [name] Add a VL1 root removeroot Remove a VL1 root - makelocator
[...] Make and sign a locator - makelocatordnskey Create a new secure DNS name and key - makelocatordns Make DNS TXT records for a locator + locator [args] Locator management commands + new
[...] Create and sign a locator + newdnskey Create a secure DNS name and secret + getdns Create secure DNS TXT records + identity [args] Identity management commands + new Create new identity (including secret) + getpublic Extract only public part of identity + validate Locally validate an identity + sign Sign a file with an identity's key + verify Verify a signature networks Show joined VL2 virtual networks join Join a virtual network leave Leave a virtual network diff --git a/go/cmd/zerotier/cli/makelocator.go b/go/cmd/zerotier/cli/identity.go similarity index 86% rename from go/cmd/zerotier/cli/makelocator.go rename to go/cmd/zerotier/cli/identity.go index 99f4fec04..836b927a8 100644 --- a/go/cmd/zerotier/cli/makelocator.go +++ b/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) { } diff --git a/go/cmd/zerotier/cli/locator.go b/go/cmd/zerotier/cli/locator.go new file mode 100644 index 000000000..10a66f50d --- /dev/null +++ b/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) +} diff --git a/go/cmd/zerotier/cli/makelocatordns.go b/go/cmd/zerotier/cli/makelocatordns.go deleted file mode 100644 index d8cf741e9..000000000 --- a/go/cmd/zerotier/cli/makelocatordns.go +++ /dev/null @@ -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) { -} diff --git a/go/cmd/zerotier/cli/makelocatordnskey.go b/go/cmd/zerotier/cli/makelocatordnskey.go deleted file mode 100644 index 843230122..000000000 --- a/go/cmd/zerotier/cli/makelocatordnskey.go +++ /dev/null @@ -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) { -} diff --git a/go/cmd/zerotier/cli/peers.go b/go/cmd/zerotier/cli/peers.go index 87a3c8a73..1f6d11327 100644 --- a/go/cmd/zerotier/cli/peers.go +++ b/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(" \n") for _, peer := range peers { diff --git a/go/cmd/zerotier/cli/status.go b/go/cmd/zerotier/cli/status.go index daba026c8..c5d10d261 100644 --- a/go/cmd/zerotier/cli/status.go +++ b/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 { diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index bdb728108..c9d653f78 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/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) diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 0745fb5c6..37fe739f7 100644 --- a/go/native/GoGlue.cpp +++ b/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; } diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index 2bdc32d0e..63f04fbca 100644 --- a/go/native/GoGlue.h +++ b/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, diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index bc8603fce..bde3b43ae 100644 --- a/go/pkg/zerotier/identity.go +++ b/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 } diff --git a/go/pkg/zerotier/inetaddress.go b/go/pkg/zerotier/inetaddress.go index 18f6b3546..0eb8f03cb 100644 --- a/go/pkg/zerotier/inetaddress.go +++ b/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() diff --git a/go/pkg/zerotier/locator.go b/go/pkg/zerotier/locator.go index aa1e8ba60..28458b33e 100644 --- a/go/pkg/zerotier/locator.go +++ b/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) diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 426e5d85c..b9a01be03 100644 --- a/go/pkg/zerotier/node.go +++ b/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 diff --git a/go/pkg/zerotier/root.go b/go/pkg/zerotier/root.go index 0aa977fe1..98afc79ca 100644 --- a/go/pkg/zerotier/root.go +++ b/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 } diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 75607e132..4a45b1139 100644 --- a/include/ZeroTierCore.h +++ b/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; /** diff --git a/node/Str.hpp b/node/Str.hpp index 35a36aacd..647790e05 100644 --- a/node/Str.hpp +++ b/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; diff --git a/node/Topology.hpp b/node/Topology.hpp index 0c647f64d..344f83c8d 100644 --- a/node/Topology.hpp +++ b/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(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(rl) + sizeof(ZT_RootList) + (sizeof(ZT_Root) * totalRoots); - struct sockaddr_storage *addrBuf = reinterpret_cast(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(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();(acphy()[ac]; - rl->roots[c].addressCount = ac; - - _peers_l.lock(); - const SharedPtr *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(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; }