From 964c235ecf2beed859135a29856c2aa37e874f80 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 25 May 2020 14:23:48 -0700 Subject: [PATCH] Golang fixes --- go/cmd/zerotier/cli/help.go | 2 +- go/go.mod | 2 +- go/go.sum | 2 + go/pkg/zerotier/fingerprint.go | 7 +- go/pkg/zerotier/identity.go | 44 +- go/pkg/zerotier/nativetap.go | 38 +- go/pkg/zerotier/node.go | 406 ++++++++---------- go/pkg/zerotier/peer.go | 3 +- .../sys/internal/unsafeheader/unsafeheader.go | 30 ++ .../golang.org/x/sys/windows/dll_windows.go | 29 ++ .../golang.org/x/sys/windows/env_windows.go | 11 +- .../x/sys/windows/security_windows.go | 20 +- .../x/sys/windows/syscall_windows.go | 37 +- go/vendor/modules.txt | 3 +- node/Identity.hpp | 8 +- 15 files changed, 349 insertions(+), 293 deletions(-) create mode 100644 go/vendor/golang.org/x/sys/internal/unsafeheader/unsafeheader.go diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index bc6f0148c..37403f2a7 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/go/cmd/zerotier/cli/help.go @@ -63,7 +63,7 @@ Commands: validate Locally validate an identity sign Sign a file with an identity's key verify Verify a signature - makeroot
[...] Make a root spec (see docs) + makeroot Make a root spec (see docs) The 'service' command does not exit until the service receives a signal. This is typically run from launchd (Mac), systemd or init (Linux), a Windows diff --git a/go/go.mod b/go/go.mod index 8900c9376..faa7e7c59 100644 --- a/go/go.mod +++ b/go/go.mod @@ -5,5 +5,5 @@ go 1.13 require ( github.com/Microsoft/go-winio v0.4.14 github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 - golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect + golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect ) diff --git a/go/go.sum b/go/go.sum index 6d49b43b7..84bcfc370 100644 --- a/go/go.sum +++ b/go/go.sum @@ -19,3 +19,5 @@ golang.org/x/sys v0.0.0-20200120151820-655fe14d7479 h1:LhLiKguPgZL+Tglay4GhVtfF0 golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/go/pkg/zerotier/fingerprint.go b/go/pkg/zerotier/fingerprint.go index 10b8bd1eb..c34a4db1f 100644 --- a/go/pkg/zerotier/fingerprint.go +++ b/go/pkg/zerotier/fingerprint.go @@ -18,21 +18,18 @@ package zerotier 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))) + fpb, err := Base32StdLowerCase.DecodeString(strings.TrimSpace(strings.ToLower(fps))) if err != nil { return nil, err } @@ -49,7 +46,7 @@ func (fp *Fingerprint) String() string { var tmp [53]byte fp.Address.CopyTo(tmp[0:5]) copy(tmp[5:],fp.Hash[:]) - return ztBase32.EncodeToString(tmp[:]) + return Base32StdLowerCase.EncodeToString(tmp[:]) } func (fp *Fingerprint) apiFingerprint() *C.ZT_Fingerprint { diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index 281ab731c..79cb45ec7 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -27,18 +27,14 @@ import ( "unsafe" ) -// IdentityTypeC25519 is a classic Curve25519/Ed25519 identity -const IdentityTypeC25519 = 0 - -// IdentityTypeP384 is an identity containing both NIST P-384 and Curve25519/Ed25519 key types and leveraging both when possible -const IdentityTypeP384 = 1 - -// Sizes of components of different identity types const ( - IdentityTypeC25519PublicKeySize = 64 // C25519/Ed25519 keys - IdentityTypeC25519PrivateKeySize = 64 // C25519/Ed25519 private keys - IdentityTypeP384PublicKeySize = 209 // C25519/Ed25519, P-384 point-compressed public, P-384 self-signature - IdentityTypeP384PrivateKeySize = 112 // C25519/Ed25519 and P-384 private keys + IdentityTypeC25519 = C.ZT_IDENTITY_TYPE_C25519 + IdentityTypeP384 = C.ZT_IDENTITY_TYPE_P384 + + 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 ) // Identity is precisely what it sounds like: the address and associated keys for a ZeroTier node @@ -79,18 +75,18 @@ func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { return id, nil } -// cIdentity returns a pointer to the core ZT_Identity instance or nil/0 on error. -func (id *Identity) cIdentity() unsafe.Pointer { +// initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error. +func (id *Identity) initCIdentityPtr() bool { if uintptr(id.cid) == 0 { idCStr := C.CString(id.String()) defer C.free(unsafe.Pointer(idCStr)) id.cid = C.ZT_Identity_fromString(idCStr) if uintptr(id.cid) == 0 { - return nil + return false } runtime.SetFinalizer(id, identityFinalizer) } - return id.cid + return true } // NewIdentity generates a new identity of the selected type @@ -197,8 +193,7 @@ func (id *Identity) String() string { // LocallyValidate performs local self-validation of this identity func (id *Identity) LocallyValidate() bool { - id.cIdentity() - if uintptr(id.cid) == 0 { + if !id.initCIdentityPtr() { return false } return C.ZT_Identity_validate(id.cid) != 0 @@ -206,8 +201,7 @@ func (id *Identity) LocallyValidate() bool { // Sign signs a message with this identity func (id *Identity) Sign(msg []byte) ([]byte, error) { - id.cIdentity() - if uintptr(id.cid) == 0 { + if !id.initCIdentityPtr() { return nil, ErrInvalidKey } @@ -226,15 +220,9 @@ func (id *Identity) Sign(msg []byte) ([]byte, error) { // Verify verifies a signature func (id *Identity) Verify(msg, sig []byte) bool { - if len(sig) == 0 { + if len(sig) == 0 || !id.initCIdentityPtr() { return false } - - id.cIdentity() - if uintptr(id.cid) == 0 { - return false - } - var dataP unsafe.Pointer if len(msg) > 0 { dataP = unsafe.Pointer(&msg[0]) @@ -247,9 +235,7 @@ func (id *Identity) MakeRoot(addresses []InetAddress) ([]byte, error) { if len(addresses) == 0 { return nil, errors.New("at least one static address must be specified for a root") } - - id.cIdentity() - if uintptr(id.cid) == 0 { + if !id.initCIdentityPtr() { return nil, errors.New("error initializing ZT_Identity") } diff --git a/go/pkg/zerotier/nativetap.go b/go/pkg/zerotier/nativetap.go index c7c12cf48..bba58a1cb 100644 --- a/go/pkg/zerotier/nativetap.go +++ b/go/pkg/zerotier/nativetap.go @@ -159,25 +159,27 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast } func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { + node := cNodeRefs[uintptr(gn)] + if node == nil { + return + } + node.networksLock.RLock() + network := node.networks[NetworkID(nwid)] + node.networksLock.RUnlock() + if network == nil { + return + } + + node.runWaitGroup.Add(1) go func() { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - node.networksLock.RLock() - network := node.networks[NetworkID(nwid)] - node.networksLock.RUnlock() - if network != nil { - tap, _ := network.tap.(*nativeTap) - if tap != nil { - mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)} - tap.multicastGroupHandlersLock.Lock() - defer tap.multicastGroupHandlersLock.Unlock() - for _, h := range tap.multicastGroupHandlers { - h(added, mg) - } + defer node.runWaitGroup.Done() + tap, _ := network.tap.(*nativeTap) + if tap != nil { + mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)} + tap.multicastGroupHandlersLock.Lock() + defer tap.multicastGroupHandlersLock.Unlock() + for _, h := range tap.multicastGroupHandlers { + h(added, mg) } } }() diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 43fdd87de..eace0fc4d 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -45,7 +45,6 @@ import ( var nullLogger = log.New(ioutil.Discard, "", 0) -// Network status states const ( NetworkIDStringLength = 16 AddressStringLength = 10 @@ -62,16 +61,17 @@ const ( networkConfigOpUpdate int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE 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 ) var ( // PlatformDefaultHomePath is the default location of ZeroTier's working path on this system PlatformDefaultHomePath string = C.GoString(C.ZT_PLATFORM_DEFAULT_HOMEPATH) - // This map is used to get the Go Node object from a pointer passed back in via C callbacks - nodesByUserPtr = make(map[uintptr]*Node) - nodesByUserPtrCtr = uintptr(0) - nodesByUserPtrLock sync.RWMutex + cNodeRefs [maxCNodeRefs]*Node + cNodeRefUsed [maxCNodeRefs]uint32 CoreVersionMajor int CoreVersionMinor int @@ -90,6 +90,9 @@ func init() { // Node is an instance of a virtual port on the global switch. type Node struct { + // an arbitrary uintptr given to the core as its pointer back to Go's Node instance + cPtr uintptr + // networks contains networks we have joined, and networksByMAC by their local MAC address networks map[NetworkID]*Network networksByMAC map[MAC]*Network // locked by networksLock @@ -107,10 +110,13 @@ type Node struct { peersPath string networksPath string localConfigPath string + infoLogPath string + errorLogPath string // localConfig is the current state of local.conf - localConfig LocalConfig - localConfigLock sync.RWMutex + localConfig *LocalConfig + previousLocalConfig *LocalConfig + localConfigLock sync.RWMutex // logs for information, errors, and trace output infoLogW *sizeLimitWriter @@ -120,13 +126,13 @@ type Node struct { errLog *log.Logger traceLog *log.Logger - // gn is the instance of GoNode, the Go wrapper and multithreaded I/O engine + // gn is the GoNode instance gn *C.ZT_GoNode - // zn is the underlying instance of the ZeroTier core + // zn is the underlying ZT_Node (ZeroTier::Node) instance zn unsafe.Pointer - // id is the identity of this node + // 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 @@ -135,14 +141,34 @@ type Node struct { // runWaitGroup is used to wait for all node goroutines on shutdown runWaitGroup sync.WaitGroup - - // an arbitrary uintptr given to the core as its pointer back to Go's Node instance - cPtr uintptr } // NewNode creates and initializes a new instance of the ZeroTier node service func NewNode(basePath string) (n *Node, err error) { n = new(Node) + + // 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) + cNodeRefs[cPtr] = nil + } + }() + n.networks = make(map[NetworkID]*Network) n.networksByMAC = make(map[MAC]*Network) n.interfaceAddresses = make(map[string]net.IP) @@ -168,17 +194,20 @@ func NewNode(basePath string) (n *Node, err error) { n.localConfigPath = path.Join(basePath, "local.conf") _, identitySecretNotFoundErr := os.Stat(path.Join(basePath, "identity.secret")) + n.localConfig = new(LocalConfig) err = n.localConfig.Read(n.localConfigPath, true, identitySecretNotFoundErr != nil) if err != nil { return } + n.infoLogPath = path.Join(basePath, "info.log") + n.errorLogPath = path.Join(basePath, "error.log") if n.localConfig.Settings.LogSizeMax >= 0 { - n.infoLogW, err = sizeLimitWriterOpen(path.Join(basePath, "info.log")) + n.infoLogW, err = sizeLimitWriterOpen(n.infoLogPath) if err != nil { return } - n.errLogW, err = sizeLimitWriterOpen(path.Join(basePath, "error.log")) + n.errLogW, err = sizeLimitWriterOpen(n.errorLogPath) if err != nil { return } @@ -186,6 +215,7 @@ func NewNode(basePath string) (n *Node, err error) { n.errLog = log.New(n.errLogW, "", log.LstdFlags) } else { n.infoLog = nullLogger + n.errLog = nullLogger } if n.localConfig.Settings.PortSearch { @@ -249,29 +279,17 @@ func NewNode(basePath string) (n *Node, err error) { return nil, err } - nodesByUserPtrLock.Lock() - nodesByUserPtrCtr++ - n.cPtr = nodesByUserPtrCtr - nodesByUserPtr[n.cPtr] = n - nodesByUserPtrLock.Unlock() - cPath := C.CString(basePath) n.gn = C.ZT_GoNode_new(cPath, C.uintptr_t(n.cPtr)) C.free(unsafe.Pointer(cPath)) if n.gn == nil { n.infoLog.Println("FATAL: node initialization failed") - nodesByUserPtrLock.Lock() - delete(nodesByUserPtr, n.cPtr) - nodesByUserPtrLock.Unlock() return nil, ErrNodeInitFailed } n.zn = unsafe.Pointer(C.ZT_GoNode_getNode(n.gn)) n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(n.zn)) if err != nil { n.infoLog.Printf("FATAL: error obtaining node's identity") - nodesByUserPtrLock.Lock() - delete(nodesByUserPtr, n.cPtr) - nodesByUserPtrLock.Unlock() C.ZT_GoNode_delete(n.gn) return nil, err } @@ -281,137 +299,20 @@ func NewNode(basePath string) (n *Node, err error) { n.runWaitGroup.Add(1) go func() { defer n.runWaitGroup.Done() - var previousExplicitExternalAddresses []ExternalAddress // empty at first so these will be configured lastMaintenanceRun := int64(0) - for atomic.LoadUint32(&n.running) != 0 { time.Sleep(500 * time.Millisecond) - nowS := time.Now().Unix() if (nowS - lastMaintenanceRun) >= 30 { lastMaintenanceRun = nowS - - ////////////////////////////////////////////////////////////////////// - n.localConfigLock.RLock() - - // Get local physical interface addresses, excluding blacklisted and ZeroTier-created interfaces - interfaceAddresses := make(map[string]net.IP) - ifs, _ := net.Interfaces() - if len(ifs) > 0 { - n.networksLock.RLock() - scanInterfaces: - for _, i := range ifs { - for _, bl := range n.localConfig.Settings.InterfacePrefixBlacklist { - if strings.HasPrefix(strings.ToLower(i.Name), strings.ToLower(bl)) { - continue scanInterfaces - } - } - m, _ := NewMACFromBytes(i.HardwareAddr) - if _, isZeroTier := n.networksByMAC[m]; !isZeroTier { - addrs, _ := i.Addrs() - for _, a := range addrs { - ipn, _ := a.(*net.IPNet) - if ipn != nil && len(ipn.IP) > 0 && !ipn.IP.IsLinkLocalUnicast() && !ipn.IP.IsMulticast() { - interfaceAddresses[ipn.IP.String()] = ipn.IP - } - } - } - } - n.networksLock.RUnlock() - } - - // Open or close locally bound UDP ports for each local interface address. - // This opens ports if they are not already open and then closes ports if - // they are open but no longer seem to exist. - interfaceAddressesChanged := false - ports := make([]int, 0, 2) - if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 { - ports = append(ports, n.localConfig.Settings.PrimaryPort) - } - if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 { - ports = append(ports, n.localConfig.Settings.SecondaryPort) - } - n.interfaceAddressesLock.Lock() - for astr, ipn := range interfaceAddresses { - if _, alreadyKnown := n.interfaceAddresses[astr]; !alreadyKnown { - interfaceAddressesChanged = true - ipCstr := C.CString(ipn.String()) - for pn, p := range ports { - n.infoLog.Printf("UDP binding to port %d on interface %s", p, astr) - primary := C.int(0) - if pn == 0 { - primary = 1 - } - C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(p), primary) - } - C.free(unsafe.Pointer(ipCstr)) - } - } - for astr, ipn := range n.interfaceAddresses { - if _, stillPresent := interfaceAddresses[astr]; !stillPresent { - interfaceAddressesChanged = true - ipCstr := C.CString(ipn.String()) - for _, p := range ports { - n.infoLog.Printf("UDP closing socket bound to port %d on interface %s", p, astr) - C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(p)) - } - C.free(unsafe.Pointer(ipCstr)) - } - } - n.interfaceAddresses = interfaceAddresses - n.interfaceAddressesLock.Unlock() - - // Update node's understanding of our interface addresses if they've changed - if interfaceAddressesChanged || reflect.DeepEqual(n.localConfig.Settings.ExplicitAddresses, previousExplicitExternalAddresses) { - previousExplicitExternalAddresses = n.localConfig.Settings.ExplicitAddresses - - // Consolidate explicit and detected interface addresses, removing duplicates. - externalAddresses := make(map[[3]uint64]*ExternalAddress) - for _, ip := range interfaceAddresses { - for _, p := range ports { - a := &ExternalAddress{ - InetAddress: InetAddress{ - IP: ip, - Port: p, - }, - Permanent: false, - } - externalAddresses[a.key()] = a - } - } - for _, a := range n.localConfig.Settings.ExplicitAddresses { - externalAddresses[a.key()] = &a - } - - if len(externalAddresses) > 0 { - cAddrs := make([]C.ZT_InterfaceAddress, len(externalAddresses)) - cAddrCount := 0 - for _, a := range externalAddresses { - makeSockaddrStorage(a.IP, a.Port, &(cAddrs[cAddrCount].address)) - if a.Permanent { - cAddrs[cAddrCount].permanent = 1 - } else { - cAddrs[cAddrCount].permanent = 0 - } - cAddrCount++ - } - C.ZT_Node_setInterfaceAddresses(n.zn, &cAddrs[0], C.uint(cAddrCount)) - } else { - C.ZT_Node_setInterfaceAddresses(n.zn, nil, 0) - } - } - - // Trim infoLog if it's gone over its size limit - if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil { - _ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true) - } - - n.localConfigLock.RUnlock() - ////////////////////////////////////////////////////////////////////// + n.runMaintenance() } } }() + // Stop deferred cPtr table cleanup function from deregistering this instance + cPtr = -1 + return n, nil } @@ -429,9 +330,8 @@ func (n *Node) Close() { n.runWaitGroup.Wait() - nodesByUserPtrLock.Lock() - delete(nodesByUserPtr, uintptr(unsafe.Pointer(n))) - nodesByUserPtrLock.Unlock() + cNodeRefs[n.cPtr] = nil + atomic.StoreUint32(&cNodeRefUsed[n.cPtr],0) } } @@ -457,7 +357,7 @@ func (n *Node) InterfaceAddresses() []net.IP { } // LocalConfig gets this node's local configuration -func (n *Node) LocalConfig() LocalConfig { +func (n *Node) LocalConfig() *LocalConfig { n.localConfigLock.RLock() defer n.localConfigLock.RUnlock() return n.localConfig @@ -477,24 +377,14 @@ 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 { + if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || + n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort || + n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax { restartRequired = true } - if lc.Settings.LogSizeMax < 0 { - n.infoLog = nullLogger - _ = n.infoLogW.Close() - n.infoLogW = nil - } else if n.infoLogW != nil { - n.infoLogW, err = sizeLimitWriterOpen(path.Join(n.basePath, "service.infoLog")) - if err == nil { - n.infoLog = log.New(n.infoLogW, "", log.LstdFlags) - } else { - n.infoLog = nullLogger - n.infoLogW = nil - } - } - n.localConfig = *lc + n.previousLocalConfig = n.localConfig + n.localConfig = lc return } @@ -555,30 +445,11 @@ func (n *Node) Leave(nwid NetworkID) error { return nil } -// AddRoot adds a root server with an optional bootstrap address for establishing first contact. -// If you're already using roots backed by proper global LF data stores the bootstrap address may -// be unnecessary as your node can probably find the new root automatically. -func (n *Node) AddRoot(id *Identity, bootstrap *InetAddress) error { - if id == nil { - return ErrInvalidParameter - } - var cBootstrap C.struct_sockaddr_storage - if bootstrap != nil { - makeSockaddrStorage(bootstrap.IP, bootstrap.Port, &cBootstrap) - } else { - zeroSockaddrStorage(&cBootstrap) - } - C.ZT_Node_addRoot(n.zn, nil, id.cIdentity(), &cBootstrap) +func (n *Node) AddRoot(spec []byte) error { return nil } -// RemoveRoot removes a root server for this node. -// This doesn't instantly close paths to the given peer or forget about it. It just -// demotes it to a normal peer. -func (n *Node) RemoveRoot(id *Identity) { - if id != nil { - C.ZT_Node_removeRoot(n.zn, nil, id.cIdentity()) - } +func (n *Node) RemoveRoot(address Address) { } // GetNetwork looks up a network by ID or returns nil if not joined @@ -618,7 +489,7 @@ func (n *Node) Peers() []*Peer { p2.Paths = make([]Path, 0, int(p.pathCount)) for j := 0; j < len(p2.Paths); j++ { - pt := (*C.ZT_PeerPhysicalPath)(unsafe.Pointer(uintptr(unsafe.Pointer(p.paths)) + uintptr(j * C.sizeof_ZT_PeerPhysicalPath))) + pt := (*C.ZT_PeerPhysicalPath)(unsafe.Pointer(uintptr(unsafe.Pointer(p.paths)) + uintptr(j*C.sizeof_ZT_PeerPhysicalPath))) if pt.alive != 0 { a := sockaddrStorageToUDPAddr(&pt.address) if a != nil { @@ -645,6 +516,116 @@ func (n *Node) Peers() []*Peer { // -------------------------------------------------------------------------------------------------------------------- +func (n *Node) runMaintenance() { + n.localConfigLock.RLock() + defer n.localConfigLock.RUnlock() + + // Get local physical interface addresses, excluding blacklisted and ZeroTier-created interfaces + interfaceAddresses := make(map[string]net.IP) + ifs, _ := net.Interfaces() + if len(ifs) > 0 { + n.networksLock.RLock() + scanInterfaces: + for _, i := range ifs { + for _, bl := range n.localConfig.Settings.InterfacePrefixBlacklist { + if strings.HasPrefix(strings.ToLower(i.Name), strings.ToLower(bl)) { + continue scanInterfaces + } + } + m, _ := NewMACFromBytes(i.HardwareAddr) + if _, isZeroTier := n.networksByMAC[m]; !isZeroTier { + addrs, _ := i.Addrs() + for _, a := range addrs { + ipn, _ := a.(*net.IPNet) + if ipn != nil && len(ipn.IP) > 0 && !ipn.IP.IsLinkLocalUnicast() && !ipn.IP.IsMulticast() { + interfaceAddresses[ipn.IP.String()] = ipn.IP + } + } + } + } + n.networksLock.RUnlock() + } + + // Open or close locally bound UDP ports for each local interface address. + // This opens ports if they are not already open and then closes ports if + // they are open but no longer seem to exist. + interfaceAddressesChanged := false + ports := make([]int, 0, 2) + if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 { + ports = append(ports, n.localConfig.Settings.PrimaryPort) + } + if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 { + ports = append(ports, n.localConfig.Settings.SecondaryPort) + } + n.interfaceAddressesLock.Lock() + for astr, ipn := range interfaceAddresses { + if _, alreadyKnown := n.interfaceAddresses[astr]; !alreadyKnown { + interfaceAddressesChanged = true + ipCstr := C.CString(ipn.String()) + for pn, p := range ports { + n.infoLog.Printf("UDP binding to port %d on interface %s", p, astr) + primary := C.int(0) + if pn == 0 { + primary = 1 + } + C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(p), primary) + } + C.free(unsafe.Pointer(ipCstr)) + } + } + for astr, ipn := range n.interfaceAddresses { + if _, stillPresent := interfaceAddresses[astr]; !stillPresent { + interfaceAddressesChanged = true + ipCstr := C.CString(ipn.String()) + for _, p := range ports { + n.infoLog.Printf("UDP closing socket bound to port %d on interface %s", p, astr) + C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(p)) + } + C.free(unsafe.Pointer(ipCstr)) + } + } + n.interfaceAddresses = interfaceAddresses + n.interfaceAddressesLock.Unlock() + + // Update node's interface address list if detected or configured addresses have changed. + if interfaceAddressesChanged || n.previousLocalConfig == nil || !reflect.DeepEqual(n.localConfig.Settings.ExplicitAddresses, n.previousLocalConfig.Settings.ExplicitAddresses) { + var cAddrs []C.ZT_InterfaceAddress + externalAddresses := make(map[[3]uint64]*InetAddress) + for _, a := range n.localConfig.Settings.ExplicitAddresses { + ak := a.key() + if _, have := externalAddresses[ak]; !have { + externalAddresses[ak] = &a + cAddrs = append(cAddrs, C.ZT_InterfaceAddress{}) + makeSockaddrStorage(a.IP, a.Port, &(cAddrs[len(cAddrs)-1].address)) + cAddrs[len(cAddrs)-1].permanent = 1 // explicit addresses are permanent, meaning they can be put in a locator + } + } + for _, ip := range interfaceAddresses { + for _, p := range ports { + a := InetAddress{ IP: ip, Port: p } + ak := a.key() + if _, have := externalAddresses[ak]; !have { + externalAddresses[ak] = &a + cAddrs = append(cAddrs, C.ZT_InterfaceAddress{}) + makeSockaddrStorage(a.IP, a.Port, &(cAddrs[len(cAddrs)-1].address)) + cAddrs[len(cAddrs)-1].permanent = 0 + } + } + } + + if len(cAddrs) > 0 { + C.ZT_Node_setInterfaceAddresses(n.zn, &cAddrs[0], C.uint(len(cAddrs))) + } else { + C.ZT_Node_setInterfaceAddresses(n.zn, nil, 0) + } + } + + // Trim infoLog if it's gone over its size limit + if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil { + _ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true) + } +} + func (n *Node) multicastSubscribe(nwid uint64, mg *MulticastGroup) { C.ZT_Node_multicastSubscribe(n.zn, nil, C.uint64_t(nwid), C.uint64_t(mg.MAC), C.ulong(mg.ADI)) } @@ -743,20 +724,17 @@ func (n *Node) handleTrace(traceMessage string) { } } -// func (n *Node) handleUserMessage(origin *Identity, messageTypeID uint64, data []byte) { -// } - -////////////////////////////////////////////////////////////////////////////// - // These are callbacks called by the core and GoGlue stuff to talk to the // service. These launch goroutines to do their work where possible to // avoid blocking anything in the core. //export goPathCheckFunc func goPathCheckFunc(gn, _ unsafe.Pointer, af C.int, ip unsafe.Pointer, _ C.int) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() + node := cNodeRefs[uintptr(gn)] + if node == nil { + return 0 + } + var nip net.IP if af == syscall.AF_INET { nip = ((*[4]byte)(ip))[:] @@ -765,17 +743,16 @@ func goPathCheckFunc(gn, _ unsafe.Pointer, af C.int, ip unsafe.Pointer, _ C.int) } else { return 0 } - if node != nil && len(nip) > 0 && node.pathCheck(nip) { + if len(nip) > 0 && node.pathCheck(nip) { return 1 } + return 0 } //export goPathLookupFunc func goPathLookupFunc(gn unsafe.Pointer, _ C.uint64_t, _ int, identity, familyP, ipP, portP unsafe.Pointer) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() + node := cNodeRefs[uintptr(gn)] if node == nil { return 0 } @@ -807,19 +784,17 @@ func goPathLookupFunc(gn unsafe.Pointer, _ C.uint64_t, _ int, identity, familyP, //export goStateObjectPutFunc func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { + node := cNodeRefs[uintptr(gn)] + if node == nil { + return + } + id2 := *((*[2]uint64)(id)) var data2 []byte if len > 0 { data2 = C.GoBytes(data, len) } - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - node.runWaitGroup.Add(1) go func() { if len < 0 { @@ -833,12 +808,11 @@ func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Poin //export goStateObjectGetFunc func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Pointer) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() + node := cNodeRefs[uintptr(gn)] if node == nil { return -1 } + *((*uintptr)(dataP)) = 0 tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) if found && len(tmp) > 0 { @@ -849,14 +823,13 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Poi *((*uintptr)(dataP)) = uintptr(cData) return C.int(len(tmp)) } + return -1 } //export goVirtualNetworkConfigFunc func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() + node := cNodeRefs[uintptr(gn)] if node == nil { return } @@ -916,12 +889,11 @@ func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, //export goZtEvent func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() + node := cNodeRefs[uintptr(gn)] if node == nil { return } + switch eventType { case C.ZT_EVENT_OFFLINE: atomic.StoreUint32(&node.online, 0) diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go index cb49308bf..5a372b8c1 100644 --- a/go/pkg/zerotier/peer.go +++ b/go/pkg/zerotier/peer.go @@ -27,7 +27,6 @@ type Peer struct { // PeerMutableFields contains only the mutable fields of Peer as nullable pointers. type PeerMutableFields struct { - Identity *Identity `json:"identity"` Root *bool `json:"root"` - Bootstrap *InetAddress `json:"bootstrap,omitempty"` + RootSpec []byte `json:"rootSpec"` } diff --git a/go/vendor/golang.org/x/sys/internal/unsafeheader/unsafeheader.go b/go/vendor/golang.org/x/sys/internal/unsafeheader/unsafeheader.go new file mode 100644 index 000000000..e07899b90 --- /dev/null +++ b/go/vendor/golang.org/x/sys/internal/unsafeheader/unsafeheader.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package unsafeheader contains header declarations for the Go runtime's +// slice and string implementations. +// +// This package allows x/sys to use types equivalent to +// reflect.SliceHeader and reflect.StringHeader without introducing +// a dependency on the (relatively heavy) "reflect" package. +package unsafeheader + +import ( + "unsafe" +) + +// Slice is the runtime representation of a slice. +// It cannot be used safely or portably and its representation may change in a later release. +type Slice struct { + Data unsafe.Pointer + Len int + Cap int +} + +// String is the runtime representation of a string. +// It cannot be used safely or portably and its representation may change in a later release. +type String struct { + Data unsafe.Pointer + Len int +} diff --git a/go/vendor/golang.org/x/sys/windows/dll_windows.go b/go/vendor/golang.org/x/sys/windows/dll_windows.go index d77711341..82076fb74 100644 --- a/go/vendor/golang.org/x/sys/windows/dll_windows.go +++ b/go/vendor/golang.org/x/sys/windows/dll_windows.go @@ -104,6 +104,35 @@ func (d *DLL) MustFindProc(name string) *Proc { return p } +// FindProcByOrdinal searches DLL d for procedure by ordinal and returns *Proc +// if found. It returns an error if search fails. +func (d *DLL) FindProcByOrdinal(ordinal uintptr) (proc *Proc, err error) { + a, e := GetProcAddressByOrdinal(d.Handle, ordinal) + name := "#" + itoa(int(ordinal)) + if e != nil { + return nil, &DLLError{ + Err: e, + ObjName: name, + Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), + } + } + p := &Proc{ + Dll: d, + Name: name, + addr: a, + } + return p, nil +} + +// MustFindProcByOrdinal is like FindProcByOrdinal but panics if search fails. +func (d *DLL) MustFindProcByOrdinal(ordinal uintptr) *Proc { + p, e := d.FindProcByOrdinal(ordinal) + if e != nil { + panic(e) + } + return p +} + // Release unloads DLL d from memory. func (d *DLL) Release() (err error) { return FreeLibrary(d.Handle) diff --git a/go/vendor/golang.org/x/sys/windows/env_windows.go b/go/vendor/golang.org/x/sys/windows/env_windows.go index f482a9fab..92ac05ff4 100644 --- a/go/vendor/golang.org/x/sys/windows/env_windows.go +++ b/go/vendor/golang.org/x/sys/windows/env_windows.go @@ -8,7 +8,6 @@ package windows import ( "syscall" - "unicode/utf16" "unsafe" ) @@ -40,17 +39,11 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) { defer DestroyEnvironmentBlock(block) blockp := uintptr(unsafe.Pointer(block)) for { - entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:] - for i, v := range entry { - if v == 0 { - entry = entry[:i] - break - } - } + entry := UTF16PtrToString((*uint16)(unsafe.Pointer(blockp))) if len(entry) == 0 { break } - env = append(env, string(utf16.Decode(entry))) + env = append(env, entry) blockp += 2 * (uintptr(len(entry)) + 1) } return env, nil diff --git a/go/vendor/golang.org/x/sys/windows/security_windows.go b/go/vendor/golang.org/x/sys/windows/security_windows.go index 4b6eff186..9e3c44a85 100644 --- a/go/vendor/golang.org/x/sys/windows/security_windows.go +++ b/go/vendor/golang.org/x/sys/windows/security_windows.go @@ -7,6 +7,8 @@ package windows import ( "syscall" "unsafe" + + "golang.org/x/sys/internal/unsafeheader" ) const ( @@ -1229,7 +1231,7 @@ func (sd *SECURITY_DESCRIPTOR) String() string { return "" } defer LocalFree(Handle(unsafe.Pointer(sddl))) - return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(sddl))[:]) + return UTF16PtrToString(sddl) } // ToAbsolute converts a self-relative security descriptor into an absolute one. @@ -1307,9 +1309,17 @@ func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURIT } func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR { - sdBytes := make([]byte, selfRelativeSD.Length()) - copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)]) - return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0])) + sdLen := (int)(selfRelativeSD.Length()) + + var src []byte + h := (*unsafeheader.Slice)(unsafe.Pointer(&src)) + h.Data = unsafe.Pointer(selfRelativeSD) + h.Len = sdLen + h.Cap = sdLen + + dst := make([]byte, sdLen) + copy(dst, src) + return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0])) } // SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a @@ -1391,6 +1401,6 @@ func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL } defer LocalFree(Handle(unsafe.Pointer(winHeapACL))) aclBytes := make([]byte, winHeapACL.aclSize) - copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes)]) + copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes):len(aclBytes)]) return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil } diff --git a/go/vendor/golang.org/x/sys/windows/syscall_windows.go b/go/vendor/golang.org/x/sys/windows/syscall_windows.go index 053d664d0..12c0544cb 100644 --- a/go/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/go/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -13,6 +13,8 @@ import ( "time" "unicode/utf16" "unsafe" + + "golang.org/x/sys/internal/unsafeheader" ) type Handle uintptr @@ -117,6 +119,32 @@ func UTF16PtrFromString(s string) (*uint16, error) { return &a[0], nil } +// UTF16PtrToString takes a pointer to a UTF-16 sequence and returns the corresponding UTF-8 encoded string. +// If the pointer is nil, this returns the empty string. This assumes that the UTF-16 sequence is terminated +// at a zero word; if the zero word is not present, the program may crash. +func UTF16PtrToString(p *uint16) string { + if p == nil { + return "" + } + if *p == 0 { + return "" + } + + // Find NUL terminator. + n := 0 + for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ { + ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p)) + } + + var s []uint16 + h := (*unsafeheader.Slice)(unsafe.Pointer(&s)) + h.Data = unsafe.Pointer(p) + h.Len = n + h.Cap = n + + return string(utf16.Decode(s)) +} + func Getpagesize() int { return 4096 } // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. @@ -1181,7 +1209,12 @@ type IPv6Mreq struct { Interface uint32 } -func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, syscall.EWINDOWS } +func GetsockoptInt(fd Handle, level, opt int) (int, error) { + v := int32(0) + l := int32(unsafe.Sizeof(v)) + err := Getsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), &l) + return int(v), err +} func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)} @@ -1378,7 +1411,7 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e return "", err } defer CoTaskMemFree(unsafe.Pointer(p)) - return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil + return UTF16PtrToString(p), nil } // RtlGetVersion returns the version of the underlying operating system, ignoring diff --git a/go/vendor/modules.txt b/go/vendor/modules.txt index 2131072e2..d10ed15e6 100644 --- a/go/vendor/modules.txt +++ b/go/vendor/modules.txt @@ -4,5 +4,6 @@ github.com/Microsoft/go-winio/pkg/guid # github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 github.com/hectane/go-acl github.com/hectane/go-acl/api -# golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 +# golang.org/x/sys v0.0.0-20200523222454-059865788121 +golang.org/x/sys/internal/unsafeheader golang.org/x/sys/windows diff --git a/node/Identity.hpp b/node/Identity.hpp index 118dcc9b8..f4a2fc02d 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -24,10 +24,12 @@ #include "Fingerprint.hpp" #include "Containers.hpp" -#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 -#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + ZT_ECC384_PUBLIC_KEY_SIZE) +#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 +#define ZT_IDENTITY_C25519_PUBLIC_KEY_SIZE ZT_C25519_COMBINED_PUBLIC_KEY_SIZE +#define ZT_IDENTITY_C25519_PRIVATE_KEY_SIZE ZT_C25519_COMBINED_PRIVATE_KEY_SIZE +#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + ZT_ECC384_PUBLIC_KEY_SIZE) #define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_COMBINED_PRIVATE_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE) -#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) +#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) namespace ZeroTier {