Move some toString/fromString to C++ since any ZT code base would need it, and stub out the controller commands.

This commit is contained in:
Adam Ierymenko 2020-05-30 11:42:59 -07:00
parent a5390b1bc8
commit 1970dab13d
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
17 changed files with 414 additions and 67 deletions

View file

@ -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) {
}

View file

@ -18,7 +18,6 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
// Help dumps help to stdout
func Help() { func Help() {
fmt.Printf(`ZeroTier Network Hypervisor Service Version %d.%d.%d fmt.Printf(`ZeroTier Network Hypervisor Service Version %d.%d.%d
(c)2013-2020 ZeroTier, Inc. (c)2013-2020 ZeroTier, Inc.
@ -38,11 +37,11 @@ Commands:
service Start as service service Start as service
status Show node status, identity, and config status Show node status, identity, and config
peers List all VL1 peers peers List all VL1 peers
join <network ID> [fingerprint] Join a virtual network join <network> [fingerprint] Join a virtual network
leave <network ID> Leave a virtual network leave <network> Leave a virtual network
networks List VL2 virtual networks networks List VL2 virtual networks
network <network ID> Show verbose network info network <network> Show verbose network info
set <network ID> [option] [value] Get or set a network config option set <network> [option] [value] Get or set a network config option
manageips <boolean> Is IP management allowed? manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed? manageroutes <boolean> Is route management allowed?
globalips <boolean> Allow assignment of global IPs? globalips <boolean> Allow assignment of global IPs?
@ -62,11 +61,20 @@ Commands:
verify <identity> <file> <sig> Verify a signature verify <identity> <file> <sig> Verify a signature
locator <command> [args] Locator management commands locator <command> [args] Locator management commands
new <identity> <address> [...] Create and sign a new locator new <identity> <address> [...] Create and sign a new locator
show <locator> [<identity>] Show locator information show <locator> [identity] Show locator information
roots List root peers root [command] Root management commands
addroot <identity> <locator> Add a root or update its locator list List root peers (same as no command)
addroot <url> Add or update roots from a URL add <identity> <locator> Add or manually update a root
removeroot <address> Remove a peer from the root list add <url> Add or update root(s) from a URL
remove <address> Un-designate a peer as a root
controller <command> [option] Local controller management commands
networks List networks run by local controller
new Create a new network
set <network> [setting] [value] Show or modify network settings
members <network> List members of a network
member <network> [setting] [value] Show or modify member level settings
auth <address|fingerprint> Authorize a peer
deauth <address|fingerprint> Deauthorize a peer
The 'service' command does not exit until the service receives a signal. 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 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 Identities can be specified verbatim on the command line or as a path to
a file. This is detected automatically. a file. This is detected automatically.
`,zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) `,zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
} }

View file

@ -23,7 +23,6 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
// Identity command
func Identity(args []string) { func Identity(args []string) {
if len(args) > 0 { if len(args) > 0 {
switch args[0] { switch args[0] {

View file

@ -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<len(args);i++ {
ep, _ := zerotier.NewEndpointFromString(args[i])
if ep != nil {
eps = append(eps, *ep)
}
}
loc, err := zerotier.NewLocator(zerotier.TimeMs(),eps,id)
if err != nil {
fmt.Printf("ERROR: unable to create or sign locator: %s\n",err.Error())
os.Exit(1)
}
fmt.Println(loc.String())
os.Exit(0)
}
case "show":
if len(args) > 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)
}

View file

@ -99,23 +99,43 @@ func readIdentity(s string) *zerotier.Identity {
} }
idData, err := ioutil.ReadFile(s) idData, err := ioutil.ReadFile(s)
if err != nil { 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) os.Exit(1)
} }
id, err := zerotier.NewIdentityFromString(string(idData)) id, err := zerotier.NewIdentityFromString(string(idData))
if err != nil { 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) os.Exit(1)
} }
return id 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 { func networkStatusStr(status int) string {
switch status { switch status {
case zerotier.NetworkStatusNotFound: case zerotier.NetworkStatusNotFound:
return "NOTFOUND" return "NOTFOUND"
case zerotier.NetworkStatusAccessDenied: case zerotier.NetworkStatusAccessDenied:
return "DENIED" return "ACCESSDENIED"
case zerotier.NetworkStatusRequestConfiguration: case zerotier.NetworkStatusRequestConfiguration:
return "UPDATING" return "UPDATING"
case zerotier.NetworkStatusOK: case zerotier.NetworkStatusOK:

View file

@ -21,7 +21,6 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
// Network CLI command
func Network(basePath, authToken string, args []string, jsonOutput bool) { func Network(basePath, authToken string, args []string, jsonOutput bool) {
if len(args) != 1 { if len(args) != 1 {
Help() Help()

View file

@ -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) {
}

View file

@ -127,13 +127,6 @@ func main() {
case "peers", "listpeers", "lspeers": case "peers", "listpeers", "lspeers":
authTokenRequired(authToken) authTokenRequired(authToken)
cli.Peers(basePath, authToken, cmdArgs, *jflag, false) 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": case "join":
authTokenRequired(authToken) authTokenRequired(authToken)
cli.Join(basePath, authToken, cmdArgs) cli.Join(basePath, authToken, cmdArgs)
@ -151,8 +144,19 @@ func main() {
cli.Set(basePath, authToken, cmdArgs) cli.Set(basePath, authToken, cmdArgs)
case "identity": case "identity":
cli.Identity(cmdArgs) 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() cli.Help()
os.Exit(1) os.Exit(1)
} }

View file

@ -5,11 +5,12 @@ package zerotier
// static inline uint64_t _getAddress(const ZT_Endpoint *ep) { return ep->value.fp.address; } // 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 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 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 "C"
import ( import (
"encoding/json" "encoding/json"
"fmt" "strings"
"unsafe" "unsafe"
) )
@ -29,6 +30,42 @@ type Endpoint struct {
cep C.ZT_Endpoint 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. // Type returns this endpoint's type.
func (ep *Endpoint) Type() int { func (ep *Endpoint) Type() int {
return int(ep.cep._type) return int(ep.cep._type)
@ -77,15 +114,12 @@ func (ep *Endpoint) MAC() MAC {
} }
func (ep *Endpoint) String() string { func (ep *Endpoint) String() string {
switch ep.cep._type { var buf [4096]byte
case EndpointTypeZeroTier: cs := C.ZT_Endpoint_toString(&ep.cep,unsafe.Pointer(&buf[0]),4096)
return fmt.Sprintf("%d/%s", ep.Type(), ep.Fingerprint().String()) if cs == nil {
case EndpointTypeEthernet, EndpointTypeWifiDirect, EndpointTypeBluetooth: return "0"
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())
} }
return fmt.Sprintf("%d", ep.Type()) return C.GoString(cs)
} }
func (ep *Endpoint) MarshalJSON() ([]byte, error) { func (ep *Endpoint) MarshalJSON() ([]byte, error) {
@ -94,6 +128,15 @@ func (ep *Endpoint) MarshalJSON() ([]byte, error) {
} }
func (ep *Endpoint) UnmarshalJSON(j []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 return nil
} }

View file

@ -32,7 +32,7 @@ func NewFingerprintFromString(fps string) (*Fingerprint, error) {
if len(fps) < AddressStringLength { if len(fps) < AddressStringLength {
return nil, ErrInvalidZeroTierAddress return nil, ErrInvalidZeroTierAddress
} }
ss := strings.Split(fps, "/") ss := strings.Split(fps, "-")
if len(ss) < 1 || len(ss) > 2 { if len(ss) < 1 || len(ss) > 2 {
return nil, ErrInvalidParameter return nil, ErrInvalidParameter
} }
@ -67,7 +67,7 @@ func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint {
func (fp *Fingerprint) String() string { func (fp *Fingerprint) String() string {
if len(fp.Hash) == 48 { 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() return fp.Address.String()
} }

View file

@ -64,9 +64,26 @@ func NewLocatorFromBytes(lb []byte) (*Locator, error) {
return goloc, nil 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. // 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. // 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) cfp := C.ZT_Locator_fingerprint(loc.cl)
if uintptr(unsafe.Pointer(cfp)) == 0 { if uintptr(unsafe.Pointer(cfp)) == 0 {
err = ErrInternal err = ErrInternal
@ -84,3 +101,14 @@ func (loc *Locator) GetInfo(id *Identity) (fp *Fingerprint, eps []Endpoint, vali
} }
return 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 ""
}

View file

@ -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 * Create and sign a new locator
* *
@ -2149,6 +2160,14 @@ ZT_SDK_API ZT_Locator *ZT_Locator_unmarshal(
const void *data, const void *data,
unsigned int len); 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 * 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); 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. * 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); 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 * Get the number of endpoints in this locator
* *

View file

@ -16,7 +16,7 @@
namespace ZeroTier { 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; 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; break;
case ZT_ENDPOINT_TYPE_ZEROTIER: case ZT_ENDPOINT_TYPE_ZEROTIER:
s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER]; s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER];
s[1] = '/'; s[1] = '-';
zt().toString(s + 2); zt().toString(s + 2);
break; break;
case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_ETHERNET:
case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_WIFI_DIRECT:
case ZT_ENDPOINT_TYPE_BLUETOOTH: case ZT_ENDPOINT_TYPE_BLUETOOTH:
s[0] = s_endpointTypeChars[this->type]; s[0] = s_endpointTypeChars[this->type];
s[1] = '/'; s[1] = '-';
eth().toString(s + 2); eth().toString(s + 2);
break; break;
case ZT_ENDPOINT_TYPE_IP: 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_TCP:
case ZT_ENDPOINT_TYPE_IP_HTTP2: case ZT_ENDPOINT_TYPE_IP_HTTP2:
s[0] = s_endpointTypeChars[this->type]; s[0] = s_endpointTypeChars[this->type];
s[1] = '/'; s[1] = '-';
ip().toString(s + 2); ip().toString(s + 2);
break; break;
} }
return s;
} }
bool Endpoint::fromString(const char *s) noexcept bool Endpoint::fromString(const char *s) noexcept
@ -57,7 +59,7 @@ bool Endpoint::fromString(const char *s) noexcept
if ((!s) || (!*s)) if ((!s) || (!*s))
return true; return true;
const char *start = strchr(s, '/'); const char *start = strchr(s, '-');
if (start) { if (start) {
char tmp[16]; char tmp[16];
for (unsigned int i = 0;i < 15;++i) { for (unsigned int i = 0;i < 15;++i) {
@ -248,3 +250,26 @@ bool Endpoint::operator<(const Endpoint &ep) const noexcept
} }
} // namespace ZeroTier } // 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<const ZeroTier::Endpoint *>(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<ZeroTier::Endpoint *>(ep)->fromString(str) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER;
}
} // C API

View file

@ -147,14 +147,9 @@ public:
ZT_INLINE Fingerprint zt() const noexcept ZT_INLINE Fingerprint zt() const noexcept
{ return Fingerprint(this->value.fp); } { 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 ZT_INLINE String toString() const { char tmp[ZT_ENDPOINT_STRING_SIZE_MAX]; return String(toString(tmp)); }
{
char tmp[ZT_ENDPOINT_STRING_SIZE_MAX];
toString(tmp);
return String(tmp);
}
bool fromString(const char *s) noexcept; bool fromString(const char *s) noexcept;

View file

@ -54,7 +54,7 @@ public:
{ {
Address(this->address).toString(s); Address(this->address).toString(s);
if (haveHash()) { 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)); Utils::b32e(this->hash, ZT_FINGERPRINT_HASH_SIZE, s + (ZT_ADDRESS_LENGTH_HEX + 1), ZT_FINGERPRINT_STRING_SIZE_MAX - (ZT_ADDRESS_LENGTH_HEX + 1));
} }
} }

View file

@ -53,10 +53,33 @@ bool Locator::verify(const Identity &id) const noexcept
const unsigned int signlen = marshal(signdata, true); const unsigned int signlen = marshal(signdata, true);
return id.verify(signdata, signlen, m_signature.data(), m_signature.size()); 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; 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 int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], const bool excludeSignature) const noexcept
{ {
Utils::storeBigEndian<uint64_t>(data, (uint64_t) m_ts); Utils::storeBigEndian<uint64_t>(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<uint64_t>(data); m_ts = (int64_t) Utils::loadBigEndian<uint64_t>(data);
int p = 8; int p = 8;
int l = m_signer.unmarshal(data + p,len - p); int l = m_signer.unmarshal(data + p, len - p);
if (l <= 0) if (l <= 0)
return -1; return -1;
p += l; p += l;
@ -123,7 +146,7 @@ int Locator::unmarshal(const uint8_t *data, const int len) noexcept
return -1; return -1;
const unsigned int siglen = Utils::loadBigEndian<uint16_t>(data + p); const unsigned int siglen = Utils::loadBigEndian<uint16_t>(data + p);
p += 2; 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; return -1;
m_signature.unsafeSetSize(siglen); m_signature.unsafeSetSize(siglen);
Utils::copy(m_signature.data(), data + p, 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)) if ((ts <= 0) || (!endpoints) || (endpointCount == 0) || (!signer))
return nullptr; return nullptr;
ZeroTier::Locator *loc = new ZeroTier::Locator(); ZeroTier::Locator *loc = new ZeroTier::Locator();
for(unsigned int i=0;i<endpointCount;++i) for (unsigned int i = 0;i < endpointCount;++i)
loc->add(reinterpret_cast<const ZeroTier::Endpoint *>(endpoints)[i]); loc->add(reinterpret_cast<const ZeroTier::Endpoint *>(endpoints)[i]);
if (!loc->sign(ts,*reinterpret_cast<const ZeroTier::Identity *>(signer))) { if (!loc->sign(ts, *reinterpret_cast<const ZeroTier::Identity *>(signer))) {
delete loc;
return nullptr;
}
return reinterpret_cast<ZT_Locator *>(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; delete loc;
return nullptr; return nullptr;
} }
@ -168,45 +207,62 @@ ZT_Locator *ZT_Locator_unmarshal(
if ((!data) || (len == 0)) if ((!data) || (len == 0))
return nullptr; return nullptr;
ZeroTier::Locator *loc = new ZeroTier::Locator(); ZeroTier::Locator *loc = new ZeroTier::Locator();
if (loc->unmarshal(reinterpret_cast<const uint8_t *>(data),(int)len) <= 0) { if (loc->unmarshal(reinterpret_cast<const uint8_t *>(data), (int) len) <= 0) {
delete loc; delete loc;
return nullptr; return nullptr;
} }
return reinterpret_cast<ZT_Locator *>(loc); return reinterpret_cast<ZT_Locator *>(loc);
} catch ( ... ) { } catch (...) {
return nullptr; 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)) if ((!loc) || (bufSize < ZT_LOCATOR_MARSHAL_SIZE_MAX))
return -1; return -1;
return reinterpret_cast<const ZeroTier::Locator *>(loc)->marshal(reinterpret_cast<uint8_t *>(buf),(int)bufSize); return reinterpret_cast<const ZeroTier::Locator *>(loc)->marshal(reinterpret_cast<uint8_t *>(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<const ZeroTier::Locator *>(loc)->toString(buf);
} }
const ZT_Fingerprint *ZT_Locator_fingerprint(const ZT_Locator *loc) const ZT_Fingerprint *ZT_Locator_fingerprint(const ZT_Locator *loc)
{ {
if (!loc) if (!loc)
return nullptr; return nullptr;
return (ZT_Fingerprint *)(&(reinterpret_cast<const ZeroTier::Locator *>(loc)->signer())); return (ZT_Fingerprint *) (&(reinterpret_cast<const ZeroTier::Locator *>(loc)->signer()));
}
int64_t ZT_Locator_timestamp(const ZT_Locator *loc)
{
if (!loc)
return 0;
return reinterpret_cast<const ZeroTier::Locator *>(loc)->timestamp();
} }
unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc) unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc)
{ {
return (loc) ? (unsigned int)(reinterpret_cast<const ZeroTier::Locator *>(loc)->endpoints().size()) : 0; return (loc) ? (unsigned int) (reinterpret_cast<const ZeroTier::Locator *>(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) if (!loc)
return nullptr; return nullptr;
if (ep >= (unsigned int)(reinterpret_cast<const ZeroTier::Locator *>(loc)->endpoints().size())) if (ep >= (unsigned int) (reinterpret_cast<const ZeroTier::Locator *>(loc)->endpoints().size()))
return nullptr; return nullptr;
return reinterpret_cast<const ZT_Endpoint *>(&(reinterpret_cast<const ZeroTier::Locator *>(loc)->endpoints()[ep])); return reinterpret_cast<const ZT_Endpoint *>(&(reinterpret_cast<const ZeroTier::Locator *>(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)) if ((!loc) || (!signer))
return 0; return 0;

View file

@ -24,6 +24,7 @@
#define ZT_LOCATOR_MAX_ENDPOINTS 8 #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_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 { namespace ZeroTier {
@ -105,14 +106,27 @@ public:
*/ */
bool verify(const Identity &id) const noexcept; 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 explicit ZT_INLINE operator bool() const noexcept
{ return m_ts > 0; } { return m_ts > 0; }
static constexpr int marshalSizeMax() noexcept static constexpr int marshalSizeMax() noexcept { return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
{ return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept; int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept;
int unmarshal(const uint8_t *data, int len) noexcept; int unmarshal(const uint8_t *data, int len) noexcept;
private: private: