Browse Source

Going once... going twice!

Adam Ierymenko 5 years ago
parent
commit
bb53ee9567

+ 9 - 0
go/native/GoGlue.h

@@ -20,6 +20,15 @@
 
 
 #include "../../include/ZeroTierCore.h"
 #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 */
 /* A pointer to an instance of EthernetTap */

+ 1 - 1
go/pkg/zerotier/address.go

@@ -38,7 +38,7 @@ func (a Address) String() string {
 
 
 // MarshalJSON marshals this Address as a string
 // MarshalJSON marshals this Address as a string
 func (a Address) MarshalJSON() ([]byte, error) {
 func (a Address) MarshalJSON() ([]byte, error) {
-	return []byte(a.String()), nil
+	return []byte("\"" + a.String() + "\""), nil
 }
 }
 
 
 // UnmarshalJSON unmarshals this Address from a string
 // UnmarshalJSON unmarshals this Address from a string

+ 0 - 109
go/pkg/zerotier/base62.go

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

+ 0 - 56
go/pkg/zerotier/blob.go

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

+ 40 - 23
go/pkg/zerotier/identity.go

@@ -14,8 +14,8 @@
 package zerotier
 package zerotier
 
 
 import (
 import (
-	"encoding/base32"
 	"encoding/hex"
 	"encoding/hex"
+	"encoding/json"
 	"fmt"
 	"fmt"
 	"strings"
 	"strings"
 )
 )
@@ -36,15 +36,9 @@ const (
 
 
 // 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
 type Identity struct {
 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
 	privateKey []byte
 }
 }
 
 
@@ -58,23 +52,23 @@ func NewIdentityFromString(s string) (*Identity, error) {
 
 
 	var err error
 	var err error
 	var id Identity
 	var id Identity
-	id.Address, err = NewAddressFromString(ss[0])
+	id.address, err = NewAddressFromString(ss[0])
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	if ss[1] == "0" {
 	if ss[1] == "0" {
-		id.Type = 0
+		id.idtype = 0
 	} else if ss[1] == "1" {
 	} else if ss[1] == "1" {
-		id.Type = 1
+		id.idtype = 1
 	} else {
 	} else {
 		return nil, ErrUncrecognizedIdentityType
 		return nil, ErrUncrecognizedIdentityType
 	}
 	}
 
 
-	switch id.Type {
+	switch id.idtype {
 
 
 	case 0:
 	case 0:
-		id.PublicKey, err = hex.DecodeString(ss[2])
+		id.publicKey, err = hex.DecodeString(ss[2])
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -86,15 +80,15 @@ func NewIdentityFromString(s string) (*Identity, error) {
 		}
 		}
 
 
 	case 1:
 	case 1:
-		id.PublicKey, err = base32.StdEncoding.DecodeString(ss[2])
+		id.publicKey, err = base32StdLowerCase.DecodeString(ss[2])
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if len(id.PublicKey) != IdentityTypeP384PublicKeySize {
+		if len(id.publicKey) != IdentityTypeP384PublicKeySize {
 			return nil, ErrInvalidKey
 			return nil, ErrInvalidKey
 		}
 		}
 		if len(ss) >= 4 {
 		if len(ss) >= 4 {
-			id.privateKey, err = base32.StdEncoding.DecodeString(ss[3])
+			id.privateKey, err = base32StdLowerCase.DecodeString(ss[3])
 			if err != nil {
 			if err != nil {
 				return nil, err
 				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.
 // 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 {
 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 ""
 	return ""
 }
 }
@@ -123,9 +123,26 @@ func (id *Identity) PrivateKeyString() string {
 // PublicKeyString returns the address and public key (identity.public contents).
 // PublicKeyString returns the address and public key (identity.public contents).
 // An empty string is returned if this identity is invalid or not initialized.
 // An empty string is returned if this identity is invalid or not initialized.
 func (id *Identity) String() string {
 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 s
 	}
 	}
 	return ""
 	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
+}

+ 1 - 1
go/pkg/zerotier/mac.go

@@ -48,7 +48,7 @@ func (m MAC) String() string {
 
 
 // MarshalJSON marshals this MAC as a string
 // MarshalJSON marshals this MAC as a string
 func (m MAC) MarshalJSON() ([]byte, error) {
 func (m MAC) MarshalJSON() ([]byte, error) {
-	return []byte(m.String()), nil
+	return []byte("\"" + m.String() + "\""), nil
 }
 }
 
 
 // UnmarshalJSON unmarshals this MAC from a string
 // UnmarshalJSON unmarshals this MAC from a string

+ 6 - 1
go/pkg/zerotier/misc.go

@@ -13,7 +13,12 @@
 
 
 package zerotier
 package zerotier
 
 
-import "time"
+import (
+	"encoding/base32"
+	"time"
+)
+
+var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
 
 
 // TimeMs returns the time in milliseconds since epoch.
 // TimeMs returns the time in milliseconds since epoch.
 func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }
 func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }

+ 0 - 157
go/pkg/zerotier/nativetap.go

@@ -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 <stdint.h>
-//#include <stdlib.h>
-//#include <string.h>
-//#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()
-}

+ 0 - 224
go/pkg/zerotier/node-callbacks.go

@@ -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 <stdint.h>
-//#include <stdlib.h>
-//#include <string.h>
-//#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)
-}

+ 335 - 10
go/pkg/zerotier/node.go

@@ -13,6 +13,12 @@
 
 
 package zerotier
 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 (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -28,15 +34,6 @@ import (
 	acl "github.com/hectane/go-acl"
 	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 <stdint.h>
-//#include <stdlib.h>
-//#include <string.h>
-//#include "../../native/GoGlue.h"
-import "C"
-
 // Network status states
 // Network status states
 const (
 const (
 	NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
 	NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
@@ -45,6 +42,26 @@ const (
 	NetworkStatusNotFound             int = C.ZT_NETWORK_STATUS_NOT_FOUND
 	NetworkStatusNotFound             int = C.ZT_NETWORK_STATUS_NOT_FOUND
 	NetworkStatusPortError            int = C.ZT_NETWORK_STATUS_PORT_ERROR
 	NetworkStatusPortError            int = C.ZT_NETWORK_STATUS_PORT_ERROR
 	NetworkStatusClientTooOld         int = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD
 	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) 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
 	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()
+}

+ 0 - 38
go/pkg/zerotier/version.go

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

+ 0 - 13
include/ZeroTierCore.h

@@ -1156,19 +1156,6 @@ typedef struct
 	 * Routes (excluding those implied by assigned addresses and their masks)
 	 * Routes (excluding those implied by assigned addresses and their masks)
 	 */
 	 */
 	ZT_VirtualNetworkRoute routes[ZT_MAX_NETWORK_ROUTES];
 	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;
 } ZT_VirtualNetworkConfig;
 
 
 /**
 /**

+ 0 - 6
node/Network.cpp

@@ -1366,12 +1366,6 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
 			memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute));
 			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)
 void Network::_announceMulticastGroups(void *tPtr,bool force)

+ 1 - 1
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));
       int index = 0x1F & (buffer >> (bitsLeft - 5));
       bitsLeft -= 5;
       bitsLeft -= 5;
-      result[count++] = "abcdefghijklmnopqrstuvwxyZ234567"[index];
+      result[count++] = "abcdefghijklmnopqrstuvwxyz234567"[index];
     }
     }
   }
   }
   if (count < bufSize) {
   if (count < bufSize) {