diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index 82e25e7d2..6acf9fb62 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/go/cmd/zerotier/cli/help.go @@ -24,14 +24,14 @@ Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, z // Help dumps help to stdout func Help() { - fmt.Println(copyrightText + ` - + fmt.Println(copyrightText) + fmt.Println(` Usage: zerotier [-options] [-options] [command args] Global Options: -j Output raw JSON where applicable -p Use alternate base path - -t Use secret auth token from this file + -t Use secret auth token from this file Commands: help Show this help @@ -40,12 +40,11 @@ Commands: status Show ZeroTier service status and config peers Show VL1 peers roots Show VL1 root servers - addroot [options] Add a VL1 root - static [...] Add a root with a set identity and IPs - dynamic [default locator] Add a dynamic root fetched by name - removeroot [options] Remove a VL1 root - static Remove a root with a set identity - dynamic Remove a dynamic root fetched by name + addroot [] 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 networks Show joined VL2 virtual networks join Join a virtual network leave Leave a virtual network @@ -71,6 +70,6 @@ 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 authtoken.secret file in the service's working directory and then from a file called .zerotierauth in the user's home directory. The -t option can be -used to explicitly specify a location. -`) +used to explicitly specify a location.`) + fmt.Println() } diff --git a/go/cmd/zerotier/cli/makelocator.go b/go/cmd/zerotier/cli/makelocator.go new file mode 100644 index 000000000..99f4fec04 --- /dev/null +++ b/go/cmd/zerotier/cli/makelocator.go @@ -0,0 +1,18 @@ +/* + * 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 + +// MakeLocator CLI command +func MakeLocator(args []string) { +} diff --git a/go/cmd/zerotier/cli/makelocatordns.go b/go/cmd/zerotier/cli/makelocatordns.go new file mode 100644 index 000000000..d8cf741e9 --- /dev/null +++ b/go/cmd/zerotier/cli/makelocatordns.go @@ -0,0 +1,18 @@ +/* + * 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 new file mode 100644 index 000000000..843230122 --- /dev/null +++ b/go/cmd/zerotier/cli/makelocatordnskey.go @@ -0,0 +1,18 @@ +/* + * 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/zerotier.go b/go/cmd/zerotier/zerotier.go index d8dfec6d7..bdb728108 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -121,6 +121,12 @@ 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 "networks", "listnetworks": authTokenRequired(authToken) cli.Networks(basePath, authToken, cmdArgs) diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 2010890f8..72303fc75 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -730,9 +730,9 @@ int ZT_GoLocator_makeSecureDNSName(char *name,unsigned int nameBufSize,uint8_t * uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE]; ECC384GenerateKey(pub,privateKey); const Str n(Locator::makeSecureDnsName(pub)); - if (n.size() >= nameBufSize) + if (n.length() >= nameBufSize) return -1; - Utils::scopy(name,sizeof(name),n.c_Str()); + Utils::scopy(name,sizeof(name),n.c_str()); return ZT_ECC384_PRIVATE_KEY_SIZE; } @@ -775,9 +775,20 @@ int ZT_GoLocator_makeLocator( return s; } -int ZT_GoLocator_decodeLocator(const uint8_t *loc,unsigned int locSize,struct ZT_GoLocator_Info *info) +int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info) { - memset(info,0,sizeof(struct ZT_GoLocator_Info)); + Locator loc; + if (!loc.deserialize(locatorBytes,locatorSize)) + return -1; + if (!loc.verify()) + return -2; + loc.id().toString(false,info->id); + info->phyCount = 0; + info->virtCount = 0; + for(auto p=loc.phy().begin();p!=loc.phy().end();++p) + memcpy(&(info->phy[info->phyCount++]),&(*p),sizeof(struct sockaddr_storage)); + for(auto v=loc.virt().begin();v!=loc.virt().end();++v) + v->toString(false,info->virt[info->virtCount++]); return 1; } diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index f79ccc4b0..cfee60f21 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -92,11 +92,11 @@ int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int tar struct ZT_GoLocator_Info { char id[1024]; - struct sockaddr_storage phy[256]; - char virt[256][1024]; unsigned int phyCount; unsigned int virtCount; -} + struct sockaddr_storage phy[256]; + char virt[256][1024]; +}; /* Returns length of private key stored in private key buffer on success, -1 on fail */ int ZT_GoLocator_makeSecureDNSName(char name[256],unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize); @@ -120,8 +120,8 @@ int ZT_GoLocator_makeLocator( const char **virtualAddresses, unsigned int virtualAddressCount); -/* Returns nonzero on success, fills info structure */ -int ZT_GoLocator_decodeLocator(const uint8_t *loc,unsigned int locSize,struct ZT_GoLocator_Info *info); +/* Returns >0 on success, fills info structure */ +int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info); /* * The privateKey and privateKeySize are those created by makeSecureDNSName. diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index dbb884685..3b7750100 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -356,6 +356,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) { if queriedID == 0 { apiSendObj(out, req, http.StatusBadRequest, nil) } else { + var r Root + if apiReadObj(out, req, &r) == nil { + } } } else if req.Method == http.MethodGet || req.Method == http.MethodHead { roots := node.Roots() diff --git a/go/pkg/zerotier/errors.go b/go/pkg/zerotier/errors.go index 193af1b93..444d159fc 100644 --- a/go/pkg/zerotier/errors.go +++ b/go/pkg/zerotier/errors.go @@ -20,6 +20,7 @@ func (e Err) Error() string { return (string)(e) } // Simple ZeroTier Errors const ( + ErrInternal Err = "internal error" ErrNodeInitFailed Err = "unable to initialize core Node instance" ErrInvalidMACAddress Err = "invalid MAC address" ErrInvalidZeroTierAddress Err = "invalid ZeroTier address" @@ -28,5 +29,6 @@ const ( ErrTapInitFailed Err = "unable to create native Tap instance" ErrUncrecognizedIdentityType Err = "unrecognized identity type" ErrInvalidKey Err = "invalid key data" + ErrInvalidSignature Err = "invalid signature" ErrSecretKeyRequired Err = "secret key required" ) diff --git a/go/pkg/zerotier/locator.go b/go/pkg/zerotier/locator.go index 0b0182e28..cb744fabe 100644 --- a/go/pkg/zerotier/locator.go +++ b/go/pkg/zerotier/locator.go @@ -17,7 +17,10 @@ package zerotier //#include "../../native/GoGlue.h" import "C" -import "unsafe" +import ( + "encoding/json" + "unsafe" +) // LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS type LocatorDNSSigningKey struct { @@ -25,6 +28,20 @@ type LocatorDNSSigningKey struct { PrivateKey []byte } +// NewLocatorDNSSigningKey creates a new signing key and secure DNS name for storing locators in DNS +func NewLocatorDNSSigningKey() (*LocatorDNSSigningKey, error) { + var nameBuf [256]C.char + var keyBuf [64]byte + keySize := int(C.ZT_GoLocator_makeSecureDNSName(&nameBuf[0], 256, (*C.uint8_t)(unsafe.Pointer(&keyBuf[0])), 128)) + if keySize <= 0 { + return nil, ErrInternal + } + var sk LocatorDNSSigningKey + sk.SecureDNSName = C.GoString(&nameBuf[0]) + sk.PrivateKey = keyBuf[0:keySize] + return &sk, nil +} + // Locator is a binary serialized record containing information about where a ZeroTier node is located on the network type Locator struct { // Identity is the full identity of the node being located @@ -95,5 +112,62 @@ func NewLocator(id *Identity, virtualAddresses []*Identity, physicalAddresses [] }, nil } +// NewLocatorFromBytes decodes a locator from its serialized byte array form +func NewLocatorFromBytes(b []byte) (*Locator, error) { + if len(b) == 0 { + return nil, ErrInvalidParameter + } + var info C.struct_ZT_GoLocator_Info + res := C.ZT_GoLocator_decodeLocator((*C.uint8_t)(unsafe.Pointer(&b[0])), C.uint(len(b)), &info) + if res == -2 { + return nil, ErrInvalidSignature + } else if res <= 0 { + return nil, ErrInvalidParameter + } + + var loc Locator + + var err error + loc.Identity, err = NewIdentityFromString(C.GoString(info.id)) + if err != nil { + return nil, err + } + for i := 0; i < int(info.phyCount); i++ { + ua := sockaddrStorageToUDPAddr(&info.phy[i]) + if ua != nil { + loc.Physical = append(loc.Physical, &InetAddress{IP: ua.IP, Port: ua.Port}) + } + } + for i := 0; i < int(info.virtCount); i++ { + id, err := NewIdentityFromString(C.GoString(info.virt[i])) + if err == nil { + loc.Virtual = append(loc.Virtual, id) + } + } + + return &loc, nil +} + // Bytes returns this locator in byte serialized format func (l *Locator) Bytes() []byte { return l.bytes } + +// MarshalJSON marshals this Locator as its byte encoding +func (l *Locator) MarshalJSON() ([]byte, error) { + b := l.bytes + return json.Marshal(&b) +} + +// UnmarshalJSON unmarshals this Locator from a byte array in JSON. +func (l *Locator) UnmarshalJSON(j []byte) error { + var ba []byte + err := json.Unmarshal(j, &ba) + if err != nil { + return nil + } + tmp, err := NewLocatorFromBytes(ba) + if err != nil { + return err + } + *l = *tmp + return nil +} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 7f82c963b..5f0a24abf 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -558,7 +558,7 @@ func (n *Node) Roots() []*Root { } } roots = append(roots, &Root{ - DNSName: C.GoString(root.dnsName), + Name: C.GoString(root.dnsName), Identity: id, Addresses: addrs, Preferred: (root.preferred != 0), @@ -571,6 +571,42 @@ func (n *Node) Roots() []*Root { return roots } +// SetRoot sets or updates a root. +// Name can be a DNS name (preferably secure) for DNS fetched locators or can be +// the empty string for static roots. If the name is empty then the locator must +// be non-nil. +func (n *Node) SetRoot(name string, locator *Locator) error { + if len(name) == 0 { + if locator == nil { + return ErrInvalidParameter + } + name = locator.Identity.address.String() + } + var lb []byte + if locator != nil { + lb = locator.Bytes() + } + var lbp unsafe.Pointer + if len(lb) > 0 { + lbp = unsafe.Pointer(&lb[0]) + } + cn := C.CString(name) + defer C.free(unsafe.Pointer(cn)) + if C.ZT_Node_setRoot(n.zn, cn, lbp, C.uint(len(lb))) != 0 { + return ErrInternal + } + return nil +} + +// RemoveRoot removes a root. +// For static roots the name should be the ZeroTier address. +func (n *Node) RemoveRoot(name string) { + cn := C.CString(name) + defer C.free(unsafe.Pointer(cn)) + C.ZT_Node_removeRoot(n.zn, cn) + return +} + // Peers retrieves a list of current peers func (n *Node) Peers() []*Peer { var peers []*Peer diff --git a/go/pkg/zerotier/root.go b/go/pkg/zerotier/root.go index dc2ded0ad..a66325eb4 100644 --- a/go/pkg/zerotier/root.go +++ b/go/pkg/zerotier/root.go @@ -15,16 +15,10 @@ package zerotier // Root describes a root server used to find and establish communication with other nodes. type Root struct { - DNSName string + Name string Identity *Identity Addresses []InetAddress - Locator Locator + Locator *Locator Preferred bool Online bool } - -// Static returns true if this is a static root -func (r *Root) Static() bool { return len(r.DNSName) == 0 } - -// Dynamic returns true if this is a dynamic root -func (r *Root) Dynamic() bool { return len(r.DNSName) > 0 } diff --git a/go/pkg/zerotier/sizelimitwriter.go b/go/pkg/zerotier/sizelimitwriter.go index 1ae8a4276..a8ab86c52 100644 --- a/go/pkg/zerotier/sizelimitwriter.go +++ b/go/pkg/zerotier/sizelimitwriter.go @@ -57,7 +57,7 @@ func (w *sizeLimitWriter) trim(maxSize int, trimFactor float64, trimAtCR bool) e if flen > int64(maxSize) { var buf [131072]byte - trimAt := int64(float64(flen) * trimFactor) + trimAt := int64(float64(maxSize) * trimFactor) if trimAt >= flen { // sanity check return nil } diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index ddded6dc8..75607e132 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -523,13 +523,13 @@ enum ZT_Event */ typedef struct { /** - * DNS name for dynamic roots or NULL for static roots + * Name of root * - * If this is a static root this will be NULL and identity - * will never be NULL. For dynamic roots identity can be NULL - * if the name of this root has never been properly resolved. + * This will be a DNS name for dynamic roots. For static roots + * it will be the ZeroTier address. The presence or absence + * of a dot is used internally as a distinguisher. */ - const char *dnsName; + const char *name; /** * Current public identity or NULL if not known (only possible with dynamic roots) diff --git a/node/Locator.hpp b/node/Locator.hpp index 1ced7086a..2743b5330 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -316,14 +316,6 @@ public: throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; _ts = (int64_t)b.template at(p); p += 8; p += _id.deserialize(b,p); - const unsigned int signerCount = b[p++]; - if (signerCount > 1) /* only one third party signer is currently supported */ - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - if (signerCount == 1) { - p += _signedBy.deserialize(b,p); - } else { - _signedBy.zero(); - } const unsigned int physicalCount = b[p++]; _physical.resize(physicalCount); for(unsigned int i=0;i::Iterator i(const_cast(this)->_roots); while (i.next(k,v)) { - rl->roots[c].dnsName = nameBufPtr; + rl->roots[c].name = nameBufPtr; const char *p = k->c_str(); while (*p) *(nameBufPtr++) = *(p++);