A bunch more go plumbing.

This commit is contained in:
Adam Ierymenko 2020-06-04 16:03:11 -07:00
parent 1b2a4f00f2
commit 9babfcb9b6
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 128 additions and 56 deletions

View file

@ -193,13 +193,13 @@ func readLocator(s string) *zerotier.Locator {
func networkStatusStr(status int) string { func networkStatusStr(status int) string {
switch status { switch status {
case zerotier.NetworkStatusNotFound: case zerotier.NetworkStatusNotFound:
return "NOTFOUND" return "not-found"
case zerotier.NetworkStatusAccessDenied: case zerotier.NetworkStatusAccessDenied:
return "ACCESSDENIED" return "access-denied"
case zerotier.NetworkStatusRequestConfiguration: case zerotier.NetworkStatusRequestingConfiguration:
return "UPDATING" return "updating"
case zerotier.NetworkStatusOK: case zerotier.NetworkStatusOK:
return "OK" return "ok"
} }
return "???" return "???"
} }

View file

@ -39,7 +39,8 @@ func NewAddressFromBytes(b []byte) (Address, error) {
return Address((uint64(b[0]) << 32) | (uint64(b[1]) << 24) | (uint64(b[2]) << 16) | (uint64(b[3]) << 8) | uint64(b[4])), nil return Address((uint64(b[0]) << 32) | (uint64(b[1]) << 24) | (uint64(b[2]) << 16) | (uint64(b[3]) << 8) | uint64(b[4])), nil
} }
// Copy this address to a byte array, which must be 5 bytes in length or this will panic. // CopyTo writes this address to five bytes.
// If b cannot store five bytes this will panic.
func (a Address) CopyTo(b []byte) { func (a Address) CopyTo(b []byte) {
_ = b[4] _ = b[4]
b[0] = byte(a >> 32) b[0] = byte(a >> 32)
@ -52,7 +53,7 @@ func (a Address) CopyTo(b []byte) {
// IsReserved returns true if this address is reserved and therefore is not valid for a real node. // IsReserved returns true if this address is reserved and therefore is not valid for a real node.
func (a Address) IsReserved() bool { return a == 0 || (a>>32) == 0xff } func (a Address) IsReserved() bool { return a == 0 || (a>>32) == 0xff }
// String returns this address's 10-digit hex identifier // String returns this address's 10-digit hex identifier.
func (a Address) String() string { func (a Address) String() string {
return fmt.Sprintf("%.10x", uint64(a)) return fmt.Sprintf("%.10x", uint64(a))
} }

View file

@ -473,7 +473,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
} }
var nw APINetwork var nw APINetwork
if apiReadObj(out, req, &nw) == nil { if apiReadObj(out, req, &nw) == nil {
n := node.GetNetwork(nw.ID) n := node.Network(nw.ID)
if n == nil { if n == nil {
if nw.ControllerFingerprint != nil && nw.ControllerFingerprint.Address != nw.ID.Controller() { if nw.ControllerFingerprint != nil && nw.ControllerFingerprint.Address != nw.ID.Controller() {
_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"fingerprint's address does not match what should be the controller's address"}) _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"fingerprint's address does not match what should be the controller's address"})

View file

@ -24,11 +24,20 @@ import (
"unsafe" "unsafe"
) )
// FingerprintHashSize is the length of a fingerprint hash in bytes.
const FingerprintHashSize = 48
// Fingerprint bundles an address with an optional SHA384 full hash of the identity's key(s).
type Fingerprint struct { type Fingerprint struct {
Address Address Address Address
Hash []byte Hash []byte
} }
// NewFingerprintFromString decodes a string-format fingerprint.
// A fingerprint has the format address-hash, where address is a 10-digit
// ZeroTier address and a hash is a base32-encoded SHA384 hash. Fingerprints
// can be missing the hash in which case they are represented the same as
// an Address and the hash field will be nil.
func NewFingerprintFromString(fps string) (*Fingerprint, error) { func NewFingerprintFromString(fps string) (*Fingerprint, error) {
if len(fps) < AddressStringLength { if len(fps) < AddressStringLength {
return nil, ErrInvalidZeroTierAddress return nil, ErrInvalidZeroTierAddress
@ -66,22 +75,28 @@ func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint {
return &fp return &fp
} }
// String returns an address or a full address-hash depenting on whether a hash is present.
func (fp *Fingerprint) String() string { func (fp *Fingerprint) String() string {
if len(fp.Hash) == 48 { if len(fp.Hash) == FingerprintHashSize {
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()
} }
// Equals test for full equality with another fingerprint (including hash).
func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool { func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool {
return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:]) return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:])
} }
func (fp *Fingerprint) cFingerprint() *C.ZT_Fingerprint { // BestSpecificityEquals compares either just the addresses or also the hashes if both are present.
var apifp C.ZT_Fingerprint func (fp *Fingerprint) BestSpecificityEquals(fp2 *Fingerprint) bool {
apifp.address = C.uint64_t(fp.Address) if fp2 == nil || fp.Address != fp2.Address {
copy((*[48]byte)(unsafe.Pointer(&apifp.hash[0]))[:], fp.Hash[:]) return false
return &apifp }
if len(fp.Hash) == FingerprintHashSize && len(fp2.Hash) == FingerprintHashSize {
return bytes.Equal(fp.Hash, fp2.Hash)
}
return true
} }
func (fp *Fingerprint) MarshalJSON() ([]byte, error) { func (fp *Fingerprint) MarshalJSON() ([]byte, error) {
@ -99,3 +114,10 @@ func (fp *Fingerprint) UnmarshalJSON(j []byte) error {
fp.Hash = fp2.Hash fp.Hash = fp2.Hash
return err return err
} }
func (fp *Fingerprint) cFingerprint() *C.ZT_Fingerprint {
var apifp C.ZT_Fingerprint
apifp.address = C.uint64_t(fp.Address)
copy((*[48]byte)(unsafe.Pointer(&apifp.hash[0]))[:], fp.Hash[:])
return &apifp
}

View file

@ -89,7 +89,7 @@ func (id *Identity) initCIdentityPtr() bool {
return true return true
} }
// NewIdentity generates a new identity of the selected type // NewIdentity generates a new identity of the selected type.
func NewIdentity(identityType int) (*Identity, error) { func NewIdentity(identityType int) (*Identity, error) {
switch identityType { switch identityType {
case C.ZT_IDENTITY_TYPE_C25519: case C.ZT_IDENTITY_TYPE_C25519:
@ -160,7 +160,7 @@ func NewIdentityFromString(s string) (*Identity, error) {
return id, nil return id, nil
} }
// Address returns this identity's address // Address returns this identity's address.
func (id *Identity) Address() Address { return id.address } func (id *Identity) Address() Address { return id.address }
// HasPrivate returns true if this identity has its own private portion. // HasPrivate returns true if this identity has its own private portion.
@ -172,7 +172,8 @@ func (id *Identity) Fingerprint() *Fingerprint {
return newFingerprintFromCFingerprint(C.ZT_Identity_fingerprint(id.cid)) return newFingerprintFromCFingerprint(C.ZT_Identity_fingerprint(id.cid))
} }
// PrivateKeyString returns the full identity.secret if the private key is set, or an empty string if no private key is set. // PrivateKeyString returns the full identity.secret if the private key is set,
// or an empty string if no private key is set.
func (id *Identity) PrivateKeyString() string { func (id *Identity) PrivateKeyString() string {
switch id.idtype { switch id.idtype {
case IdentityTypeC25519: case IdentityTypeC25519:
@ -253,12 +254,10 @@ func (id *Identity) Equals(id2 *Identity) bool {
return id.address == id2.address && id.idtype == id2.idtype && bytes.Equal(id.publicKey, id2.publicKey) && bytes.Equal(id.privateKey, id2.privateKey) return id.address == id2.address && id.idtype == id2.idtype && bytes.Equal(id.publicKey, id2.publicKey) && bytes.Equal(id.privateKey, id2.privateKey)
} }
// MarshalJSON marshals this Identity in its string format (private key is never included)
func (id *Identity) MarshalJSON() ([]byte, error) { func (id *Identity) MarshalJSON() ([]byte, error) {
return []byte("\"" + id.String() + "\""), nil return []byte("\"" + id.String() + "\""), nil
} }
// UnmarshalJSON unmarshals this Identity from a string
func (id *Identity) UnmarshalJSON(j []byte) error { func (id *Identity) UnmarshalJSON(j []byte) error {
var s string var s string
err := json.Unmarshal(j, &s) err := json.Unmarshal(j, &s)

View file

@ -155,7 +155,7 @@ func newNetwork(node *Node, id NetworkID, t Tap) (*Network, error) {
config: NetworkConfig{ config: NetworkConfig{
ID: id, ID: id,
MAC: m, MAC: m,
Status: NetworkStatusRequestConfiguration, Status: NetworkStatusRequestingConfiguration,
Type: NetworkTypePrivate, Type: NetworkTypePrivate,
MTU: int(defaultVirtualNetworkMTU), MTU: int(defaultVirtualNetworkMTU),
}, },

View file

@ -47,12 +47,14 @@ var nullLogger = log.New(ioutil.Discard, "", 0)
const ( const (
NetworkIDStringLength = 16 NetworkIDStringLength = 16
NEtworkIDLength = 8
AddressStringLength = 10 AddressStringLength = 10
AddressLength = 5
NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION NetworkStatusRequestingConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
NetworkStatusOK int = C.ZT_NETWORK_STATUS_OK NetworkStatusOK int = C.ZT_NETWORK_STATUS_OK
NetworkStatusAccessDenied int = C.ZT_NETWORK_STATUS_ACCESS_DENIED NetworkStatusAccessDenied int = C.ZT_NETWORK_STATUS_ACCESS_DENIED
NetworkStatusNotFound int = C.ZT_NETWORK_STATUS_NOT_FOUND NetworkStatusNotFound int = C.ZT_NETWORK_STATUS_NOT_FOUND
NetworkTypePrivate int = C.ZT_NETWORK_TYPE_PRIVATE NetworkTypePrivate int = C.ZT_NETWORK_TYPE_PRIVATE
NetworkTypePublic int = C.ZT_NETWORK_TYPE_PUBLIC NetworkTypePublic int = C.ZT_NETWORK_TYPE_PUBLIC
@ -62,7 +64,8 @@ const (
defaultVirtualNetworkMTU = C.ZT_DEFAULT_MTU defaultVirtualNetworkMTU = C.ZT_DEFAULT_MTU
// maxCNodeRefs is the maximum number of Node instances that can be created in this process (increasing is fine) // maxCNodeRefs is the maximum number of Node instances that can be created in this process.
// This is perfectly fine to increase.
maxCNodeRefs = 8 maxCNodeRefs = 8
) )
@ -73,7 +76,10 @@ var (
CoreVersionRevision int CoreVersionRevision int
CoreVersionBuild int CoreVersionBuild int
cNodeRefs [maxCNodeRefs]*Node // cNodeRefs maps an index to a *Node
cNodeRefs [maxCNodeRefs]*Node
// cNodeRefsUsed maps an index to whether or not the corresponding cNodeRefs[] entry is used.
cNodeRefUsed [maxCNodeRefs]uint32 cNodeRefUsed [maxCNodeRefs]uint32
) )
@ -92,7 +98,9 @@ type Node struct {
// Time this node was created // Time this node was created
startupTime int64 startupTime int64
// an arbitrary uintptr given to the core as its pointer back to Go's Node instance // an arbitrary uintptr given to the core as its pointer back to Go's Node instance.
// This is an index in the cNodeRefs array, which is synchronized by way of a set of
// used/free booleans accessed atomically.
cPtr uintptr cPtr uintptr
// networks contains networks we have joined, and networksByMAC by their local MAC address // networks contains networks we have joined, and networksByMAC by their local MAC address
@ -100,13 +108,14 @@ type Node struct {
networksByMAC map[MAC]*Network // locked by networksLock networksByMAC map[MAC]*Network // locked by networksLock
networksLock sync.RWMutex networksLock sync.RWMutex
// interfaceAddresses are physical IPs assigned to the local machine (detected, not configured) // interfaceAddresses are physical IPs assigned to the local machine.
// These are the detected IPs, not those configured explicitly. They include
// both private and global IPs.
interfaceAddresses map[string]net.IP interfaceAddresses map[string]net.IP
interfaceAddressesLock sync.Mutex interfaceAddressesLock sync.Mutex
// online and running are atomic flags set to control and monitor background tasks
online uint32
running uint32 running uint32
online uint32
basePath string basePath string
peersPath string peersPath string
@ -115,20 +124,20 @@ type Node struct {
infoLogPath string infoLogPath string
errorLogPath string errorLogPath string
// localConfig is the current state of local.conf // localConfig is the current state of local.conf.
localConfig *LocalConfig localConfig *LocalConfig
previousLocalConfig *LocalConfig previousLocalConfig *LocalConfig
localConfigLock sync.RWMutex localConfigLock sync.RWMutex
// logs for information, errors, and trace output
infoLogW *sizeLimitWriter infoLogW *sizeLimitWriter
errLogW *sizeLimitWriter errLogW *sizeLimitWriter
traceLogW io.Writer traceLogW io.Writer
infoLog *log.Logger
errLog *log.Logger
traceLog *log.Logger
// gn is the GoNode instance infoLog *log.Logger
errLog *log.Logger
traceLog *log.Logger
// gn is the GoNode instance, see go/native/GoNode.hpp
gn *C.ZT_GoNode gn *C.ZT_GoNode
// zn is the underlying ZT_Node (ZeroTier::Node) instance // zn is the underlying ZT_Node (ZeroTier::Node) instance
@ -137,11 +146,12 @@ type Node struct {
// id is the identity of this node (includes private key) // id is the identity of this node (includes private key)
id *Identity id *Identity
// HTTP server instances: one for a named socket (Unix domain or Windows named pipe) and one on a local TCP socket namedSocketAPIServer *http.Server
namedSocketApiServer *http.Server tcpAPIServer *http.Server
tcpApiServer *http.Server
// runWaitGroup is used to wait for all node goroutines on shutdown // runWaitGroup is used to wait for all node goroutines on shutdown.
// Any new goroutine is tracked via this wait group so node shutdown can
// itself wait until all goroutines have exited.
runWaitGroup sync.WaitGroup runWaitGroup sync.WaitGroup
} }
@ -163,8 +173,11 @@ func NewNode(basePath string) (n *Node, err error) {
} }
} }
if cPtr < 0 { if cPtr < 0 {
return nil, errors.New("too many nodes in this instance") return nil, ErrInternal
} }
// Check and delete node reference pointer if it's non-negative. This helps
// with error handling cleanup. At the end we set cPtr to -1 to disable.
defer func() { defer func() {
if cPtr >= 0 { if cPtr >= 0 {
atomic.StoreUint32(&cNodeRefUsed[cPtr], 0) atomic.StoreUint32(&cNodeRefUsed[cPtr], 0)
@ -175,7 +188,7 @@ func NewNode(basePath string) (n *Node, err error) {
n.networks = make(map[NetworkID]*Network) n.networks = make(map[NetworkID]*Network)
n.networksByMAC = make(map[MAC]*Network) n.networksByMAC = make(map[MAC]*Network)
n.interfaceAddresses = make(map[string]net.IP) n.interfaceAddresses = make(map[string]net.IP)
n.online = 0
n.running = 1 n.running = 1
_ = os.MkdirAll(basePath, 0755) _ = os.MkdirAll(basePath, 0755)
@ -265,7 +278,7 @@ func NewNode(basePath string) (n *Node, err error) {
_ = n.localConfig.Write(n.localConfigPath) _ = n.localConfig.Write(n.localConfigPath)
} }
n.namedSocketApiServer, n.tcpApiServer, err = createAPIServer(basePath, n) n.namedSocketAPIServer, n.tcpAPIServer, err = createAPIServer(basePath, n)
if err != nil { if err != nil {
n.infoLog.Printf("FATAL: unable to start API server: %s", err.Error()) n.infoLog.Printf("FATAL: unable to start API server: %s", err.Error())
return nil, err return nil, err
@ -293,12 +306,13 @@ func NewNode(basePath string) (n *Node, err error) {
defer n.runWaitGroup.Done() defer n.runWaitGroup.Done()
lastMaintenanceRun := int64(0) lastMaintenanceRun := int64(0)
for atomic.LoadUint32(&n.running) != 0 { for atomic.LoadUint32(&n.running) != 0 {
time.Sleep(500 * time.Millisecond) time.Sleep(250 * time.Millisecond)
nowS := time.Now().Unix() nowS := time.Now().Unix()
if (nowS - lastMaintenanceRun) >= 30 { if (nowS - lastMaintenanceRun) >= 30 {
lastMaintenanceRun = nowS lastMaintenanceRun = nowS
n.runMaintenance() n.runMaintenance()
} }
time.Sleep(250 * time.Millisecond)
} }
}() }()
@ -311,11 +325,11 @@ func NewNode(basePath string) (n *Node, err error) {
// Close closes this Node and frees its underlying C++ Node structures // Close closes this Node and frees its underlying C++ Node structures
func (n *Node) Close() { func (n *Node) Close() {
if atomic.SwapUint32(&n.running, 0) != 0 { if atomic.SwapUint32(&n.running, 0) != 0 {
if n.namedSocketApiServer != nil { if n.namedSocketAPIServer != nil {
_ = n.namedSocketApiServer.Close() _ = n.namedSocketAPIServer.Close()
} }
if n.tcpApiServer != nil { if n.tcpAPIServer != nil {
_ = n.tcpApiServer.Close() _ = n.tcpAPIServer.Close()
} }
C.ZT_GoNode_delete(n.gn) C.ZT_GoNode_delete(n.gn)
@ -369,9 +383,7 @@ func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error)
} }
} }
if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort || n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax {
n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort ||
n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax {
restartRequired = true restartRequired = true
} }
@ -437,17 +449,29 @@ func (n *Node) Leave(nwid NetworkID) error {
return nil return nil
} }
func (n *Node) AddRoot(id *Identity, loc *Locator) (*Peer, error) { // AddRoot designates a peer as root, adding it if missing.
// TODO func (n *Node) AddRoot(id *Identity) (*Peer, error) {
return nil, nil if !id.initCIdentityPtr() {
return nil, ErrInvalidKey
}
rc := C.ZT_Node_addRoot(n.zn, nil, id.cid)
if rc != 0 {
return nil, ErrInvalidParameter
}
p := n.Peer(id.Fingerprint())
if p == nil {
return nil, ErrInvalidParameter
}
return p, nil
} }
// RemoveRoot un-designates a peer as root.
func (n *Node) RemoveRoot(address Address) { func (n *Node) RemoveRoot(address Address) {
C.ZT_Node_removeRoot(n.zn, nil, C.uint64_t(address)) C.ZT_Node_removeRoot(n.zn, nil, C.uint64_t(address))
} }
// GetNetwork looks up a network by ID or returns nil if not joined // Network looks up a network by ID or returns nil if not joined
func (n *Node) GetNetwork(nwid NetworkID) *Network { func (n *Node) Network(nwid NetworkID) *Network {
n.networksLock.RLock() n.networksLock.RLock()
nw := n.networks[nwid] nw := n.networks[nwid]
n.networksLock.RUnlock() n.networksLock.RUnlock()
@ -484,6 +508,32 @@ func (n *Node) Peers() []*Peer {
return peers return peers
} }
// Peer looks up a single peer by address or full fingerprint.
// The fpOrAddress parameter may be either. If it is neither nil is returned.
// A nil pointer is returned if nothing is found.
func (n *Node) Peer(fpOrAddress interface{}) *Peer {
fp, _ := fpOrAddress.(*Fingerprint)
if fp == nil {
a, _ := fpOrAddress.(*Address)
if a == nil {
return nil
}
fp = &Fingerprint{Address: *a}
}
pl := C.ZT_Node_peers(n.zn)
if pl != nil {
for i := uintptr(0); i < uintptr(pl.peerCount); i++ {
p, _ := newPeerFromCPeer((*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_Peer))))
if p != nil && p.Identity.Fingerprint().BestSpecificityEquals(fp) {
C.ZT_freeQueryResult(unsafe.Pointer(pl))
return p
}
}
C.ZT_freeQueryResult(unsafe.Pointer(pl))
}
return nil
}
// AddPeer adds a peer by explicit identity. // AddPeer adds a peer by explicit identity.
func (n *Node) AddPeer(id *Identity) error { func (n *Node) AddPeer(id *Identity) error {
if id == nil { if id == nil {