diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index 7beaf43b8..2acfc6299 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -20,6 +20,15 @@ #include "../../include/ZeroTierCore.h" +#if __has_include("../../version.h") +#include "../../version.h" +#else +#define ZEROTIER_ONE_VERSION_MAJOR 255 +#define ZEROTIER_ONE_VERSION_MINOR 255 +#define ZEROTIER_ONE_VERSION_REVISION 255 +#define ZEROTIER_ONE_VERSION_BUILD 255 +#endif + /****************************************************************************/ /* A pointer to an instance of EthernetTap */ diff --git a/go/pkg/zerotier/address.go b/go/pkg/zerotier/address.go index 1fada8007..bd0f1bf78 100644 --- a/go/pkg/zerotier/address.go +++ b/go/pkg/zerotier/address.go @@ -38,7 +38,7 @@ func (a Address) String() string { // MarshalJSON marshals this Address as a string func (a Address) MarshalJSON() ([]byte, error) { - return []byte(a.String()), nil + return []byte("\"" + a.String() + "\""), nil } // UnmarshalJSON unmarshals this Address from a string diff --git a/go/pkg/zerotier/base62.go b/go/pkg/zerotier/base62.go deleted file mode 100644 index 1ddbb5ee5..000000000 --- a/go/pkg/zerotier/base62.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -package zerotier - -import ( - "bytes" - "errors" -) - -// Base62Alphabet is the alphabet used for LF's Base62 encoding. -const Base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - -var base62Encoding, _ = newBaseXEncoding(Base62Alphabet) - -type baseXEncoding struct { - base int - alphabet []rune - alphabetMap map[rune]int -} - -func newBaseXEncoding(alphabet string) (*baseXEncoding, error) { - runes := []rune(alphabet) - runeMap := make(map[rune]int) - for i := 0; i < len(runes); i++ { - if _, ok := runeMap[runes[i]]; ok { - return nil, errors.New("bad alphabet") - } - runeMap[runes[i]] = i - } - return &baseXEncoding{ - base: len(runes), - alphabet: runes, - alphabetMap: runeMap, - }, nil -} - -func (e *baseXEncoding) encode(source []byte) string { - if len(source) == 0 { - return "" - } - digits := []int{0} - for i := 0; i < len(source); i++ { - carry := int(source[i]) - for j := 0; j < len(digits); j++ { - carry += digits[j] << 8 - digits[j] = carry % e.base - carry = carry / e.base - } - for carry > 0 { - digits = append(digits, carry%e.base) - carry = carry / e.base - } - } - var res bytes.Buffer - for k := 0; source[k] == 0 && k < len(source)-1; k++ { - res.WriteRune(e.alphabet[0]) - } - for q := len(digits) - 1; q >= 0; q-- { - res.WriteRune(e.alphabet[digits[q]]) - } - return res.String() -} - -func (e *baseXEncoding) decode(source string) []byte { - if len(source) == 0 { - return nil - } - runes := []rune(source) - bytes := []byte{0} - for i := 0; i < len(source); i++ { - value, ok := e.alphabetMap[runes[i]] - if ok { // ignore non-base characters - carry := int(value) - for j := 0; j < len(bytes); j++ { - carry += int(bytes[j]) * e.base - bytes[j] = byte(carry & 0xff) - carry >>= 8 - } - for carry > 0 { - bytes = append(bytes, byte(carry&0xff)) - carry >>= 8 - } - } - } - for k := 0; runes[k] == e.alphabet[0] && k < len(runes)-1; k++ { - bytes = append(bytes, 0) - } - for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 { - bytes[i], bytes[j] = bytes[j], bytes[i] - } - return bytes -} - -// Base62Encode encodes a byte array in base62 form -func Base62Encode(in []byte) string { return base62Encoding.encode(in) } - -// Base62Decode decodes a base62 string into a byte array, ignoring non-base62 characters -func Base62Decode(in string) []byte { return base62Encoding.decode(in) } diff --git a/go/pkg/zerotier/blob.go b/go/pkg/zerotier/blob.go deleted file mode 100644 index e12fb43a4..000000000 --- a/go/pkg/zerotier/blob.go +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -package zerotier - -// This is copied from the LF code base to make JSON blob encoding uniform - -import ( - "encoding/json" - "unicode/utf8" -) - -// Blob is a byte array that serializes to a string or a base62 string prefixed by \b (binary) -type Blob []byte - -// MarshalJSON returns this blob marshaled as a string using \b for non-UTF8 binary data. -func (b Blob) MarshalJSON() ([]byte, error) { - if utf8.Valid(b) { - return json.Marshal(string(b)) - } - return []byte("\"\\b" + Base62Encode(b) + "\""), nil -} - -// UnmarshalJSON unmarshals this blob from a string or byte array. -func (b *Blob) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err == nil { - if len(s) == 0 { - *b = nil - } else if s[0] == '\b' { - *b = Base62Decode(s[1:]) - return nil - } - *b = []byte(s) - return nil - } - - // Byte arrays are also accepted - var bb []byte - if json.Unmarshal(j, &bb) != nil { - return err - } - *b = bb - return nil -} diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index f31c4b51a..f6e729a2f 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -14,8 +14,8 @@ package zerotier import ( - "encoding/base32" "encoding/hex" + "encoding/json" "fmt" "strings" ) @@ -36,15 +36,9 @@ const ( // Identity is precisely what it sounds like: the address and associated keys for a ZeroTier node type Identity struct { - // Address is this identity's 40-bit short address - Address Address - - // Type is either IdentityTypeC25519 or IdentityTypeP384 - Type int - - // PublicKey is this identity's public key bytes - PublicKey Blob - + address Address + idtype int + publicKey []byte privateKey []byte } @@ -58,23 +52,23 @@ func NewIdentityFromString(s string) (*Identity, error) { var err error var id Identity - id.Address, err = NewAddressFromString(ss[0]) + id.address, err = NewAddressFromString(ss[0]) if err != nil { return nil, err } if ss[1] == "0" { - id.Type = 0 + id.idtype = 0 } else if ss[1] == "1" { - id.Type = 1 + id.idtype = 1 } else { return nil, ErrUncrecognizedIdentityType } - switch id.Type { + switch id.idtype { case 0: - id.PublicKey, err = hex.DecodeString(ss[2]) + id.publicKey, err = hex.DecodeString(ss[2]) if err != nil { return nil, err } @@ -86,15 +80,15 @@ func NewIdentityFromString(s string) (*Identity, error) { } case 1: - id.PublicKey, err = base32.StdEncoding.DecodeString(ss[2]) + id.publicKey, err = base32StdLowerCase.DecodeString(ss[2]) if err != nil { return nil, err } - if len(id.PublicKey) != IdentityTypeP384PublicKeySize { + if len(id.publicKey) != IdentityTypeP384PublicKeySize { return nil, ErrInvalidKey } if len(ss) >= 4 { - id.privateKey, err = base32.StdEncoding.DecodeString(ss[3]) + id.privateKey, err = base32StdLowerCase.DecodeString(ss[3]) if err != nil { return nil, err } @@ -113,9 +107,15 @@ func (id *Identity) HasPrivate() bool { return len(id.privateKey) > 0 } // PrivateKeyString returns the full identity.secret if the private key is set, or an empty string if no private key is set. func (id *Identity) PrivateKeyString() string { - if len(id.privateKey) == 64 { - s := fmt.Sprintf("%.10x:0:%x:%x", id.Address, id.PublicKey, id.privateKey) - return s + switch id.idtype { + case IdentityTypeC25519: + if len(id.publicKey) == IdentityTypeC25519PublicKeySize && len(id.privateKey) == IdentityTypeC25519PrivateKeySize { + return fmt.Sprintf("%.10x:0:%x:%x", uint64(id.address), id.publicKey, id.privateKey) + } + case IdentityTypeP384: + if len(id.publicKey) == IdentityTypeP384PublicKeySize && len(id.privateKey) == IdentityTypeP384PrivateKeySize { + return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), base32StdLowerCase.EncodeToString(id.publicKey), base32StdLowerCase.EncodeToString(id.privateKey)) + } } return "" } @@ -123,9 +123,26 @@ func (id *Identity) PrivateKeyString() string { // PublicKeyString returns the address and public key (identity.public contents). // An empty string is returned if this identity is invalid or not initialized. func (id *Identity) String() string { - if len(id.PublicKey) == 64 { - s := fmt.Sprintf("%.10x:0:%x", id.Address, id.PublicKey) + if len(id.publicKey) == IdentityTypeC25519PublicKeySize { + s := fmt.Sprintf("%.10x:0:%x", id.address, id.publicKey) return s } return "" } + +// MarshalJSON marshals this Identity in its string format (private key is never included) +func (id *Identity) MarshalJSON() ([]byte, error) { + return []byte("\"" + id.String() + "\""), nil +} + +// UnmarshalJSON unmarshals this Identity from a string +func (id *Identity) UnmarshalJSON(j []byte) error { + var s string + err := json.Unmarshal(j, &s) + if err != nil { + return err + } + nid, err := NewIdentityFromString(s) + *id = *nid + return err +} diff --git a/go/pkg/zerotier/mac.go b/go/pkg/zerotier/mac.go index 0f6cd8bef..6359cf8b2 100644 --- a/go/pkg/zerotier/mac.go +++ b/go/pkg/zerotier/mac.go @@ -48,7 +48,7 @@ func (m MAC) String() string { // MarshalJSON marshals this MAC as a string func (m MAC) MarshalJSON() ([]byte, error) { - return []byte(m.String()), nil + return []byte("\"" + m.String() + "\""), nil } // UnmarshalJSON unmarshals this MAC from a string diff --git a/go/pkg/zerotier/misc.go b/go/pkg/zerotier/misc.go index 1883cde41..73538c7ee 100644 --- a/go/pkg/zerotier/misc.go +++ b/go/pkg/zerotier/misc.go @@ -13,7 +13,12 @@ package zerotier -import "time" +import ( + "encoding/base32" + "time" +) + +var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") // TimeMs returns the time in milliseconds since epoch. func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) } diff --git a/go/pkg/zerotier/nativetap.go b/go/pkg/zerotier/nativetap.go deleted file mode 100644 index e870902bb..000000000 --- a/go/pkg/zerotier/nativetap.go +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -package zerotier - -import ( - "fmt" - "net" - "sync" - "sync/atomic" - "unsafe" -) - -//#cgo CFLAGS: -O3 -//#define ZT_CGO 1 -//#include -//#include -//#include -//#include "../../native/GoGlue.h" -import "C" - -// nativeTap is a Tap implementation that wraps a native C++ interface to a system tun/tap device -type nativeTap struct { - tap unsafe.Pointer - networkStatus uint32 - enabled uint32 - multicastGroupHandlers []func(bool, *MulticastGroup) - multicastGroupHandlersLock sync.Mutex -} - -// Type returns a human-readable description of this tap implementation -func (t *nativeTap) Type() string { - return "native" -} - -// Error gets this tap device's error status -func (t *nativeTap) Error() (int, string) { - return 0, "" -} - -// SetEnabled sets this tap's enabled state -func (t *nativeTap) SetEnabled(enabled bool) { - if enabled && atomic.SwapUint32(&t.enabled, 1) == 0 { - C.ZT_GoTap_setEnabled(t.tap, 1) - } else if !enabled && atomic.SwapUint32(&t.enabled, 0) == 1 { - C.ZT_GoTap_setEnabled(t.tap, 0) - } -} - -// Enabled returns true if this tap is currently processing packets -func (t *nativeTap) Enabled() bool { - return atomic.LoadUint32(&t.enabled) != 0 -} - -// AddIP adds an IP address (with netmask) to this tap -func (t *nativeTap) AddIP(ip net.IPNet) error { - bits, _ := ip.Mask.Size() - if len(ip.IP) == 16 { - if bits > 128 || bits < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_addIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) - } else if len(ip.IP) == 4 { - if bits > 32 || bits < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_addIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) - } - return ErrInvalidParameter -} - -// RemoveIP removes this IP address (with netmask) from this tap -func (t *nativeTap) RemoveIP(ip net.IPNet) error { - bits, _ := ip.Mask.Size() - if len(ip.IP) == 16 { - if bits > 128 || bits < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_removeIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) - return nil - } - if len(ip.IP) == 4 { - if bits > 32 || bits < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_removeIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) - return nil - } - return ErrInvalidParameter -} - -// IPs returns IPs currently assigned to this tap (including externally or system-assigned IPs) -func (t *nativeTap) IPs() (ips []net.IPNet, err error) { - defer func() { - e := recover() - if e != nil { - err = fmt.Errorf("%v", e) - } - }() - var ipbuf [16384]byte - count := int(C.ZT_GoTap_ips(t.tap, unsafe.Pointer(&ipbuf[0]), 16384)) - ipptr := 0 - for i := 0; i < count; i++ { - af := int(ipbuf[ipptr]) - ipptr++ - switch af { - case afInet: - var ip [4]byte - for j := 0; j < 4; j++ { - ip[j] = ipbuf[ipptr] - ipptr++ - } - bits := ipbuf[ipptr] - ipptr++ - ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 32)}) - case afInet6: - var ip [16]byte - for j := 0; j < 16; j++ { - ip[j] = ipbuf[ipptr] - ipptr++ - } - bits := ipbuf[ipptr] - ipptr++ - ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 128)}) - } - } - return -} - -// DeviceName gets this tap's OS-specific device name -func (t *nativeTap) DeviceName() string { - var dn [256]byte - C.ZT_GoTap_deviceName(t.tap, (*C.char)(unsafe.Pointer(&dn[0]))) - for i, b := range dn { - if b == 0 { - return string(dn[0:i]) - } - } - return "" -} - -// AddMulticastGroupChangeHandler adds a function to be called when the tap subscribes or unsubscribes to a multicast group. -func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *MulticastGroup)) { - t.multicastGroupHandlersLock.Lock() - t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler) - t.multicastGroupHandlersLock.Unlock() -} diff --git a/go/pkg/zerotier/node-callbacks.go b/go/pkg/zerotier/node-callbacks.go deleted file mode 100644 index ad6e49081..000000000 --- a/go/pkg/zerotier/node-callbacks.go +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -package zerotier - -// These are exported callbacks that are called from the C++ code in GoGlue.cpp - -//#cgo CFLAGS: -O3 -//#define ZT_CGO 1 -//#include -//#include -//#include -//#include "../../native/GoGlue.h" -import "C" - -import ( - "net" - "sync" - "sync/atomic" - "unsafe" -) - -const ( - afInet int = C.AF_INET - afInet6 int = C.AF_INET6 - - networkStatusRequestingConfiguration = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION - networkStatusOK = C.ZT_NETWORK_STATUS_OK - networkStatusAccessDenied = C.ZT_NETWORK_STATUS_ACCESS_DENIED - networkStatusNotFound = C.ZT_NETWORK_STATUS_NOT_FOUND - networkStatusPortError = C.ZT_NETWORK_STATUS_PORT_ERROR - networkStatusClientTooOld = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD -) - -var ( - nodesByUserPtr map[uintptr]*Node - nodesByUserPtrLock sync.RWMutex -) - -//export goPathCheckFunc -func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsafe.Pointer, port C.int) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node != nil && node.pathCheck(uint64(ztAddress), int(af), nil, int(port)) { - return 1 - } - return 0 -} - -//export goPathLookupFunc -func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFamily int, familyP, ipP, portP unsafe.Pointer) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return 0 - } - - ip, port := node.pathLookup(uint64(ztAddress)) - if len(ip) > 0 && port > 0 && port <= 65535 { - ip4 := ip.To4() - if len(ip4) == 4 { - *((*C.int)(familyP)) = C.int(afInet) - copy((*[4]byte)(ipP)[:], ip4) - *((*C.int)(portP)) = C.int(port) - return 1 - } else if len(ip) == 16 { - *((*C.int)(familyP)) = C.int(afInet6) - copy((*[16]byte)(ipP)[:], ip) - *((*C.int)(portP)) = C.int(port) - return 1 - } - } - return 0 -} - -//export goStateObjectPutFunc -func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - if len < 0 { - node.stateObjectDelete(int(objType), *((*[2]uint64)(id))) - } else { - node.stateObjectPut(int(objType), *((*[2]uint64)(id)), C.GoBytes(data, len)) - } -} - -//export goStateObjectGetFunc -func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, bufSize C.uint) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return -1 - } - tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) - if found && len(tmp) < int(bufSize) { - if len(tmp) > 0 { - C.memcpy(data, unsafe.Pointer(&(tmp[0])), C.ulong(len(tmp))) - } - return C.int(len(tmp)) - } - return -1 -} - -//export goDNSResolverFunc -func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - - recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes) - recordName := C.GoString((*C.char)(name)) - - go func() { - recordNameCStrCopy := C.CString(recordName) - for _, rt := range recordTypes { - switch rt { - case C.ZT_DNS_RECORD_TXT: - recs, _ := net.LookupTXT(recordName) - for _, rec := range recs { - if len(rec) > 0 { - rnCS := C.CString(rec) - C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0) - C.free(unsafe.Pointer(rnCS)) - } - } - } - } - C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0) - C.free(unsafe.Pointer(recordNameCStrCopy)) - }() -} - -//export goVirtualNetworkConfigFunc -func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) C.int { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return 255 - } - return C.int(node.handleNetworkConfigUpdate(uint64(nwid), int(op), (*C.ZT_VirtualNetworkConfig)(conf))) -} - -//export goZtEvent -func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - switch eventType { - case C.ZT_EVENT_OFFLINE: - atomic.StoreUint32(&node.online, 0) - case C.ZT_EVENT_ONLINE: - atomic.StoreUint32(&node.online, 1) - case C.ZT_EVENT_TRACE: - node.handleTrace(C.GoString((*C.char)(data))) - case C.ZT_EVENT_USER_MESSAGE: - um := (*C.ZT_UserMessage)(data) - node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) - case C.ZT_EVENT_REMOTE_TRACE: - rt := (*C.ZT_RemoteTrace)(data) - node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) - } -} - -// These are really part of nativeTap - -func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - - node.networksLock.RLock() - network := node.networks[uint64(nwid)] - node.networksLock.RUnlock() - - network.tapLock.Lock() - tap, _ := network.tap.(*nativeTap) - network.tapLock.Unlock() - - 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) - } - } -} - -//export goHandleTapAddedMulticastGroup -func goHandleTapAddedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { - handleTapMulticastGroupChange(gn, nwid, mac, adi, true) -} - -//export goHandleTapRemovedMulticastGroup -func goHandleTapRemovedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { - handleTapMulticastGroupChange(gn, nwid, mac, adi, false) -} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index ae8f6ee06..449df4a3b 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -13,6 +13,12 @@ package zerotier +//#cgo CFLAGS: -O3 +//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/osdep/libzt_osdep.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++ -lpthread +//#define ZT_CGO 1 +//#include "../../native/GoGlue.h" +import "C" + import ( "errors" "fmt" @@ -28,15 +34,6 @@ import ( acl "github.com/hectane/go-acl" ) -//#cgo CFLAGS: -O3 -//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/osdep/libzt_osdep.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++ -lpthread -//#define ZT_CGO 1 -//#include -//#include -//#include -//#include "../../native/GoGlue.h" -import "C" - // Network status states const ( NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION @@ -45,6 +42,26 @@ const ( NetworkStatusNotFound int = C.ZT_NETWORK_STATUS_NOT_FOUND NetworkStatusPortError int = C.ZT_NETWORK_STATUS_PORT_ERROR NetworkStatusClientTooOld int = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD + + // CoreVersionMajor is the major version of the ZeroTier core + CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR + + // CoreVersionMinor is the minor version of the ZeroTier core + CoreVersionMinor int = C.ZEROTIER_ONE_VERSION_MINOR + + // CoreVersionRevision is the revision of the ZeroTier core + CoreVersionRevision int = C.ZEROTIER_ONE_VERSION_REVISION + + // CoreVersionBuild is the build version of the ZeroTier core + CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD + + afInet int = C.AF_INET + afInet6 int = C.AF_INET6 +) + +var ( + nodesByUserPtr map[uintptr]*Node + nodesByUserPtrLock sync.RWMutex ) ////////////////////////////////////////////////////////////////////////////// @@ -225,6 +242,314 @@ func (n *Node) handleUserMessage(originAddress, messageTypeID uint64, data []byt func (n *Node) handleRemoteTrace(originAddress uint64, dictData []byte) { } -func (n *Node) handleNetworkConfigUpdate(nwid uint64, op int, config *C.ZT_VirtualNetworkConfig) int { +////////////////////////////////////////////////////////////////////////////// + +//export goPathCheckFunc +func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsafe.Pointer, port C.int) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node != nil && node.pathCheck(uint64(ztAddress), int(af), nil, int(port)) { + return 1 + } return 0 } + +//export goPathLookupFunc +func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFamily int, familyP, ipP, portP unsafe.Pointer) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return 0 + } + + ip, port := node.pathLookup(uint64(ztAddress)) + if len(ip) > 0 && port > 0 && port <= 65535 { + ip4 := ip.To4() + if len(ip4) == 4 { + *((*C.int)(familyP)) = C.int(afInet) + copy((*[4]byte)(ipP)[:], ip4) + *((*C.int)(portP)) = C.int(port) + return 1 + } else if len(ip) == 16 { + *((*C.int)(familyP)) = C.int(afInet6) + copy((*[16]byte)(ipP)[:], ip) + *((*C.int)(portP)) = C.int(port) + return 1 + } + } + return 0 +} + +//export goStateObjectPutFunc +func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + if len < 0 { + node.stateObjectDelete(int(objType), *((*[2]uint64)(id))) + } else { + node.stateObjectPut(int(objType), *((*[2]uint64)(id)), C.GoBytes(data, len)) + } +} + +//export goStateObjectGetFunc +func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, bufSize C.uint) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return -1 + } + tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) + if found && len(tmp) < int(bufSize) { + if len(tmp) > 0 { + C.memcpy(data, unsafe.Pointer(&(tmp[0])), C.ulong(len(tmp))) + } + return C.int(len(tmp)) + } + return -1 +} + +//export goDNSResolverFunc +func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + + recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes) + recordName := C.GoString((*C.char)(name)) + + go func() { + recordNameCStrCopy := C.CString(recordName) + for _, rt := range recordTypes { + switch rt { + case C.ZT_DNS_RECORD_TXT: + recs, _ := net.LookupTXT(recordName) + for _, rec := range recs { + if len(rec) > 0 { + rnCS := C.CString(rec) + C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0) + C.free(unsafe.Pointer(rnCS)) + } + } + } + } + C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0) + C.free(unsafe.Pointer(recordNameCStrCopy)) + }() +} + +//export goVirtualNetworkConfigFunc +func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return 255 + } + node.networksLock.RLock() + network := node.networks[uint64(nwid)] + node.networksLock.RUnlock() + if network != nil { + } + //return C.int(node.handleNetworkConfigUpdate(uint64(nwid), int(op), (*C.ZT_VirtualNetworkConfig)(conf))) +} + +//export goZtEvent +func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + switch eventType { + case C.ZT_EVENT_OFFLINE: + atomic.StoreUint32(&node.online, 0) + case C.ZT_EVENT_ONLINE: + atomic.StoreUint32(&node.online, 1) + case C.ZT_EVENT_TRACE: + node.handleTrace(C.GoString((*C.char)(data))) + case C.ZT_EVENT_USER_MESSAGE: + um := (*C.ZT_UserMessage)(data) + node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) + case C.ZT_EVENT_REMOTE_TRACE: + rt := (*C.ZT_RemoteTrace)(data) + node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) + } +} + +// These are really part of nativeTap + +func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + + node.networksLock.RLock() + network := node.networks[uint64(nwid)] + node.networksLock.RUnlock() + + network.tapLock.Lock() + tap, _ := network.tap.(*nativeTap) + network.tapLock.Unlock() + + 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) + } + } +} + +//export goHandleTapAddedMulticastGroup +func goHandleTapAddedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { + handleTapMulticastGroupChange(gn, nwid, mac, adi, true) +} + +//export goHandleTapRemovedMulticastGroup +func goHandleTapRemovedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { + handleTapMulticastGroupChange(gn, nwid, mac, adi, false) +} + +////////////////////////////////////////////////////////////////////////////// + +// nativeTap is a Tap implementation that wraps a native C++ interface to a system tun/tap device +type nativeTap struct { + tap unsafe.Pointer + networkStatus uint32 + enabled uint32 + multicastGroupHandlers []func(bool, *MulticastGroup) + multicastGroupHandlersLock sync.Mutex +} + +// Type returns a human-readable description of this tap implementation +func (t *nativeTap) Type() string { + return "native" +} + +// Error gets this tap device's error status +func (t *nativeTap) Error() (int, string) { + return 0, "" +} + +// SetEnabled sets this tap's enabled state +func (t *nativeTap) SetEnabled(enabled bool) { + if enabled && atomic.SwapUint32(&t.enabled, 1) == 0 { + C.ZT_GoTap_setEnabled(t.tap, 1) + } else if !enabled && atomic.SwapUint32(&t.enabled, 0) == 1 { + C.ZT_GoTap_setEnabled(t.tap, 0) + } +} + +// Enabled returns true if this tap is currently processing packets +func (t *nativeTap) Enabled() bool { + return atomic.LoadUint32(&t.enabled) != 0 +} + +// AddIP adds an IP address (with netmask) to this tap +func (t *nativeTap) AddIP(ip net.IPNet) error { + bits, _ := ip.Mask.Size() + if len(ip.IP) == 16 { + if bits > 128 || bits < 0 { + return ErrInvalidParameter + } + C.ZT_GoTap_addIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) + } else if len(ip.IP) == 4 { + if bits > 32 || bits < 0 { + return ErrInvalidParameter + } + C.ZT_GoTap_addIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) + } + return ErrInvalidParameter +} + +// RemoveIP removes this IP address (with netmask) from this tap +func (t *nativeTap) RemoveIP(ip net.IPNet) error { + bits, _ := ip.Mask.Size() + if len(ip.IP) == 16 { + if bits > 128 || bits < 0 { + return ErrInvalidParameter + } + C.ZT_GoTap_removeIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) + return nil + } + if len(ip.IP) == 4 { + if bits > 32 || bits < 0 { + return ErrInvalidParameter + } + C.ZT_GoTap_removeIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) + return nil + } + return ErrInvalidParameter +} + +// IPs returns IPs currently assigned to this tap (including externally or system-assigned IPs) +func (t *nativeTap) IPs() (ips []net.IPNet, err error) { + defer func() { + e := recover() + if e != nil { + err = fmt.Errorf("%v", e) + } + }() + var ipbuf [16384]byte + count := int(C.ZT_GoTap_ips(t.tap, unsafe.Pointer(&ipbuf[0]), 16384)) + ipptr := 0 + for i := 0; i < count; i++ { + af := int(ipbuf[ipptr]) + ipptr++ + switch af { + case afInet: + var ip [4]byte + for j := 0; j < 4; j++ { + ip[j] = ipbuf[ipptr] + ipptr++ + } + bits := ipbuf[ipptr] + ipptr++ + ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 32)}) + case afInet6: + var ip [16]byte + for j := 0; j < 16; j++ { + ip[j] = ipbuf[ipptr] + ipptr++ + } + bits := ipbuf[ipptr] + ipptr++ + ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 128)}) + } + } + return +} + +// DeviceName gets this tap's OS-specific device name +func (t *nativeTap) DeviceName() string { + var dn [256]byte + C.ZT_GoTap_deviceName(t.tap, (*C.char)(unsafe.Pointer(&dn[0]))) + for i, b := range dn { + if b == 0 { + return string(dn[0:i]) + } + } + return "" +} + +// AddMulticastGroupChangeHandler adds a function to be called when the tap subscribes or unsubscribes to a multicast group. +func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *MulticastGroup)) { + t.multicastGroupHandlersLock.Lock() + t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler) + t.multicastGroupHandlersLock.Unlock() +} diff --git a/go/pkg/zerotier/version.go b/go/pkg/zerotier/version.go deleted file mode 100644 index 52f0932e7..000000000 --- a/go/pkg/zerotier/version.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -package zerotier - -//#if __has_include("../../../version.h") -//#include "../../../version.h" -//#else -//#define ZEROTIER_ONE_VERSION_MAJOR 255 -//#define ZEROTIER_ONE_VERSION_MINOR 255 -//#define ZEROTIER_ONE_VERSION_REVISION 255 -//#define ZEROTIER_ONE_VERSION_BUILD 255 -//#endif -import "C" - -const ( - // CoreVersionMajor is the major version of the ZeroTier core - CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR - - // CoreVersionMinor is the minor version of the ZeroTier core - CoreVersionMinor int = C.ZEROTIER_ONE_VERSION_MINOR - - // CoreVersionRevision is the revision of the ZeroTier core - CoreVersionRevision int = C.ZEROTIER_ONE_VERSION_REVISION - - // CoreVersionBuild is the build version of the ZeroTier core - CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD -) diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 37c7ebd23..2c51f4f0e 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -1156,19 +1156,6 @@ typedef struct * Routes (excluding those implied by assigned addresses and their masks) */ ZT_VirtualNetworkRoute routes[ZT_MAX_NETWORK_ROUTES]; - - /** - * Number of multicast groups subscribed - */ - unsigned int multicastSubscriptionCount; - - /** - * Multicast groups to which this network's device is subscribed - */ - struct { - uint64_t mac; /* MAC in lower 48 bits */ - uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP groups */ - } multicastSubscriptions[ZT_MAX_MULTICAST_SUBSCRIPTIONS]; } ZT_VirtualNetworkConfig; /** diff --git a/node/Network.cpp b/node/Network.cpp index 9f685411b..ed30f963c 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1366,12 +1366,6 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute)); } } - - ec->multicastSubscriptionCount = (unsigned int)_myMulticastGroups.size(); - for(unsigned long i=0;i<(unsigned long)_myMulticastGroups.size();++i) { - ec->multicastSubscriptions[i].mac = _myMulticastGroups[i].mac().toInt(); - ec->multicastSubscriptions[i].adi = _myMulticastGroups[i].adi(); - } } void Network::_announceMulticastGroups(void *tPtr,bool force) diff --git a/node/Utils.cpp b/node/Utils.cpp index b1584c6a9..2de30002f 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -306,7 +306,7 @@ int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize) } int index = 0x1F & (buffer >> (bitsLeft - 5)); bitsLeft -= 5; - result[count++] = "abcdefghijklmnopqrstuvwxyZ234567"[index]; + result[count++] = "abcdefghijklmnopqrstuvwxyz234567"[index]; } } if (count < bufSize) {