Browse Source

A bunch of go fixes, wire up root add/delete.

Adam Ierymenko 5 years ago
parent
commit
caad356b93

+ 2 - 1
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;

+ 2 - 1
go/native/GoGlue.h

@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdio.h>
 
 #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);

+ 71 - 51
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
 				}
-			} else {
-				return
 			}
 		}
 
-		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
+		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 {
+				_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
+			}
+		} 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

+ 5 - 0
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)

+ 9 - 8
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?)")
 	}

+ 34 - 10
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<maxCNodeRefs;i++ {
-		if atomic.CompareAndSwapUint32(&cNodeRefUsed[i],0,1) {
+	for i := 0; i < maxCNodeRefs; i++ {
+		if atomic.CompareAndSwapUint32(&cNodeRefUsed[i], 0, 1) {
 			cNodeRefs[i] = n
 			cPtr = i
 			n.cPtr = uintptr(i)
@@ -164,7 +168,7 @@ func NewNode(basePath string) (n *Node, err error) {
 	}
 	defer func() {
 		if cPtr >= 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

+ 0 - 1
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"`
 }

+ 1 - 7
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"`
-}

+ 2 - 1
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

+ 5 - 6
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<Address>::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<Identity,Locator> 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<ZeroTier::Node *>(node)->addRoot(tptr,rdef,rdeflen);
+		return reinterpret_cast<ZeroTier::Node *>(node)->addRoot(tptr,rdef,rdeflen,address);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {

+ 1 - 1
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;