From 60fa07bff255c825c96b02e4263eac41d004cf65 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 27 Mar 2020 17:02:44 -0700 Subject: [PATCH] Implement fingerprint option on join in Go land --- go/cmd/zerotier/cli/join.go | 25 ++++++++++++-- go/native/GoGlue.cpp | 4 +-- go/native/GoGlue.h | 2 +- go/pkg/zerotier/address.go | 9 +++++ go/pkg/zerotier/api.go | 1 + go/pkg/zerotier/fingerprint.go | 60 ++++++++++++++++++++++++++++++++++ go/pkg/zerotier/node.go | 8 +++-- node/NetworkConfig.hpp | 2 +- 8 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 go/pkg/zerotier/fingerprint.go diff --git a/go/cmd/zerotier/cli/join.go b/go/cmd/zerotier/cli/join.go index 89a13d8cc..18733d923 100644 --- a/go/cmd/zerotier/cli/join.go +++ b/go/cmd/zerotier/cli/join.go @@ -23,7 +23,7 @@ import ( // Join CLI command func Join(basePath, authToken string, args []string) { - if len(args) != 1 { + if len(args) < 1 || len(args) > 2 { Help() os.Exit(1) } @@ -39,9 +39,28 @@ func Join(basePath, authToken string, args []string) { } nwids := fmt.Sprintf("%.16x", nwid) + var fp *zerotier.Fingerprint + if len(args) == 2 { + fp, err = zerotier.NewFingerprintFromString(args[1]) + if err != nil { + fmt.Printf("ERROR: invalid network controller fingerprint: %s\n", args[1]) + os.Exit(1) + } + } + var network zerotier.APINetwork network.ID = zerotier.NetworkID(nwid) - apiPost(basePath, authToken, "/network/"+nwids, &network, nil) - fmt.Printf("OK %s", nwids) + network.ControllerFingerprint = fp + + if apiPost(basePath, authToken, "/network/"+nwids, &network, nil) <= 0 { + fmt.Printf("FAILED\n") + } else { + if fp == nil { + fmt.Printf("OK %s\n", nwids) + } else { + fmt.Printf("OK %s %s\n", nwids, fp.String()) + } + } + os.Exit(0) } diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index faaf7421f..4f065c1fe 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -593,7 +593,7 @@ static void tapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from, &(reinterpret_cast(uptr)->nextBackgroundTaskDeadline)); } -extern "C" ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid) +extern "C" ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid,const ZT_Fingerprint *const controllerFingerprint) { try { std::lock_guard l(gn->taps_l); @@ -606,7 +606,7 @@ extern "C" ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid) if (!tap) return nullptr; gn->taps[nwid] = tap; - gn->node->join(nwid,tap.get(),nullptr); + gn->node->join(nwid,controllerFingerprint,tap.get(),nullptr); return (ZT_GoTap *)tap.get(); } catch ( ... ) { return nullptr; diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index 39ea65879..a5554b7cc 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -54,7 +54,7 @@ int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,int po /* Close all listener threads for a given local IP and port */ int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,int port); -ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid); +ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint); void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid); diff --git a/go/pkg/zerotier/address.go b/go/pkg/zerotier/address.go index b9a79dfed..59d7ede80 100644 --- a/go/pkg/zerotier/address.go +++ b/go/pkg/zerotier/address.go @@ -39,6 +39,15 @@ 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. +func (a Address) CopyTo(b []byte) { + b[0] = byte(a >> 32) + b[1] = byte(a >> 24) + b[2] = byte(a >> 16) + b[3] = byte(a >> 8) + b[4] = byte(a) +} + // 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 } diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index 074a3b541..52dfaeeab 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -151,6 +151,7 @@ type APINetwork struct { ID NetworkID `json:"id"` Config NetworkConfig `json:"config"` Settings *NetworkLocalSettings `json:"settings,omitempty"` + ControllerFingerprint *Fingerprint `json:"controllerFingerprint,omitempty"` MulticastSubscriptions []*MulticastGroup `json:"multicastSubscriptions,omitempty"` PortType string `json:"portType"` PortName string `json:"portName"` diff --git a/go/pkg/zerotier/fingerprint.go b/go/pkg/zerotier/fingerprint.go new file mode 100644 index 000000000..10b8bd1eb --- /dev/null +++ b/go/pkg/zerotier/fingerprint.go @@ -0,0 +1,60 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package zerotier + +//#cgo CFLAGS: -O3 +//#include "../../native/GoGlue.h" +import "C" + +import ( + "encoding/base32" + "errors" + "strings" + "unsafe" +) + +var ztBase32 = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding) + +type Fingerprint struct { + Address Address `json:"address"` + Hash [48]byte `json:"hash"` +} + +func NewFingerprintFromString(fps string) (*Fingerprint, error) { + fpb, err := ztBase32.DecodeString(strings.TrimSpace(strings.ToLower(fps))) + if err != nil { + return nil, err + } + if len(fpb) != 53 { + return nil, errors.New("invalid fingerprint length") + } + var fp Fingerprint + fp.Address, _ = NewAddressFromBytes(fpb[0:5]) + copy(fp.Hash[:],fpb[5:]) + return &fp, nil +} + +func (fp *Fingerprint) String() string { + var tmp [53]byte + fp.Address.CopyTo(tmp[0:5]) + copy(tmp[5:],fp.Hash[:]) + return ztBase32.EncodeToString(tmp[:]) +} + +func (fp *Fingerprint) apiFingerprint() *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 +} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index d6125cd02..5465f64a1 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -498,7 +498,7 @@ func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error) // Join a network. // If tap is nil, the default system tap for this OS/platform is used (if available). -func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*Network, error) { +func (n *Node) Join(nwid NetworkID, controllerFingerprint *Fingerprint, settings *NetworkLocalSettings, tap Tap) (*Network, error) { n.networksLock.RLock() if nw, have := n.networks[nwid]; have { n.infoLog.Printf("join network %.16x ignored: already a member", nwid) @@ -512,7 +512,11 @@ func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*N if tap != nil { panic("non-native taps not yet implemented") } - ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid)) + var fp *C.ZT_Fingerprint + if controllerFingerprint != nil { + fp = controllerFingerprint.apiFingerprint() + } + ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid), fp) if ntap == nil { n.infoLog.Printf("join network %.16x failed: tap device failed to initialize (check drivers / kernel modules)", uint64(nwid)) return nil, ErrTapInitFailed diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 42c6a8288..6c25796ea 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -206,7 +206,7 @@ struct NetworkConfig : TriviallyCopyable ZT_INLINE bool permitsBridging(const Address &fromPeer) const noexcept { for(unsigned int i=0;i