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 validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature 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. 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 This is typically run from launchd (Mac), systemd or init (Linux), a Windows

View file

@ -5,5 +5,5 @@ go 1.13
require ( require (
github.com/Microsoft/go-winio v0.4.14 github.com/Microsoft/go-winio v0.4.14
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 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-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 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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 "C"
import ( import (
"encoding/base32"
"errors" "errors"
"strings" "strings"
"unsafe" "unsafe"
) )
var ztBase32 = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
type Fingerprint struct { type Fingerprint struct {
Address Address `json:"address"` Address Address `json:"address"`
Hash [48]byte `json:"hash"` Hash [48]byte `json:"hash"`
} }
func NewFingerprintFromString(fps string) (*Fingerprint, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -49,7 +46,7 @@ func (fp *Fingerprint) String() string {
var tmp [53]byte var tmp [53]byte
fp.Address.CopyTo(tmp[0:5]) fp.Address.CopyTo(tmp[0:5])
copy(tmp[5:],fp.Hash[:]) copy(tmp[5:],fp.Hash[:])
return ztBase32.EncodeToString(tmp[:]) return Base32StdLowerCase.EncodeToString(tmp[:])
} }
func (fp *Fingerprint) apiFingerprint() *C.ZT_Fingerprint { func (fp *Fingerprint) apiFingerprint() *C.ZT_Fingerprint {

View file

@ -27,18 +27,14 @@ import (
"unsafe" "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 ( const (
IdentityTypeC25519PublicKeySize = 64 // C25519/Ed25519 keys IdentityTypeC25519 = C.ZT_IDENTITY_TYPE_C25519
IdentityTypeC25519PrivateKeySize = 64 // C25519/Ed25519 private keys IdentityTypeP384 = C.ZT_IDENTITY_TYPE_P384
IdentityTypeP384PublicKeySize = 209 // C25519/Ed25519, P-384 point-compressed public, P-384 self-signature
IdentityTypeP384PrivateKeySize = 112 // C25519/Ed25519 and P-384 private keys 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 // 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 return id, nil
} }
// cIdentity returns a pointer to the core ZT_Identity instance or nil/0 on error. // initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error.
func (id *Identity) cIdentity() unsafe.Pointer { func (id *Identity) initCIdentityPtr() bool {
if uintptr(id.cid) == 0 { if uintptr(id.cid) == 0 {
idCStr := C.CString(id.String()) idCStr := C.CString(id.String())
defer C.free(unsafe.Pointer(idCStr)) defer C.free(unsafe.Pointer(idCStr))
id.cid = C.ZT_Identity_fromString(idCStr) id.cid = C.ZT_Identity_fromString(idCStr)
if uintptr(id.cid) == 0 { if uintptr(id.cid) == 0 {
return nil return false
} }
runtime.SetFinalizer(id, identityFinalizer) runtime.SetFinalizer(id, identityFinalizer)
} }
return id.cid return true
} }
// NewIdentity generates a new identity of the selected type // 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 // LocallyValidate performs local self-validation of this identity
func (id *Identity) LocallyValidate() bool { func (id *Identity) LocallyValidate() bool {
id.cIdentity() if !id.initCIdentityPtr() {
if uintptr(id.cid) == 0 {
return false return false
} }
return C.ZT_Identity_validate(id.cid) != 0 return C.ZT_Identity_validate(id.cid) != 0
@ -206,8 +201,7 @@ func (id *Identity) LocallyValidate() bool {
// Sign signs a message with this identity // Sign signs a message with this identity
func (id *Identity) Sign(msg []byte) ([]byte, error) { func (id *Identity) Sign(msg []byte) ([]byte, error) {
id.cIdentity() if !id.initCIdentityPtr() {
if uintptr(id.cid) == 0 {
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
@ -226,15 +220,9 @@ func (id *Identity) Sign(msg []byte) ([]byte, error) {
// Verify verifies a signature // Verify verifies a signature
func (id *Identity) Verify(msg, sig []byte) bool { func (id *Identity) Verify(msg, sig []byte) bool {
if len(sig) == 0 { if len(sig) == 0 || !id.initCIdentityPtr() {
return false return false
} }
id.cIdentity()
if uintptr(id.cid) == 0 {
return false
}
var dataP unsafe.Pointer var dataP unsafe.Pointer
if len(msg) > 0 { if len(msg) > 0 {
dataP = unsafe.Pointer(&msg[0]) dataP = unsafe.Pointer(&msg[0])
@ -247,9 +235,7 @@ func (id *Identity) MakeRoot(addresses []InetAddress) ([]byte, error) {
if len(addresses) == 0 { if len(addresses) == 0 {
return nil, errors.New("at least one static address must be specified for a root") return nil, errors.New("at least one static address must be specified for a root")
} }
if !id.initCIdentityPtr() {
id.cIdentity()
if uintptr(id.cid) == 0 {
return nil, errors.New("error initializing ZT_Identity") return nil, errors.New("error initializing ZT_Identity")
} }

View file

@ -159,17 +159,20 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast
} }
func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) {
go func() { node := cNodeRefs[uintptr(gn)]
nodesByUserPtrLock.RLock()
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil { if node == nil {
return return
} }
node.networksLock.RLock() node.networksLock.RLock()
network := node.networks[NetworkID(nwid)] network := node.networks[NetworkID(nwid)]
node.networksLock.RUnlock() node.networksLock.RUnlock()
if network != nil { if network == nil {
return
}
node.runWaitGroup.Add(1)
go func() {
defer node.runWaitGroup.Done()
tap, _ := network.tap.(*nativeTap) tap, _ := network.tap.(*nativeTap)
if tap != nil { if tap != nil {
mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)} mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)}
@ -179,7 +182,6 @@ func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi
h(added, mg) h(added, mg)
} }
} }
}
}() }()
} }

View file

@ -45,7 +45,6 @@ import (
var nullLogger = log.New(ioutil.Discard, "", 0) var nullLogger = log.New(ioutil.Discard, "", 0)
// Network status states
const ( const (
NetworkIDStringLength = 16 NetworkIDStringLength = 16
AddressStringLength = 10 AddressStringLength = 10
@ -62,16 +61,17 @@ const (
networkConfigOpUpdate int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE networkConfigOpUpdate int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE
defaultVirtualNetworkMTU = C.ZT_DEFAULT_MTU 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 ( var (
// PlatformDefaultHomePath is the default location of ZeroTier's working path on this system // PlatformDefaultHomePath is the default location of ZeroTier's working path on this system
PlatformDefaultHomePath string = C.GoString(C.ZT_PLATFORM_DEFAULT_HOMEPATH) 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 cNodeRefs [maxCNodeRefs]*Node
nodesByUserPtr = make(map[uintptr]*Node) cNodeRefUsed [maxCNodeRefs]uint32
nodesByUserPtrCtr = uintptr(0)
nodesByUserPtrLock sync.RWMutex
CoreVersionMajor int CoreVersionMajor int
CoreVersionMinor int CoreVersionMinor int
@ -90,6 +90,9 @@ func init() {
// Node is an instance of a virtual port on the global switch. // Node is an instance of a virtual port on the global switch.
type Node struct { 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 contains networks we have joined, and networksByMAC by their local MAC address
networks map[NetworkID]*Network networks map[NetworkID]*Network
networksByMAC map[MAC]*Network // locked by networksLock networksByMAC map[MAC]*Network // locked by networksLock
@ -107,9 +110,12 @@ type Node struct {
peersPath string peersPath string
networksPath string networksPath string
localConfigPath string localConfigPath string
infoLogPath string
errorLogPath string
// localConfig is the current state of local.conf // localConfig is the current state of local.conf
localConfig LocalConfig localConfig *LocalConfig
previousLocalConfig *LocalConfig
localConfigLock sync.RWMutex localConfigLock sync.RWMutex
// logs for information, errors, and trace output // logs for information, errors, and trace output
@ -120,13 +126,13 @@ type Node struct {
errLog *log.Logger errLog *log.Logger
traceLog *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 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 zn unsafe.Pointer
// id is the identity of this node // id is the identity of this node (includes private key)
id *Identity id *Identity
// HTTP server instances: one for a named socket (Unix domain or Windows named pipe) and one on a local TCP socket // 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 is used to wait for all node goroutines on shutdown
runWaitGroup sync.WaitGroup 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 // NewNode creates and initializes a new instance of the ZeroTier node service
func NewNode(basePath string) (n *Node, err error) { func NewNode(basePath string) (n *Node, err error) {
n = new(Node) 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.networks = make(map[NetworkID]*Network)
n.networksByMAC = make(map[MAC]*Network) n.networksByMAC = make(map[MAC]*Network)
n.interfaceAddresses = make(map[string]net.IP) 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") n.localConfigPath = path.Join(basePath, "local.conf")
_, identitySecretNotFoundErr := os.Stat(path.Join(basePath, "identity.secret")) _, identitySecretNotFoundErr := os.Stat(path.Join(basePath, "identity.secret"))
n.localConfig = new(LocalConfig)
err = n.localConfig.Read(n.localConfigPath, true, identitySecretNotFoundErr != nil) err = n.localConfig.Read(n.localConfigPath, true, identitySecretNotFoundErr != nil)
if err != nil { if err != nil {
return return
} }
n.infoLogPath = path.Join(basePath, "info.log")
n.errorLogPath = path.Join(basePath, "error.log")
if n.localConfig.Settings.LogSizeMax >= 0 { if n.localConfig.Settings.LogSizeMax >= 0 {
n.infoLogW, err = sizeLimitWriterOpen(path.Join(basePath, "info.log")) n.infoLogW, err = sizeLimitWriterOpen(n.infoLogPath)
if err != nil { if err != nil {
return return
} }
n.errLogW, err = sizeLimitWriterOpen(path.Join(basePath, "error.log")) n.errLogW, err = sizeLimitWriterOpen(n.errorLogPath)
if err != nil { if err != nil {
return return
} }
@ -186,6 +215,7 @@ func NewNode(basePath string) (n *Node, err error) {
n.errLog = log.New(n.errLogW, "", log.LstdFlags) n.errLog = log.New(n.errLogW, "", log.LstdFlags)
} else { } else {
n.infoLog = nullLogger n.infoLog = nullLogger
n.errLog = nullLogger
} }
if n.localConfig.Settings.PortSearch { if n.localConfig.Settings.PortSearch {
@ -249,29 +279,17 @@ func NewNode(basePath string) (n *Node, err error) {
return nil, err return nil, err
} }
nodesByUserPtrLock.Lock()
nodesByUserPtrCtr++
n.cPtr = nodesByUserPtrCtr
nodesByUserPtr[n.cPtr] = n
nodesByUserPtrLock.Unlock()
cPath := C.CString(basePath) cPath := C.CString(basePath)
n.gn = C.ZT_GoNode_new(cPath, C.uintptr_t(n.cPtr)) n.gn = C.ZT_GoNode_new(cPath, C.uintptr_t(n.cPtr))
C.free(unsafe.Pointer(cPath)) C.free(unsafe.Pointer(cPath))
if n.gn == nil { if n.gn == nil {
n.infoLog.Println("FATAL: node initialization failed") n.infoLog.Println("FATAL: node initialization failed")
nodesByUserPtrLock.Lock()
delete(nodesByUserPtr, n.cPtr)
nodesByUserPtrLock.Unlock()
return nil, ErrNodeInitFailed return nil, ErrNodeInitFailed
} }
n.zn = unsafe.Pointer(C.ZT_GoNode_getNode(n.gn)) n.zn = unsafe.Pointer(C.ZT_GoNode_getNode(n.gn))
n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(n.zn)) n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(n.zn))
if err != nil { if err != nil {
n.infoLog.Printf("FATAL: error obtaining node's identity") n.infoLog.Printf("FATAL: error obtaining node's identity")
nodesByUserPtrLock.Lock()
delete(nodesByUserPtr, n.cPtr)
nodesByUserPtrLock.Unlock()
C.ZT_GoNode_delete(n.gn) C.ZT_GoNode_delete(n.gn)
return nil, err return nil, err
} }
@ -281,18 +299,226 @@ func NewNode(basePath string) (n *Node, err error) {
n.runWaitGroup.Add(1) n.runWaitGroup.Add(1)
go func() { go func() {
defer n.runWaitGroup.Done() defer n.runWaitGroup.Done()
var previousExplicitExternalAddresses []ExternalAddress // empty at first so these will be configured
lastMaintenanceRun := int64(0) lastMaintenanceRun := int64(0)
for atomic.LoadUint32(&n.running) != 0 { for atomic.LoadUint32(&n.running) != 0 {
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
nowS := time.Now().Unix() nowS := time.Now().Unix()
if (nowS - lastMaintenanceRun) >= 30 { if (nowS - lastMaintenanceRun) >= 30 {
lastMaintenanceRun = nowS lastMaintenanceRun = nowS
n.runMaintenance()
}
}
}()
////////////////////////////////////////////////////////////////////// // Stop deferred cPtr table cleanup function from deregistering this instance
cPtr = -1
return n, nil
}
// Close closes this Node and frees its underlying C++ Node structures
func (n *Node) Close() {
if atomic.SwapUint32(&n.running, 0) != 0 {
if n.namedSocketApiServer != nil {
_ = n.namedSocketApiServer.Close()
}
if n.tcpApiServer != nil {
_ = n.tcpApiServer.Close()
}
C.ZT_GoNode_delete(n.gn)
n.runWaitGroup.Wait()
cNodeRefs[n.cPtr] = nil
atomic.StoreUint32(&cNodeRefUsed[n.cPtr],0)
}
}
// Address returns this node's address
func (n *Node) Address() Address { return n.id.address }
// Identity returns this node's identity (including secret portion)
func (n *Node) Identity() *Identity { return n.id }
// Online returns true if this node can reach something
func (n *Node) Online() bool { return atomic.LoadUint32(&n.online) != 0 }
// InterfaceAddresses are external IPs belonging to physical interfaces on this machine
func (n *Node) InterfaceAddresses() []net.IP {
var ea []net.IP
n.interfaceAddressesLock.Lock()
for _, a := range n.interfaceAddresses {
ea = append(ea, a)
}
n.interfaceAddressesLock.Unlock()
sort.Slice(ea, func(a, b int) bool { return bytes.Compare(ea[a], ea[b]) < 0 })
return ea
}
// LocalConfig gets this node's local configuration
func (n *Node) LocalConfig() *LocalConfig {
n.localConfigLock.RLock() n.localConfigLock.RLock()
defer n.localConfigLock.RUnlock()
return n.localConfig
}
// SetLocalConfig updates this node's local configuration
func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error) {
n.networksLock.RLock()
n.localConfigLock.Lock()
defer n.localConfigLock.Unlock()
defer n.networksLock.RUnlock()
for nid, nc := range lc.Network {
nw := n.networks[nid]
if nw != nil {
nw.SetLocalSettings(&nc)
}
}
if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort ||
n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort ||
n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax {
restartRequired = true
}
n.previousLocalConfig = n.localConfig
n.localConfig = lc
return
}
// 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, 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)
if settings != nil {
nw.SetLocalSettings(settings)
}
return nw, nil
}
n.networksLock.RUnlock()
if tap != nil {
panic("non-native taps not yet implemented")
}
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
}
nw, err := newNetwork(n, nwid, &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1})
if err != nil {
n.infoLog.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error())
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
return nil, err
}
n.networksLock.Lock()
n.networks[nwid] = nw
n.networksLock.Unlock()
if settings != nil {
nw.SetLocalSettings(settings)
}
return nw, nil
}
// Leave a network.
func (n *Node) Leave(nwid NetworkID) error {
n.networksLock.Lock()
nw := n.networks[nwid]
delete(n.networks, nwid)
n.networksLock.Unlock()
if nw != nil {
n.infoLog.Printf("leaving network %.16x", nwid)
nw.leaving()
}
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
return nil
}
func (n *Node) AddRoot(spec []byte) error {
return nil
}
func (n *Node) RemoveRoot(address Address) {
}
// GetNetwork looks up a network by ID or returns nil if not joined
func (n *Node) GetNetwork(nwid NetworkID) *Network {
n.networksLock.RLock()
nw := n.networks[nwid]
n.networksLock.RUnlock()
return nw
}
// Networks returns a list of networks that this node has joined
func (n *Node) Networks() []*Network {
var nws []*Network
n.networksLock.RLock()
for _, nw := range n.networks {
nws = append(nws, nw)
}
n.networksLock.RUnlock()
return nws
}
// Peers retrieves a list of current peers
func (n *Node) Peers() []*Peer {
var peers []*Peer
pl := C.ZT_Node_peers(n.zn)
if pl != nil {
for i := uintptr(0); i < uintptr(pl.peerCount); i++ {
p := (*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_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.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)}
p2.Latency = int(p.latency)
p2.Root = p.root != 0
p2.Bootstrap = NewInetAddressFromSockaddr(unsafe.Pointer(&p.bootstrap))
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)))
if pt.alive != 0 {
a := sockaddrStorageToUDPAddr(&pt.address)
if a != nil {
p2.Paths = append(p2.Paths, Path{
IP: a.IP,
Port: a.Port,
LastSend: int64(pt.lastSend),
LastReceive: int64(pt.lastReceive),
TrustedPathID: uint64(pt.trustedPathId),
})
}
}
}
peers = append(peers, p2)
}
C.ZT_Node_freeQueryResult(n.zn, unsafe.Pointer(pl))
}
sort.Slice(peers, func(a, b int) bool {
return peers[a].Address < peers[b].Address
})
return peers
}
// --------------------------------------------------------------------------------------------------------------------
func (n *Node) runMaintenance() {
n.localConfigLock.RLock()
defer n.localConfigLock.RUnlock()
// Get local physical interface addresses, excluding blacklisted and ZeroTier-created interfaces // Get local physical interface addresses, excluding blacklisted and ZeroTier-created interfaces
interfaceAddresses := make(map[string]net.IP) interfaceAddresses := make(map[string]net.IP)
@ -361,41 +587,34 @@ func NewNode(basePath string) (n *Node, err error) {
n.interfaceAddresses = interfaceAddresses n.interfaceAddresses = interfaceAddresses
n.interfaceAddressesLock.Unlock() n.interfaceAddressesLock.Unlock()
// Update node's understanding of our interface addresses if they've changed // Update node's interface address list if detected or configured addresses have changed.
if interfaceAddressesChanged || reflect.DeepEqual(n.localConfig.Settings.ExplicitAddresses, previousExplicitExternalAddresses) { if interfaceAddressesChanged || n.previousLocalConfig == nil || !reflect.DeepEqual(n.localConfig.Settings.ExplicitAddresses, n.previousLocalConfig.Settings.ExplicitAddresses) {
previousExplicitExternalAddresses = n.localConfig.Settings.ExplicitAddresses var cAddrs []C.ZT_InterfaceAddress
externalAddresses := make(map[[3]uint64]*InetAddress)
// Consolidate explicit and detected interface addresses, removing duplicates. for _, a := range n.localConfig.Settings.ExplicitAddresses {
externalAddresses := make(map[[3]uint64]*ExternalAddress) 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 _, ip := range interfaceAddresses {
for _, p := range ports { for _, p := range ports {
a := &ExternalAddress{ a := InetAddress{ IP: ip, Port: p }
InetAddress: InetAddress{ ak := a.key()
IP: ip, if _, have := externalAddresses[ak]; !have {
Port: p, externalAddresses[ak] = &a
}, cAddrs = append(cAddrs, C.ZT_InterfaceAddress{})
Permanent: false, makeSockaddrStorage(a.IP, a.Port, &(cAddrs[len(cAddrs)-1].address))
} cAddrs[len(cAddrs)-1].permanent = 0
externalAddresses[a.key()] = a
} }
} }
for _, a := range n.localConfig.Settings.ExplicitAddresses {
externalAddresses[a.key()] = &a
} }
if len(externalAddresses) > 0 { if len(cAddrs) > 0 {
cAddrs := make([]C.ZT_InterfaceAddress, len(externalAddresses)) C.ZT_Node_setInterfaceAddresses(n.zn, &cAddrs[0], C.uint(len(cAddrs)))
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 { } else {
C.ZT_Node_setInterfaceAddresses(n.zn, nil, 0) C.ZT_Node_setInterfaceAddresses(n.zn, nil, 0)
} }
@ -405,246 +624,8 @@ func NewNode(basePath string) (n *Node, err error) {
if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil { if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil {
_ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true) _ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true)
} }
n.localConfigLock.RUnlock()
//////////////////////////////////////////////////////////////////////
}
}
}()
return n, nil
} }
// Close closes this Node and frees its underlying C++ Node structures
func (n *Node) Close() {
if atomic.SwapUint32(&n.running, 0) != 0 {
if n.namedSocketApiServer != nil {
_ = n.namedSocketApiServer.Close()
}
if n.tcpApiServer != nil {
_ = n.tcpApiServer.Close()
}
C.ZT_GoNode_delete(n.gn)
n.runWaitGroup.Wait()
nodesByUserPtrLock.Lock()
delete(nodesByUserPtr, uintptr(unsafe.Pointer(n)))
nodesByUserPtrLock.Unlock()
}
}
// Address returns this node's address
func (n *Node) Address() Address { return n.id.address }
// Identity returns this node's identity (including secret portion)
func (n *Node) Identity() *Identity { return n.id }
// Online returns true if this node can reach something
func (n *Node) Online() bool { return atomic.LoadUint32(&n.online) != 0 }
// InterfaceAddresses are external IPs belonging to physical interfaces on this machine
func (n *Node) InterfaceAddresses() []net.IP {
var ea []net.IP
n.interfaceAddressesLock.Lock()
for _, a := range n.interfaceAddresses {
ea = append(ea, a)
}
n.interfaceAddressesLock.Unlock()
sort.Slice(ea, func(a, b int) bool { return bytes.Compare(ea[a], ea[b]) < 0 })
return ea
}
// LocalConfig gets this node's local configuration
func (n *Node) LocalConfig() LocalConfig {
n.localConfigLock.RLock()
defer n.localConfigLock.RUnlock()
return n.localConfig
}
// SetLocalConfig updates this node's local configuration
func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error) {
n.networksLock.RLock()
n.localConfigLock.Lock()
defer n.localConfigLock.Unlock()
defer n.networksLock.RUnlock()
for nid, nc := range lc.Network {
nw := n.networks[nid]
if nw != nil {
nw.SetLocalSettings(&nc)
}
}
if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort {
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
return
}
// 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, 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)
if settings != nil {
nw.SetLocalSettings(settings)
}
return nw, nil
}
n.networksLock.RUnlock()
if tap != nil {
panic("non-native taps not yet implemented")
}
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
}
nw, err := newNetwork(n, nwid, &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1})
if err != nil {
n.infoLog.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error())
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
return nil, err
}
n.networksLock.Lock()
n.networks[nwid] = nw
n.networksLock.Unlock()
if settings != nil {
nw.SetLocalSettings(settings)
}
return nw, nil
}
// Leave a network.
func (n *Node) Leave(nwid NetworkID) error {
n.networksLock.Lock()
nw := n.networks[nwid]
delete(n.networks, nwid)
n.networksLock.Unlock()
if nw != nil {
n.infoLog.Printf("leaving network %.16x", nwid)
nw.leaving()
}
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
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)
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())
}
}
// GetNetwork looks up a network by ID or returns nil if not joined
func (n *Node) GetNetwork(nwid NetworkID) *Network {
n.networksLock.RLock()
nw := n.networks[nwid]
n.networksLock.RUnlock()
return nw
}
// Networks returns a list of networks that this node has joined
func (n *Node) Networks() []*Network {
var nws []*Network
n.networksLock.RLock()
for _, nw := range n.networks {
nws = append(nws, nw)
}
n.networksLock.RUnlock()
return nws
}
// Peers retrieves a list of current peers
func (n *Node) Peers() []*Peer {
var peers []*Peer
pl := C.ZT_Node_peers(n.zn)
if pl != nil {
for i := uintptr(0); i < uintptr(pl.peerCount); i++ {
p := (*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_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.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)}
p2.Latency = int(p.latency)
p2.Root = p.root != 0
p2.Bootstrap = NewInetAddressFromSockaddr(unsafe.Pointer(&p.bootstrap))
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)))
if pt.alive != 0 {
a := sockaddrStorageToUDPAddr(&pt.address)
if a != nil {
p2.Paths = append(p2.Paths, Path{
IP: a.IP,
Port: a.Port,
LastSend: int64(pt.lastSend),
LastReceive: int64(pt.lastReceive),
TrustedPathID: uint64(pt.trustedPathId),
})
}
}
}
peers = append(peers, p2)
}
C.ZT_Node_freeQueryResult(n.zn, unsafe.Pointer(pl))
}
sort.Slice(peers, func(a, b int) bool {
return peers[a].Address < peers[b].Address
})
return peers
}
// --------------------------------------------------------------------------------------------------------------------
func (n *Node) multicastSubscribe(nwid uint64, mg *MulticastGroup) { 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)) 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 // 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 // service. These launch goroutines to do their work where possible to
// avoid blocking anything in the core. // avoid blocking anything in the core.
//export goPathCheckFunc //export goPathCheckFunc
func goPathCheckFunc(gn, _ unsafe.Pointer, af C.int, ip unsafe.Pointer, _ C.int) C.int { func goPathCheckFunc(gn, _ unsafe.Pointer, af C.int, ip unsafe.Pointer, _ C.int) C.int {
nodesByUserPtrLock.RLock() node := cNodeRefs[uintptr(gn)]
node := nodesByUserPtr[uintptr(gn)] if node == nil {
nodesByUserPtrLock.RUnlock() return 0
}
var nip net.IP var nip net.IP
if af == syscall.AF_INET { if af == syscall.AF_INET {
nip = ((*[4]byte)(ip))[:] nip = ((*[4]byte)(ip))[:]
@ -765,17 +743,16 @@ func goPathCheckFunc(gn, _ unsafe.Pointer, af C.int, ip unsafe.Pointer, _ C.int)
} else { } else {
return 0 return 0
} }
if node != nil && len(nip) > 0 && node.pathCheck(nip) { if len(nip) > 0 && node.pathCheck(nip) {
return 1 return 1
} }
return 0 return 0
} }
//export goPathLookupFunc //export goPathLookupFunc
func goPathLookupFunc(gn unsafe.Pointer, _ C.uint64_t, _ int, identity, familyP, ipP, portP unsafe.Pointer) C.int { func goPathLookupFunc(gn unsafe.Pointer, _ C.uint64_t, _ int, identity, familyP, ipP, portP unsafe.Pointer) C.int {
nodesByUserPtrLock.RLock() node := cNodeRefs[uintptr(gn)]
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil { if node == nil {
return 0 return 0
} }
@ -807,19 +784,17 @@ func goPathLookupFunc(gn unsafe.Pointer, _ C.uint64_t, _ int, identity, familyP,
//export goStateObjectPutFunc //export goStateObjectPutFunc
func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { 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)) id2 := *((*[2]uint64)(id))
var data2 []byte var data2 []byte
if len > 0 { if len > 0 {
data2 = C.GoBytes(data, len) data2 = C.GoBytes(data, len)
} }
nodesByUserPtrLock.RLock()
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil {
return
}
node.runWaitGroup.Add(1) node.runWaitGroup.Add(1)
go func() { go func() {
if len < 0 { if len < 0 {
@ -833,12 +808,11 @@ func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Poin
//export goStateObjectGetFunc //export goStateObjectGetFunc
func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Pointer) C.int { func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Pointer) C.int {
nodesByUserPtrLock.RLock() node := cNodeRefs[uintptr(gn)]
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil { if node == nil {
return -1 return -1
} }
*((*uintptr)(dataP)) = 0 *((*uintptr)(dataP)) = 0
tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id)))
if found && len(tmp) > 0 { 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) *((*uintptr)(dataP)) = uintptr(cData)
return C.int(len(tmp)) return C.int(len(tmp))
} }
return -1 return -1
} }
//export goVirtualNetworkConfigFunc //export goVirtualNetworkConfigFunc
func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) { func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
nodesByUserPtrLock.RLock() node := cNodeRefs[uintptr(gn)]
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil { if node == nil {
return return
} }
@ -916,12 +889,11 @@ func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int,
//export goZtEvent //export goZtEvent
func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) {
nodesByUserPtrLock.RLock() node := cNodeRefs[uintptr(gn)]
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil { if node == nil {
return return
} }
switch eventType { switch eventType {
case C.ZT_EVENT_OFFLINE: case C.ZT_EVENT_OFFLINE:
atomic.StoreUint32(&node.online, 0) 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. // PeerMutableFields contains only the mutable fields of Peer as nullable pointers.
type PeerMutableFields struct { type PeerMutableFields struct {
Identity *Identity `json:"identity"`
Root *bool `json:"root"` 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 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. // Release unloads DLL d from memory.
func (d *DLL) Release() (err error) { func (d *DLL) Release() (err error) {
return FreeLibrary(d.Handle) return FreeLibrary(d.Handle)

View file

@ -8,7 +8,6 @@ package windows
import ( import (
"syscall" "syscall"
"unicode/utf16"
"unsafe" "unsafe"
) )
@ -40,17 +39,11 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
defer DestroyEnvironmentBlock(block) defer DestroyEnvironmentBlock(block)
blockp := uintptr(unsafe.Pointer(block)) blockp := uintptr(unsafe.Pointer(block))
for { for {
entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:] entry := UTF16PtrToString((*uint16)(unsafe.Pointer(blockp)))
for i, v := range entry {
if v == 0 {
entry = entry[:i]
break
}
}
if len(entry) == 0 { if len(entry) == 0 {
break break
} }
env = append(env, string(utf16.Decode(entry))) env = append(env, entry)
blockp += 2 * (uintptr(len(entry)) + 1) blockp += 2 * (uintptr(len(entry)) + 1)
} }
return env, nil return env, nil

View file

@ -7,6 +7,8 @@ package windows
import ( import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
const ( const (
@ -1229,7 +1231,7 @@ func (sd *SECURITY_DESCRIPTOR) String() string {
return "" return ""
} }
defer LocalFree(Handle(unsafe.Pointer(sddl))) 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. // 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 { func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR {
sdBytes := make([]byte, selfRelativeSD.Length()) sdLen := (int)(selfRelativeSD.Length())
copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)])
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0])) 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 // 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))) defer LocalFree(Handle(unsafe.Pointer(winHeapACL)))
aclBytes := make([]byte, winHeapACL.aclSize) 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 return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil
} }

View file

@ -13,6 +13,8 @@ import (
"time" "time"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
type Handle uintptr type Handle uintptr
@ -117,6 +119,32 @@ func UTF16PtrFromString(s string) (*uint16, error) {
return &a[0], nil 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 } func Getpagesize() int { return 4096 }
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
@ -1181,7 +1209,12 @@ type IPv6Mreq struct {
Interface uint32 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) { func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) {
sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)} 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 return "", err
} }
defer CoTaskMemFree(unsafe.Pointer(p)) 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 // 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 v0.0.0-20190604041725-da78bae5fc95
github.com/hectane/go-acl github.com/hectane/go-acl
github.com/hectane/go-acl/api 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 golang.org/x/sys/windows

View file

@ -25,6 +25,8 @@
#include "Containers.hpp" #include "Containers.hpp"
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 #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_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_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)