From 1970dab13d2b4e2c274c3ae2b39f3f58d6b8bb21 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 30 May 2020 11:42:59 -0700 Subject: [PATCH] Move some toString/fromString to C++ since any ZT code base would need it, and stub out the controller commands. --- go/cmd/zerotier/cli/controller.go | 17 +++++++ go/cmd/zerotier/cli/help.go | 29 +++++++---- go/cmd/zerotier/cli/identity.go | 1 - go/cmd/zerotier/cli/locator.go | 83 ++++++++++++++++++++++++++++++ go/cmd/zerotier/cli/misc.go | 26 ++++++++-- go/cmd/zerotier/cli/network.go | 1 - go/cmd/zerotier/cli/root.go | 17 +++++++ go/cmd/zerotier/zerotier.go | 18 ++++--- go/pkg/zerotier/endpoint.go | 63 +++++++++++++++++++---- go/pkg/zerotier/fingerprint.go | 4 +- go/pkg/zerotier/locator.go | 30 ++++++++++- include/ZeroTierCore.h | 40 +++++++++++++++ node/Endpoint.cpp | 35 +++++++++++-- node/Endpoint.hpp | 9 +--- node/Fingerprint.hpp | 2 +- node/Locator.cpp | 84 +++++++++++++++++++++++++------ node/Locator.hpp | 22 ++++++-- 17 files changed, 414 insertions(+), 67 deletions(-) create mode 100644 go/cmd/zerotier/cli/controller.go create mode 100644 go/cmd/zerotier/cli/locator.go create mode 100644 go/cmd/zerotier/cli/root.go diff --git a/go/cmd/zerotier/cli/controller.go b/go/cmd/zerotier/cli/controller.go new file mode 100644 index 000000000..98766e088 --- /dev/null +++ b/go/cmd/zerotier/cli/controller.go @@ -0,0 +1,17 @@ +/* + * Copyright (c)2013-2020 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: 2024-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 + +func Controller(basePath, authToken string, args []string, jsonOutput bool) { +} diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index 21107a9c2..cd6dc837d 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/go/cmd/zerotier/cli/help.go @@ -18,7 +18,6 @@ import ( "zerotier/pkg/zerotier" ) -// Help dumps help to stdout func Help() { fmt.Printf(`ZeroTier Network Hypervisor Service Version %d.%d.%d (c)2013-2020 ZeroTier, Inc. @@ -38,11 +37,11 @@ Commands: service Start as service status Show node status, identity, and config peers List all VL1 peers - join [fingerprint] Join a virtual network - leave Leave a virtual network + join [fingerprint] Join a virtual network + leave Leave a virtual network networks List VL2 virtual networks - network Show verbose network info - set [option] [value] Get or set a network config option + network Show verbose network info + set [option] [value] Get or set a network config option manageips Is IP management allowed? manageroutes Is route management allowed? globalips Allow assignment of global IPs? @@ -62,11 +61,20 @@ Commands: verify Verify a signature locator [args] Locator management commands new
[...] Create and sign a new locator - show [] Show locator information - roots List root peers - addroot Add a root or update its locator - addroot Add or update roots from a URL - removeroot
Remove a peer from the root list + show [identity] Show locator information + root [command] Root management commands + list List root peers (same as no command) + add Add or manually update a root + add Add or update root(s) from a URL + remove
Un-designate a peer as a root + controller [option] Local controller management commands + networks List networks run by local controller + new Create a new network + set [setting] [value] Show or modify network settings + members List members of a network + member [setting] [value] Show or modify member level settings + auth Authorize a peer + deauth Deauthorize a peer The 'service' command does not exit until the service receives a signal. This is typically run from launchd (Mac), systemd or init (Linux), a Windows @@ -78,6 +86,5 @@ node. Identities can be specified verbatim on the command line or as a path to a file. This is detected automatically. - `,zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) } diff --git a/go/cmd/zerotier/cli/identity.go b/go/cmd/zerotier/cli/identity.go index ba5cba9dd..9fa05e7c7 100644 --- a/go/cmd/zerotier/cli/identity.go +++ b/go/cmd/zerotier/cli/identity.go @@ -23,7 +23,6 @@ import ( "zerotier/pkg/zerotier" ) -// Identity command func Identity(args []string) { if len(args) > 0 { switch args[0] { diff --git a/go/cmd/zerotier/cli/locator.go b/go/cmd/zerotier/cli/locator.go new file mode 100644 index 000000000..83ea70564 --- /dev/null +++ b/go/cmd/zerotier/cli/locator.go @@ -0,0 +1,83 @@ +/* + * Copyright (c)2013-2020 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: 2024-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" + "time" + "zerotier/pkg/zerotier" +) + +func Locator(args []string) { + if len(args) > 0 { + switch args[0] { + + case "new": + if len(args) >= 3 { + id := readIdentity(args[1]) + if !id.HasPrivate() { + fmt.Println("ERROR: identity is missing private key and can't be used to sign a locator.") + os.Exit(1) + } + var eps []zerotier.Endpoint + for i:=2;i 1 && len(args) < 4 { + loc := readLocator(args[1]) + var id *zerotier.Identity + if len(args) == 3 { + id = readIdentity(args[2]) + } + ts, fp, eps, valid, _ := loc.GetInfo(id) + fmt.Printf("%s\n Timestamp: %s (%d)\n Validity: ",fp.String(),time.Unix(ts / 1000,ts * 1000).String(),ts) + if id == nil { + fmt.Printf("(no identity provided)\n") + } else { + if valid { + fmt.Printf("SIGNATURE VERIFIED\n") + } else { + fmt.Printf("! INVALID SIGNATURE\n") + } + } + fmt.Print(" Endpoints: ") + for i := range eps { + if i > 0 { + fmt.Print(" ") + } + fmt.Print(eps[i].String()) + } + fmt.Printf("\n") + } + + } + + } + Help() + os.Exit(1) +} diff --git a/go/cmd/zerotier/cli/misc.go b/go/cmd/zerotier/cli/misc.go index a1f126e9e..0ab1e064d 100644 --- a/go/cmd/zerotier/cli/misc.go +++ b/go/cmd/zerotier/cli/misc.go @@ -99,23 +99,43 @@ func readIdentity(s string) *zerotier.Identity { } idData, err := ioutil.ReadFile(s) if err != nil { - fmt.Printf("FATAL: identity '%s' cannot be resolved as file or literal identity: %s", s, err.Error()) + fmt.Printf("FATAL: identity '%s' cannot be parsed as file or literal: %s", s, err.Error()) os.Exit(1) } id, err := zerotier.NewIdentityFromString(string(idData)) if err != nil { - fmt.Printf("FATAL: identity '%s' cannot be resolved as file or literal identity: %s", s, err.Error()) + fmt.Printf("FATAL: identity '%s' cannot be parsed as file or literal: %s", s, err.Error()) os.Exit(1) } return id } +func readLocator(s string) *zerotier.Locator { + if strings.ContainsRune(s, '@') { + loc, _ := zerotier.NewLocatorFromString(s) + if loc != nil { + return loc + } + } + locData, err := ioutil.ReadFile(s) + if err != nil { + fmt.Printf("FATAL: locator '%s' cannot be parsed as file or literal: %s", s, err.Error()) + os.Exit(1) + } + loc, err := zerotier.NewLocatorFromString(string(locData)) + if err != nil { + fmt.Printf("FATAL: locator '%s' cannot be parsed as file or literal: %s", s, err.Error()) + os.Exit(1) + } + return loc +} + func networkStatusStr(status int) string { switch status { case zerotier.NetworkStatusNotFound: return "NOTFOUND" case zerotier.NetworkStatusAccessDenied: - return "DENIED" + return "ACCESSDENIED" case zerotier.NetworkStatusRequestConfiguration: return "UPDATING" case zerotier.NetworkStatusOK: diff --git a/go/cmd/zerotier/cli/network.go b/go/cmd/zerotier/cli/network.go index 26d12626d..00246fc98 100644 --- a/go/cmd/zerotier/cli/network.go +++ b/go/cmd/zerotier/cli/network.go @@ -21,7 +21,6 @@ import ( "zerotier/pkg/zerotier" ) -// Network CLI command func Network(basePath, authToken string, args []string, jsonOutput bool) { if len(args) != 1 { Help() diff --git a/go/cmd/zerotier/cli/root.go b/go/cmd/zerotier/cli/root.go new file mode 100644 index 000000000..b6bac4c0d --- /dev/null +++ b/go/cmd/zerotier/cli/root.go @@ -0,0 +1,17 @@ +/* + * Copyright (c)2013-2020 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: 2024-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 + +func Root(basePath, authToken string, args []string, jsonOutput bool) { +} diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index 7058c3856..64712af25 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -127,13 +127,6 @@ func main() { case "peers", "listpeers", "lspeers": authTokenRequired(authToken) cli.Peers(basePath, authToken, cmdArgs, *jflag, false) - case "roots", "listroots", "lsroots": - authTokenRequired(authToken) - cli.Peers(basePath, authToken, cmdArgs, *jflag, true) - case "addroot": - // TODO - case "removeroot": - // TODO case "join": authTokenRequired(authToken) cli.Join(basePath, authToken, cmdArgs) @@ -151,8 +144,19 @@ func main() { cli.Set(basePath, authToken, cmdArgs) case "identity": cli.Identity(cmdArgs) + case "locator": + cli.Locator(cmdArgs) + case "root": + authTokenRequired(authToken) + cli.Root(basePath, authToken, cmdArgs, *jflag) + case "controller": + authTokenRequired(authToken) + cli.Controller(basePath, authToken, cmdArgs, *jflag) } + // Commands in the 'cli' sub-package do not return, so if we make + // it here the command was not recognized. + cli.Help() os.Exit(1) } diff --git a/go/pkg/zerotier/endpoint.go b/go/pkg/zerotier/endpoint.go index 38d95e98e..1f187a87c 100644 --- a/go/pkg/zerotier/endpoint.go +++ b/go/pkg/zerotier/endpoint.go @@ -5,11 +5,12 @@ package zerotier // static inline uint64_t _getAddress(const ZT_Endpoint *ep) { return ep->value.fp.address; } // static inline uint64_t _getMAC(const ZT_Endpoint *ep) { return ep->value.mac; } // static inline const struct sockaddr_storage *_getSS(const ZT_Endpoint *ep) { return &(ep->value.ss); } +// static inline void _setSS(ZT_Endpoint *ep,const void *ss) { memcpy(&(ep->value.ss),ss,sizeof(struct sockaddr_storage)); } import "C" import ( "encoding/json" - "fmt" + "strings" "unsafe" ) @@ -29,6 +30,42 @@ type Endpoint struct { cep C.ZT_Endpoint } +// NewEndpointFromString constructs a new endpoint from an InetAddress or Endpoint string. +// This will auto detect whether this is a plain InetAddress or an Endpoint in string +// format. If the former it's created as a ZT_ENDPOINT_TYPE_IP_UDP endpoint. +func NewEndpointFromString(s string) (*Endpoint, error) { + if len(s) == 0 { + var ep Endpoint + ep.cep._type = C.ZT_ENDPOINT_TYPE_NIL + return &ep, nil + } + if strings.IndexRune(s, '-') > 0 || (strings.IndexRune(s, ':') < 0 && strings.IndexRune(s, '.') < 0) { + var ep Endpoint + cs := C.CString(s) + defer C.free(cs) + if C.ZT_Endpoint_fromString(cs) == nil { + return nil, ErrInvalidParameter + } + return &ep, nil + } + inaddr := NewInetAddressFromString(s) + if inaddr == nil { + return nil, ErrInvalidParameter + } + return NewEndpointFromInetAddress(inaddr) +} + +func NewEndpointFromInetAddress(addr *InetAddress) (*Endpoint, error) { + var ep Endpoint + var ss C.struct_sockaddr_storage + if !makeSockaddrStorage(addr.IP, addr.Port, &ss) { + return nil, ErrInvalidParameter + } + ep.cep._type = C.ZT_ENDPOINT_TYPE_IP_UDP + C._setSS(&ep.cep, unsafe.Pointer(&ss)) + return &ep, nil +} + // Type returns this endpoint's type. func (ep *Endpoint) Type() int { return int(ep.cep._type) @@ -77,15 +114,12 @@ func (ep *Endpoint) MAC() MAC { } func (ep *Endpoint) String() string { - switch ep.cep._type { - case EndpointTypeZeroTier: - return fmt.Sprintf("%d/%s", ep.Type(), ep.Fingerprint().String()) - case EndpointTypeEthernet, EndpointTypeWifiDirect, EndpointTypeBluetooth: - return fmt.Sprintf("%d/%s", ep.Type(), ep.MAC().String()) - case EndpointTypeIp, EndpointTypeIpUdp, EndpointTypeIpTcp, EndpointTypeIpHttp2: - return fmt.Sprintf("%d/%s", ep.Type(), ep.InetAddress().String()) + var buf [4096]byte + cs := C.ZT_Endpoint_toString(&ep.cep,unsafe.Pointer(&buf[0]),4096) + if cs == nil { + return "0" } - return fmt.Sprintf("%d", ep.Type()) + return C.GoString(cs) } func (ep *Endpoint) MarshalJSON() ([]byte, error) { @@ -94,6 +128,15 @@ func (ep *Endpoint) MarshalJSON() ([]byte, error) { } func (ep *Endpoint) UnmarshalJSON(j []byte) error { - // TODO + var s string + err := json.Unmarshal(j, &s) + if err != nil { + return err + } + ep2, err := NewEndpointFromString(s) + if err != nil { + return err + } + *ep = *ep2 return nil } diff --git a/go/pkg/zerotier/fingerprint.go b/go/pkg/zerotier/fingerprint.go index 5654fc918..c4a9d0e0e 100644 --- a/go/pkg/zerotier/fingerprint.go +++ b/go/pkg/zerotier/fingerprint.go @@ -32,7 +32,7 @@ func NewFingerprintFromString(fps string) (*Fingerprint, error) { if len(fps) < AddressStringLength { return nil, ErrInvalidZeroTierAddress } - ss := strings.Split(fps, "/") + ss := strings.Split(fps, "-") if len(ss) < 1 || len(ss) > 2 { return nil, ErrInvalidParameter } @@ -67,7 +67,7 @@ func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint { func (fp *Fingerprint) String() string { if len(fp.Hash) == 48 { - return fmt.Sprintf("%.10x/%s", uint64(fp.Address), Base32StdLowerCase.EncodeToString(fp.Hash)) + return fmt.Sprintf("%.10x-%s", uint64(fp.Address), Base32StdLowerCase.EncodeToString(fp.Hash)) } return fp.Address.String() } diff --git a/go/pkg/zerotier/locator.go b/go/pkg/zerotier/locator.go index 572dd425d..67342ff37 100644 --- a/go/pkg/zerotier/locator.go +++ b/go/pkg/zerotier/locator.go @@ -64,9 +64,26 @@ func NewLocatorFromBytes(lb []byte) (*Locator, error) { return goloc, nil } +func NewLocatorFromString(s string) (*Locator, error) { + if len(s) == 0 { + return nil, ErrInvalidParameter + } + sb := []byte(s) + sb = append(sb,0) + loc := C.ZT_Locator_fromString(unsafe.Pointer(&sb[0])) + if uintptr(loc) == 0 { + return nil, ErrInvalidParameter + } + goloc := new(Locator) + goloc.cl = unsafe.Pointer(loc) + runtime.SetFinalizer(goloc, locatorFinalizer) + return goloc, nil +} + // GetInfo gets information about this locator, also validating its signature if 'id' is non-nil. // If 'id' is nil the 'valid' return value is undefined. -func (loc *Locator) GetInfo(id *Identity) (fp *Fingerprint, eps []Endpoint, valid bool, err error) { +func (loc *Locator) GetInfo(id *Identity) (ts int64, fp *Fingerprint, eps []Endpoint, valid bool, err error) { + ts = int64(C.ZT_Locator_timestamp(loc.cl)) cfp := C.ZT_Locator_fingerprint(loc.cl) if uintptr(unsafe.Pointer(cfp)) == 0 { err = ErrInternal @@ -84,3 +101,14 @@ func (loc *Locator) GetInfo(id *Identity) (fp *Fingerprint, eps []Endpoint, vali } return } + +func (loc *Locator) String() string { + var buf [4096]byte + C.ZT_Locator_toString(loc.cl,unsafe.Pointer(&buf[0]),4096) + for i:=range buf { + if buf[i] == 0 { + return string(buf[0:i]) + } + } + return "" +} diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 9d50c472d..2d6faa3c4 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -2123,6 +2123,17 @@ ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id); /* ---------------------------------------------------------------------------------------------------------------- */ +ZT_SDK_API char *ZT_Endpoint_toString( + const ZT_Endpoint *ep, + char *buf, + int capacity); + +ZT_SDK_API int ZT_Endpoint_fromString( + ZT_Endpoint *ep, + const char *str); + +/* ---------------------------------------------------------------------------------------------------------------- */ + /** * Create and sign a new locator * @@ -2149,6 +2160,14 @@ ZT_SDK_API ZT_Locator *ZT_Locator_unmarshal( const void *data, unsigned int len); +/** + * Decode a locator from string format + * + * @param str String format locator + * @return Locator or NULL if string is not valid + */ +ZT_SDK_API ZT_Locator *ZT_Locator_fromString(const char *str); + /** * Serialize this locator into a buffer * @@ -2159,6 +2178,19 @@ ZT_SDK_API ZT_Locator *ZT_Locator_unmarshal( */ ZT_SDK_API int ZT_Locator_marshal(const ZT_Locator *loc,void *buf,unsigned int bufSize); +/** + * Get this locator in string format + * + * @param loc Locator + * @param buf Buffer to store string + * @param capacity Capacity of buffer in bytes (recommended size: 4096) + * @return Pointer to buffer or NULL if an error occurs + */ +ZT_SDK_API char *ZT_Locator_toString( + const ZT_Locator *loc, + char *buf, + int capacity); + /** * Get a pointer to the fingerprint of this locator's signer. * @@ -2169,6 +2201,14 @@ ZT_SDK_API int ZT_Locator_marshal(const ZT_Locator *loc,void *buf,unsigned int b */ ZT_SDK_API const ZT_Fingerprint *ZT_Locator_fingerprint(const ZT_Locator *loc); +/** + * Get a locator's timestamp + * + * @param loc Locator to query + * @return Locator timestamp in milliseconds since epoch + */ +ZT_SDK_API int64_t ZT_Locator_timestamp(const ZT_Locator *loc); + /** * Get the number of endpoints in this locator * diff --git a/node/Endpoint.cpp b/node/Endpoint.cpp index 3b5b7bd81..e8800d240 100644 --- a/node/Endpoint.cpp +++ b/node/Endpoint.cpp @@ -16,7 +16,7 @@ namespace ZeroTier { -void Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept +char *Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept { static const char *const s_endpointTypeChars = ZT_ENDPOINT_TYPE_CHAR_INDEX; @@ -30,14 +30,14 @@ void Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept break; case ZT_ENDPOINT_TYPE_ZEROTIER: s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER]; - s[1] = '/'; + s[1] = '-'; zt().toString(s + 2); break; case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_BLUETOOTH: s[0] = s_endpointTypeChars[this->type]; - s[1] = '/'; + s[1] = '-'; eth().toString(s + 2); break; case ZT_ENDPOINT_TYPE_IP: @@ -45,10 +45,12 @@ void Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_HTTP2: s[0] = s_endpointTypeChars[this->type]; - s[1] = '/'; + s[1] = '-'; ip().toString(s + 2); break; } + + return s; } bool Endpoint::fromString(const char *s) noexcept @@ -57,7 +59,7 @@ bool Endpoint::fromString(const char *s) noexcept if ((!s) || (!*s)) return true; - const char *start = strchr(s, '/'); + const char *start = strchr(s, '-'); if (start) { char tmp[16]; for (unsigned int i = 0;i < 15;++i) { @@ -248,3 +250,26 @@ bool Endpoint::operator<(const Endpoint &ep) const noexcept } } // namespace ZeroTier + +extern "C" { + +char *ZT_Endpoint_toString( + const ZT_Endpoint *ep, + char *buf, + int capacity) +{ + if ((!ep) || (!buf) || (capacity < ZT_ENDPOINT_STRING_SIZE_MAX)) + return nullptr; + return reinterpret_cast(ep)->toString(buf); +} + +int ZT_Endpoint_fromString( + ZT_Endpoint *ep, + const char *str) +{ + if ((!ep) || (!str)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + return reinterpret_cast(ep)->fromString(str) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; +} + +} // C API diff --git a/node/Endpoint.hpp b/node/Endpoint.hpp index 9d395c523..b1857ee92 100644 --- a/node/Endpoint.hpp +++ b/node/Endpoint.hpp @@ -147,14 +147,9 @@ public: ZT_INLINE Fingerprint zt() const noexcept { return Fingerprint(this->value.fp); } - void toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept; + char *toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept; - ZT_INLINE String toString() const - { - char tmp[ZT_ENDPOINT_STRING_SIZE_MAX]; - toString(tmp); - return String(tmp); - } + ZT_INLINE String toString() const { char tmp[ZT_ENDPOINT_STRING_SIZE_MAX]; return String(toString(tmp)); } bool fromString(const char *s) noexcept; diff --git a/node/Fingerprint.hpp b/node/Fingerprint.hpp index a98470c7e..6fc4eaf31 100644 --- a/node/Fingerprint.hpp +++ b/node/Fingerprint.hpp @@ -54,7 +54,7 @@ public: { Address(this->address).toString(s); if (haveHash()) { - s[ZT_ADDRESS_LENGTH_HEX] = '/'; + s[ZT_ADDRESS_LENGTH_HEX] = '-'; Utils::b32e(this->hash, ZT_FINGERPRINT_HASH_SIZE, s + (ZT_ADDRESS_LENGTH_HEX + 1), ZT_FINGERPRINT_STRING_SIZE_MAX - (ZT_ADDRESS_LENGTH_HEX + 1)); } } diff --git a/node/Locator.cpp b/node/Locator.cpp index 6b9344aa5..aaa4dfb14 100644 --- a/node/Locator.cpp +++ b/node/Locator.cpp @@ -53,10 +53,33 @@ bool Locator::verify(const Identity &id) const noexcept const unsigned int signlen = marshal(signdata, true); return id.verify(signdata, signlen, m_signature.data(), m_signature.size()); } - } catch ( ... ) {} // fail verify on any unexpected exception + } catch (...) {} // fail verify on any unexpected exception return false; } +char *Locator::toString(char s[ZT_LOCATOR_STRING_SIZE_MAX]) const noexcept +{ + static_assert(ZT_LOCATOR_STRING_SIZE_MAX > ((((ZT_LOCATOR_MARSHAL_SIZE_MAX / 5) + 1) * 8) + ZT_ADDRESS_LENGTH_HEX + 1), "overflow"); + uint8_t bin[ZT_LOCATOR_MARSHAL_SIZE_MAX]; + Address(m_signer.address).toString(s); + s[ZT_ADDRESS_LENGTH_HEX] = '@'; + Utils::b32e(bin, marshal(bin, false), s + (ZT_ADDRESS_LENGTH_HEX + 1), ZT_LOCATOR_STRING_SIZE_MAX - (ZT_ADDRESS_LENGTH_HEX + 1)); + return s; +} + +bool Locator::fromString(const char *s) noexcept +{ + if (!s) + return false; + if (strlen(s) < (ZT_ADDRESS_LENGTH_HEX + 1)) + return false; + uint8_t bin[ZT_LOCATOR_MARSHAL_SIZE_MAX]; + const int bl = Utils::b32d(s + (ZT_ADDRESS_LENGTH_HEX + 1), bin, ZT_LOCATOR_MARSHAL_SIZE_MAX); + if ((bl <= 0) || (bl > ZT_LOCATOR_MARSHAL_SIZE_MAX)) + return false; + return unmarshal(bin, bl) > 0; +} + int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], const bool excludeSignature) const noexcept { Utils::storeBigEndian(data, (uint64_t) m_ts); @@ -96,7 +119,7 @@ int Locator::unmarshal(const uint8_t *data, const int len) noexcept m_ts = (int64_t) Utils::loadBigEndian(data); int p = 8; - int l = m_signer.unmarshal(data + p,len - p); + int l = m_signer.unmarshal(data + p, len - p); if (l <= 0) return -1; p += l; @@ -123,7 +146,7 @@ int Locator::unmarshal(const uint8_t *data, const int len) noexcept return -1; const unsigned int siglen = Utils::loadBigEndian(data + p); p += 2; - if (unlikely((siglen > ZT_SIGNATURE_BUFFER_SIZE) || ((p + (int)siglen) > len))) + if (unlikely((siglen > ZT_SIGNATURE_BUFFER_SIZE) || ((p + (int) siglen) > len))) return -1; m_signature.unsafeSetSize(siglen); Utils::copy(m_signature.data(), data + p, siglen); @@ -148,9 +171,25 @@ ZT_Locator *ZT_Locator_create( if ((ts <= 0) || (!endpoints) || (endpointCount == 0) || (!signer)) return nullptr; ZeroTier::Locator *loc = new ZeroTier::Locator(); - for(unsigned int i=0;iadd(reinterpret_cast(endpoints)[i]); - if (!loc->sign(ts,*reinterpret_cast(signer))) { + if (!loc->sign(ts, *reinterpret_cast(signer))) { + delete loc; + return nullptr; + } + return reinterpret_cast(loc); + } catch (...) { + return nullptr; + } +} + +ZT_Locator *ZT_Locator_fromString(const char *str) +{ + try { + if (!str) + return nullptr; + ZeroTier::Locator *loc = new ZeroTier::Locator(); + if (!loc->fromString(str)) { delete loc; return nullptr; } @@ -168,45 +207,62 @@ ZT_Locator *ZT_Locator_unmarshal( if ((!data) || (len == 0)) return nullptr; ZeroTier::Locator *loc = new ZeroTier::Locator(); - if (loc->unmarshal(reinterpret_cast(data),(int)len) <= 0) { + if (loc->unmarshal(reinterpret_cast(data), (int) len) <= 0) { delete loc; return nullptr; } return reinterpret_cast(loc); - } catch ( ... ) { + } catch (...) { return nullptr; } } -int ZT_Locator_marshal(const ZT_Locator *loc,void *buf,unsigned int bufSize) +int ZT_Locator_marshal(const ZT_Locator *loc, void *buf, unsigned int bufSize) { if ((!loc) || (bufSize < ZT_LOCATOR_MARSHAL_SIZE_MAX)) return -1; - return reinterpret_cast(loc)->marshal(reinterpret_cast(buf),(int)bufSize); + return reinterpret_cast(loc)->marshal(reinterpret_cast(buf), (int) bufSize); +} + +char *ZT_Locator_toString( + const ZT_Locator *loc, + char *buf, + int capacity) +{ + if ((!loc) || (capacity < ZT_LOCATOR_STRING_SIZE_MAX)) + return nullptr; + return reinterpret_cast(loc)->toString(buf); } const ZT_Fingerprint *ZT_Locator_fingerprint(const ZT_Locator *loc) { if (!loc) return nullptr; - return (ZT_Fingerprint *)(&(reinterpret_cast(loc)->signer())); + return (ZT_Fingerprint *) (&(reinterpret_cast(loc)->signer())); +} + +int64_t ZT_Locator_timestamp(const ZT_Locator *loc) +{ + if (!loc) + return 0; + return reinterpret_cast(loc)->timestamp(); } unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc) { - return (loc) ? (unsigned int)(reinterpret_cast(loc)->endpoints().size()) : 0; + return (loc) ? (unsigned int) (reinterpret_cast(loc)->endpoints().size()) : 0; } -const ZT_Endpoint *ZT_Locator_endpoint(const ZT_Locator *loc,const unsigned int ep) +const ZT_Endpoint *ZT_Locator_endpoint(const ZT_Locator *loc, const unsigned int ep) { if (!loc) return nullptr; - if (ep >= (unsigned int)(reinterpret_cast(loc)->endpoints().size())) + if (ep >= (unsigned int) (reinterpret_cast(loc)->endpoints().size())) return nullptr; return reinterpret_cast(&(reinterpret_cast(loc)->endpoints()[ep])); } -int ZT_Locator_verify(const ZT_Locator *loc,const ZT_Identity *signer) +int ZT_Locator_verify(const ZT_Locator *loc, const ZT_Identity *signer) { if ((!loc) || (!signer)) return 0; diff --git a/node/Locator.hpp b/node/Locator.hpp index 57e9976e1..da26f8bda 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -24,6 +24,7 @@ #define ZT_LOCATOR_MAX_ENDPOINTS 8 #define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + ZT_FINGERPRINT_MARSHAL_SIZE + 2 + (ZT_LOCATOR_MAX_ENDPOINTS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE) +#define ZT_LOCATOR_STRING_SIZE_MAX 4096 namespace ZeroTier { @@ -105,14 +106,27 @@ public: */ bool verify(const Identity &id) const noexcept; + /** + * Convert this locator to a string + * + * @param s String buffer + * @return Pointer to buffer + */ + char *toString(char s[ZT_LOCATOR_STRING_SIZE_MAX]) const noexcept; + + /** + * Decode a string format locator + * + * @param s Locator from toString() + * @return True if format was valid + */ + bool fromString(const char *s) noexcept; + explicit ZT_INLINE operator bool() const noexcept { return m_ts > 0; } - static constexpr int marshalSizeMax() noexcept - { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } - + static constexpr int marshalSizeMax() noexcept { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept; - int unmarshal(const uint8_t *data, int len) noexcept; private: