Golang fixes

This commit is contained in:
Adam Ierymenko 2020-05-25 14:23:48 -07:00
parent c881094202
commit 964c235ecf
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
15 changed files with 349 additions and 293 deletions

View file

@ -63,7 +63,7 @@ Commands:
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
makeroot <identity> <address> [...] Make a root spec (see docs)
makeroot <identity> <address[,...]> 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<maxCNodeRefs;i++ {
if atomic.CompareAndSwapUint32(&cNodeRefUsed[i],0,1) {
cNodeRefs[i] = n
cPtr = i
n.cPtr = uintptr(i)
break
}
}
if cPtr < 0 {
return nil, errors.New("too many nodes in this instance")
}
defer func() {
if cPtr >= 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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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