From caad356b93e27c316d33127b957c0f414f4946b8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 25 May 2020 18:05:34 -0700 Subject: [PATCH] A bunch of go fixes, wire up root add/delete. --- go/native/GoGlue.cpp | 3 +- go/native/GoGlue.h | 3 +- go/pkg/zerotier/api.go | 126 +++++++++++++++++++-------------- go/pkg/zerotier/fingerprint.go | 5 ++ go/pkg/zerotier/identity.go | 17 ++--- go/pkg/zerotier/node.go | 44 +++++++++--- go/pkg/zerotier/path.go | 1 - go/pkg/zerotier/peer.go | 8 +-- include/ZeroTierCore.h | 3 +- node/Node.cpp | 11 ++- node/Node.hpp | 2 +- 11 files changed, 134 insertions(+), 89 deletions(-) diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 84ffb7a92..13a45f9eb 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -92,7 +92,7 @@ struct ZT_GoNode_Impl }; static const std::string defaultHomePath(OSUtils::platformDefaultHomePath()); -const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str(); +const char *const ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str(); // These are implemented in Go code. extern "C" int goPathCheckFunc(void *,const ZT_Identity *,int,const void *,int); @@ -300,6 +300,7 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr) { try { struct ZT_Node_Callbacks cb; + cb.statePutFunction = &ZT_GoNode_StatePutFunction; cb.stateGetFunction = &ZT_GoNode_StateGetFunction; cb.wirePacketSendFunction = &ZT_GoNode_WirePacketSendFunction; diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index ba97afd5b..340b3c7c7 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "../../include/ZeroTierCore.h" #include "../../node/Constants.hpp" @@ -29,7 +30,7 @@ typedef void ZT_GoTap; struct ZT_GoNode_Impl; typedef struct ZT_GoNode_Impl ZT_GoNode; -extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH; +extern const char *const ZT_PLATFORM_DEFAULT_HOMEPATH; ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr); void ZT_GoNode_delete(ZT_GoNode *gn); diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index 8e71c4947..eb56529e7 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -18,6 +18,7 @@ import ( secrand "crypto/rand" "encoding/json" "fmt" + "io" "io/ioutil" "net" "net/http" @@ -33,8 +34,6 @@ import ( // APISocketName is the default socket name for accessing the API const APISocketName = "apisocket" -var startTime = TimeMs() - // APIGet makes a query to the API via a Unix domain or windows pipe socket func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, int64, error) { client, err := createNamedSocketHTTPClient(basePath, socketName) @@ -128,7 +127,7 @@ type APIStatus struct { Address Address `json:"address"` Clock int64 `json:"clock"` StartupTime int64 `json:"startupTime"` - Config LocalConfig `json:"config"` + Config *LocalConfig `json:"config"` Online bool `json:"online"` PeerCount int `json:"peerCount"` PathCount int `json:"pathCount"` @@ -254,8 +253,6 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e smux := http.NewServeMux() - //////////////////////////////////////////////////////////////////////////// - smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() @@ -278,7 +275,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e _ = apiSendObj(out, req, http.StatusOK, &APIStatus{ Address: node.Address(), Clock: TimeMs(), - StartupTime: startTime, + StartupTime: node.startupTime, Config: node.LocalConfig(), Online: node.Online(), PeerCount: len(peers), @@ -302,8 +299,6 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } }) - //////////////////////////////////////////////////////////////////////////// - smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() @@ -336,9 +331,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } }) - //////////////////////////////////////////////////////////////////////////// - smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) { + var err error + defer func() { e := recover() if e != nil { @@ -351,65 +346,92 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } apiSetStandardHeaders(out) + if req.URL.Path == "/peer/_addroot" { + if req.Method == http.MethodPost || req.Method == http.MethodPut { + rsdata, err := ioutil.ReadAll(io.LimitReader(req.Body, 16384)) + if err != nil || len(rsdata) == 0 { + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"read error"}) + } else { + p, err := node.AddRoot(rsdata) + if err != nil { + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid root spec"}) + } + _ = apiSendObj(out, req, http.StatusOK, p) + } + } else { + out.Header().Set("Allow", "POST, PUT") + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"no root spec supplied"}) + } + return + } + + var queriedStr string var queriedID Address + var queriedFP *Fingerprint if len(req.URL.Path) > 6 { - var err error - queriedID, err = NewAddressFromString(req.URL.Path[6:]) - if err != nil { - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) - return + queriedStr = req.URL.Path[6:] + if len(queriedStr) == AddressStringLength { + queriedID, err = NewAddressFromString(queriedStr) + if err != nil { + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) + return + } + } else { + queriedFP, err = NewFingerprintFromString(queriedStr) + if err != nil { + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) + return + } } } - // Right now POST/PUT is only used with peers to add or remove root servers. - if req.Method == http.MethodPost || req.Method == http.MethodPut { - if queriedID == 0 { - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) - return + var peer *Peer + peers := node.Peers() + if queriedFP != nil { + for _, p := range peers { + if p.Fingerprint.Equals(queriedFP) { + peer = p + break + } } - var peerChanges PeerMutableFields - if apiReadObj(out, req, &peerChanges) == nil { - if peerChanges.Root != nil || peerChanges.Bootstrap != nil { - peers := node.Peers() - for _, p := range peers { - if p.Address == queriedID && (peerChanges.Identity == nil || peerChanges.Identity.Equals(p.Identity)) { - if peerChanges.Root != nil && *peerChanges.Root != p.Root { - if *peerChanges.Root { - _ = node.AddRoot(p.Identity, peerChanges.Bootstrap) - } else { - node.RemoveRoot(p.Identity) - } - } - break - } + } else if queriedID != 0 { + for _, p := range peers { + if p.Address == queriedID { + peer = p + break + } + } + } + + if req.Method == http.MethodPost || req.Method == http.MethodPut { + if peer != nil { + var posted Peer + if apiReadObj(out, req, &posted) == nil { + if posted.Root && !peer.Root { + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"root spec must be submitted to /peer/_addroot, post to peers can only be used to clear the root flag"}) + } else if !posted.Root && peer.Root { + peer.Root = false + node.RemoveRoot(peer.Address) + _ = apiSendObj(out, req, http.StatusOK, peer) } } } else { - return + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) } - } - - if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodPost || req.Method == http.MethodPut { - peers := node.Peers() - if queriedID != 0 { - for _, p := range peers { - if p.Address == queriedID { - _ = apiSendObj(out, req, http.StatusOK, p) - return - } - } + } else if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodPost || req.Method == http.MethodPut { + if peer != nil { + _ = apiSendObj(out, req, http.StatusOK, peer) + } else if len(queriedStr) > 0 { _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) } else { _ = apiSendObj(out, req, http.StatusOK, peers) } } else { - out.Header().Set("Allow", "GET, HEAD, PUT, POST") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"peers are read only"}) + out.Header().Set("Allow", "GET, HEAD, POST, PUT") + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method"}) } }) - //////////////////////////////////////////////////////////////////////////// - smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() @@ -496,8 +518,6 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } }) - //////////////////////////////////////////////////////////////////////////// - listener, err := createNamedSocketListener(basePath, APISocketName) if err != nil { return nil, nil, err diff --git a/go/pkg/zerotier/fingerprint.go b/go/pkg/zerotier/fingerprint.go index c34a4db1f..595aee84d 100644 --- a/go/pkg/zerotier/fingerprint.go +++ b/go/pkg/zerotier/fingerprint.go @@ -18,6 +18,7 @@ package zerotier import "C" import ( + "bytes" "errors" "strings" "unsafe" @@ -49,6 +50,10 @@ func (fp *Fingerprint) String() string { return Base32StdLowerCase.EncodeToString(tmp[:]) } +func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool { + return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:]) +} + func (fp *Fingerprint) apiFingerprint() *C.ZT_Fingerprint { var apifp C.ZT_Fingerprint apifp.address = C.uint64_t(fp.Address) diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index 79cb45ec7..1ebd34e2a 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -27,14 +27,15 @@ import ( "unsafe" ) +// Constants from node/Identity.hpp (must be the same) const ( - IdentityTypeC25519 = C.ZT_IDENTITY_TYPE_C25519 - IdentityTypeP384 = C.ZT_IDENTITY_TYPE_P384 + IdentityTypeC25519 = 0 + IdentityTypeP384 = 1 - IdentityTypeC25519PublicKeySize = C.ZT_IDENTITY_C25519_PUBLIC_KEY_SIZE - IdentityTypeC25519PrivateKeySize = C.ZT_IDENTITY_C25519_PRIVATE_KEY_SIZE - IdentityTypeP384PublicKeySize = C.ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE - IdentityTypeP384PrivateKeySize = C.ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + IdentityTypeC25519PublicKeySize = 64 + IdentityTypeC25519PrivateKeySize = 64 + IdentityTypeP384PublicKeySize = 114 + IdentityTypeP384PrivateKeySize = 112 ) // Identity is precisely what it sounds like: the address and associated keys for a ZeroTier node @@ -239,14 +240,14 @@ func (id *Identity) MakeRoot(addresses []InetAddress) ([]byte, error) { return nil, errors.New("error initializing ZT_Identity") } - ss := make([]C.sockaddr_storage, len(addresses)) + ss := make([]C.struct_sockaddr_storage, len(addresses)) for i := range addresses { if !makeSockaddrStorage(addresses[i].IP, addresses[i].Port, &ss[i]) { return nil, errors.New("invalid address in address list") } } var buf [8192]byte - rl := C.ZT_Identity_makeRootSpecification(id.cid, C.int64_t(TimeMs()), &ss[0], C.uint(len(ss)), &buf[0], 8192) + rl := C.ZT_Identity_makeRootSpecification(id.cid, C.int64_t(TimeMs()), &ss[0], C.uint(len(ss)), unsafe.Pointer(&buf[0]), 8192) if rl <= 0 { return nil, errors.New("unable to make root specification (does identity contain a secret key?)") } diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index eace0fc4d..a2a19b589 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -63,7 +63,7 @@ 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 = 4 + maxCNodeRefs = 8 ) var ( @@ -90,6 +90,9 @@ func init() { // Node is an instance of a virtual port on the global switch. 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 cPtr uintptr @@ -146,13 +149,14 @@ type Node struct { // NewNode creates and initializes a new instance of the ZeroTier node service func NewNode(basePath string) (n *Node, err error) { n = new(Node) + n.startupTime = TimeMs() // Register this with the cNodeRefs lookup array and set up a deferred function // to unregister this if we exit before the end of the constructor such as by // returning an error. cPtr := -1 - for i:=0;i= 0 { - atomic.StoreUint32(&cNodeRefUsed[cPtr],0) + atomic.StoreUint32(&cNodeRefUsed[cPtr], 0) cNodeRefs[cPtr] = nil } }() @@ -331,7 +335,7 @@ func (n *Node) Close() { n.runWaitGroup.Wait() cNodeRefs[n.cPtr] = nil - atomic.StoreUint32(&cNodeRefUsed[n.cPtr],0) + atomic.StoreUint32(&cNodeRefUsed[n.cPtr], 0) } } @@ -445,11 +449,31 @@ func (n *Node) Leave(nwid NetworkID) error { return nil } -func (n *Node) AddRoot(spec []byte) error { - return nil +func (n *Node) AddRoot(spec []byte) (*Peer, error) { + if len(spec) == 0 { + return nil, ErrInvalidParameter + } + var address C.uint64_t + res := C.ZT_Node_addRoot(n.zn, nil, unsafe.Pointer(&spec[0]), C.uint(len(spec)), &address) + if res != 0 { + return nil, ErrInvalidParameter + } + peers := n.Peers() + for _, p := range peers { + if p.Address == Address(uint64(address)) { + return p, nil + } + } + return nil, ErrInternal } func (n *Node) RemoveRoot(address Address) { + var cfp C.ZT_Fingerprint + cfp.address = C.uint64_t(address) + for i := 0; i < 48; i++ { + cfp.hash[i] = 0 + } + C.ZT_Node_removeRoot(n.zn, nil, &cfp) } // GetNetwork looks up a network by ID or returns nil if not joined @@ -481,7 +505,8 @@ func (n *Node) Peers() []*Peer { p2 := new(Peer) p2.Address = Address(p.address) p2.Identity, _ = newIdentityFromCIdentity(unsafe.Pointer(p.identity)) - p2.Fingerprint = C.GoBytes(unsafe.Pointer(&p.fingerprint.hash[0]), 48) + p2.Fingerprint.Address = p2.Address + copy(p2.Fingerprint.Hash[:], ((*[48]byte)(unsafe.Pointer(&p.fingerprint.hash[0])))[:]) p2.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)} p2.Latency = int(p.latency) p2.Root = p.root != 0 @@ -498,7 +523,6 @@ func (n *Node) Peers() []*Peer { Port: a.Port, LastSend: int64(pt.lastSend), LastReceive: int64(pt.lastReceive), - TrustedPathID: uint64(pt.trustedPathId), }) } } @@ -602,7 +626,7 @@ func (n *Node) runMaintenance() { } for _, ip := range interfaceAddresses { for _, p := range ports { - a := InetAddress{ IP: ip, Port: p } + a := InetAddress{IP: ip, Port: p} ak := a.key() if _, have := externalAddresses[ak]; !have { externalAddresses[ak] = &a diff --git a/go/pkg/zerotier/path.go b/go/pkg/zerotier/path.go index 610afed63..b958e901d 100644 --- a/go/pkg/zerotier/path.go +++ b/go/pkg/zerotier/path.go @@ -21,5 +21,4 @@ type Path struct { Port int `json:"port"` LastSend int64 `json:"lastSend"` LastReceive int64 `json:"lastReceive"` - TrustedPathID uint64 `json:"trustedPathID"` } diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go index 5a372b8c1..4baeaf971 100644 --- a/go/pkg/zerotier/peer.go +++ b/go/pkg/zerotier/peer.go @@ -17,16 +17,10 @@ package zerotier type Peer struct { Address Address `json:"address"` Identity *Identity `json:"identity"` - Fingerprint []byte `json:"fingerprint"` + Fingerprint Fingerprint `json:"fingerprint"` Version [3]int `json:"version"` Latency int `json:"latency"` Root bool `json:"root"` Bootstrap *InetAddress `json:"bootstrap,omitempty"` Paths []Path `json:"paths,omitempty"` } - -// PeerMutableFields contains only the mutable fields of Peer as nullable pointers. -type PeerMutableFields struct { - Root *bool `json:"root"` - RootSpec []byte `json:"rootSpec"` -} diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 49c7fcf77..5b62a8757 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -1755,9 +1755,10 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @param rdef Root definition (serialized identity and locator) * @param rdeflen Length of root definition in bytes + * @param address If non-NULL will be filled with the ZeroTier address of the root (only defined if return is OK) * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen); +ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address); /** * Remove a root diff --git a/node/Node.cpp b/node/Node.cpp index 3f1fb4a57..388db530d 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -224,9 +224,6 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr,int64_t now,volatile int64 if (pf.online) { // If we have at least one online root, request whois for roots not online. - // This will give us updated locators for these roots which may contain new - // IP addresses. It will also auto-discover IPs for roots that were not added - // with an initial bootstrap address. // TODO //for (Vector
::const_iterator r(pf.rootsNotOnline.begin()); r != pf.rootsNotOnline.end(); ++r) // RR->sw->requestWhois(tPtr,now,*r); @@ -339,11 +336,13 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u } else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; } -ZT_ResultCode Node::addRoot(void *tPtr,const void *rdef,unsigned int rdeflen) +ZT_ResultCode Node::addRoot(void *tPtr,const void *rdef,unsigned int rdeflen,uint64_t *address) { if ((!rdef)||(rdeflen == 0)) return ZT_RESULT_ERROR_BAD_PARAMETER; std::pair r(Locator::parseRootSpecification(rdef,rdeflen)); + if (address) + *address = r.first.address().toInt(); return ((r.first)&&(RR->topology->addRoot(tPtr,r.first,r.second))) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; } @@ -862,10 +861,10 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint } } -enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen) +enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address) { try { - return reinterpret_cast(node)->addRoot(tptr,rdef,rdeflen); + return reinterpret_cast(node)->addRoot(tptr,rdef,rdeflen,address); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; } catch ( ... ) { diff --git a/node/Node.hpp b/node/Node.hpp index 655fefcf8..80da95e5b 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -92,7 +92,7 @@ public: ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr); ZT_ResultCode multicastSubscribe(void *tPtr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); - ZT_ResultCode addRoot(void *tptr,const void *rdef,unsigned int rdeflen); + ZT_ResultCode addRoot(void *tptr,const void *rdef,unsigned int rdeflen,uint64_t *address); ZT_ResultCode removeRoot(void *tptr,const ZT_Fingerprint *fp); uint64_t address() const; void status(ZT_NodeStatus *status) const;