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

This commit is contained in:
Adam Ierymenko 2020-05-25 18:05:34 -07:00
parent 964c235ecf
commit caad356b93
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
11 changed files with 134 additions and 89 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

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

View file

@ -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?)")
}

View file

@ -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

View file

@ -21,5 +21,4 @@ type Path struct {
Port int `json:"port"`
LastSend int64 `json:"lastSend"`
LastReceive int64 `json:"lastReceive"`
TrustedPathID uint64 `json:"trustedPathID"`
}

View file

@ -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"`
}

View file

@ -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

View file

@ -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 ( ... ) {

View file

@ -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;