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 {
switch status {
case zerotier.NetworkStatusNotFound:
return "NOTFOUND"
return "not-found"
case zerotier.NetworkStatusAccessDenied:
return "ACCESSDENIED"
case zerotier.NetworkStatusRequestConfiguration:
return "UPDATING"
return "access-denied"
case zerotier.NetworkStatusRequestingConfiguration:
return "updating"
case zerotier.NetworkStatusOK:
return "OK"
return "ok"
}
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
}
// 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) {
_ = b[4]
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.
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 {
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
if apiReadObj(out, req, &nw) == nil {
n := node.GetNetwork(nw.ID)
n := node.Network(nw.ID)
if n == nil {
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"})

View file

@ -24,11 +24,20 @@ import (
"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 {
Address Address
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) {
if len(fps) < AddressStringLength {
return nil, ErrInvalidZeroTierAddress
@ -66,22 +75,28 @@ func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint {
return &fp
}
// String returns an address or a full address-hash depenting on whether a hash is present.
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 fp.Address.String()
}
// Equals test for full equality with another fingerprint (including hash).
func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool {
return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:])
}
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
// BestSpecificityEquals compares either just the addresses or also the hashes if both are present.
func (fp *Fingerprint) BestSpecificityEquals(fp2 *Fingerprint) bool {
if fp2 == nil || fp.Address != fp2.Address {
return false
}
if len(fp.Hash) == FingerprintHashSize && len(fp2.Hash) == FingerprintHashSize {
return bytes.Equal(fp.Hash, fp2.Hash)
}
return true
}
func (fp *Fingerprint) MarshalJSON() ([]byte, error) {
@ -99,3 +114,10 @@ func (fp *Fingerprint) UnmarshalJSON(j []byte) error {
fp.Hash = fp2.Hash
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
}
// NewIdentity generates a new identity of the selected type
// NewIdentity generates a new identity of the selected type.
func NewIdentity(identityType int) (*Identity, error) {
switch identityType {
case C.ZT_IDENTITY_TYPE_C25519:
@ -160,7 +160,7 @@ func NewIdentityFromString(s string) (*Identity, error) {
return id, nil
}
// Address returns this identity's address
// Address returns this identity's address.
func (id *Identity) Address() Address { return id.address }
// 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))
}
// 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 {
switch id.idtype {
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)
}
// MarshalJSON marshals this Identity in its string format (private key is never included)
func (id *Identity) MarshalJSON() ([]byte, error) {
return []byte("\"" + id.String() + "\""), nil
}
// UnmarshalJSON unmarshals this Identity from a string
func (id *Identity) UnmarshalJSON(j []byte) error {
var s string
err := json.Unmarshal(j, &s)

View file

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

View file

@ -47,12 +47,14 @@ var nullLogger = log.New(ioutil.Discard, "", 0)
const (
NetworkIDStringLength = 16
NEtworkIDLength = 8
AddressStringLength = 10
AddressLength = 5
NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
NetworkStatusOK int = C.ZT_NETWORK_STATUS_OK
NetworkStatusAccessDenied int = C.ZT_NETWORK_STATUS_ACCESS_DENIED
NetworkStatusNotFound int = C.ZT_NETWORK_STATUS_NOT_FOUND
NetworkStatusRequestingConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
NetworkStatusOK int = C.ZT_NETWORK_STATUS_OK
NetworkStatusAccessDenied int = C.ZT_NETWORK_STATUS_ACCESS_DENIED
NetworkStatusNotFound int = C.ZT_NETWORK_STATUS_NOT_FOUND
NetworkTypePrivate int = C.ZT_NETWORK_TYPE_PRIVATE
NetworkTypePublic int = C.ZT_NETWORK_TYPE_PUBLIC
@ -62,7 +64,8 @@ const (
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
)
@ -73,7 +76,10 @@ var (
CoreVersionRevision 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
)
@ -92,7 +98,9 @@ type Node struct {
// Time this node was created
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
// 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
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
interfaceAddressesLock sync.Mutex
// online and running are atomic flags set to control and monitor background tasks
online uint32
running uint32
online uint32
basePath string
peersPath string
@ -115,20 +124,20 @@ type Node struct {
infoLogPath string
errorLogPath string
// localConfig is the current state of local.conf
// localConfig is the current state of local.conf.
localConfig *LocalConfig
previousLocalConfig *LocalConfig
localConfigLock sync.RWMutex
// logs for information, errors, and trace output
infoLogW *sizeLimitWriter
errLogW *sizeLimitWriter
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
// 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 *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
tcpApiServer *http.Server
namedSocketAPIServer *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
}
@ -163,8 +173,11 @@ func NewNode(basePath string) (n *Node, err error) {
}
}
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() {
if 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.networksByMAC = make(map[MAC]*Network)
n.interfaceAddresses = make(map[string]net.IP)
n.online = 0
n.running = 1
_ = os.MkdirAll(basePath, 0755)
@ -265,7 +278,7 @@ func NewNode(basePath string) (n *Node, err error) {
_ = n.localConfig.Write(n.localConfigPath)
}
n.namedSocketApiServer, n.tcpApiServer, err = createAPIServer(basePath, n)
n.namedSocketAPIServer, n.tcpAPIServer, err = createAPIServer(basePath, n)
if err != nil {
n.infoLog.Printf("FATAL: unable to start API server: %s", err.Error())
return nil, err
@ -293,12 +306,13 @@ func NewNode(basePath string) (n *Node, err error) {
defer n.runWaitGroup.Done()
lastMaintenanceRun := int64(0)
for atomic.LoadUint32(&n.running) != 0 {
time.Sleep(500 * time.Millisecond)
time.Sleep(250 * time.Millisecond)
nowS := time.Now().Unix()
if (nowS - lastMaintenanceRun) >= 30 {
lastMaintenanceRun = nowS
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
func (n *Node) Close() {
if atomic.SwapUint32(&n.running, 0) != 0 {
if n.namedSocketApiServer != nil {
_ = n.namedSocketApiServer.Close()
if n.namedSocketAPIServer != nil {
_ = n.namedSocketAPIServer.Close()
}
if n.tcpApiServer != nil {
_ = n.tcpApiServer.Close()
if n.tcpAPIServer != nil {
_ = n.tcpAPIServer.Close()
}
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 ||
n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort ||
n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax {
if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort || n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax {
restartRequired = true
}
@ -437,17 +449,29 @@ func (n *Node) Leave(nwid NetworkID) error {
return nil
}
func (n *Node) AddRoot(id *Identity, loc *Locator) (*Peer, error) {
// TODO
return nil, nil
// AddRoot designates a peer as root, adding it if missing.
func (n *Node) AddRoot(id *Identity) (*Peer, error) {
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) {
C.ZT_Node_removeRoot(n.zn, nil, C.uint64_t(address))
}
// GetNetwork looks up a network by ID or returns nil if not joined
func (n *Node) GetNetwork(nwid NetworkID) *Network {
// Network looks up a network by ID or returns nil if not joined
func (n *Node) Network(nwid NetworkID) *Network {
n.networksLock.RLock()
nw := n.networks[nwid]
n.networksLock.RUnlock()
@ -484,6 +508,32 @@ func (n *Node) Peers() []*Peer {
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.
func (n *Node) AddPeer(id *Identity) error {
if id == nil {