mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-11 13:35:17 +00:00
193 lines
5.4 KiB
Go
193 lines
5.4 KiB
Go
/*
|
|
* 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
|
|
|
|
//#cgo CFLAGS: -O3
|
|
//#include "../../native/GoGlue.h"
|
|
import "C"
|
|
|
|
import (
|
|
"encoding/json"
|
|
"unsafe"
|
|
)
|
|
|
|
// LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS
|
|
type LocatorDNSSigningKey struct {
|
|
SecureDNSName string
|
|
PrivateKey []byte
|
|
}
|
|
|
|
// NewLocatorDNSSigningKey creates a new signing key and secure DNS name for storing locators in DNS
|
|
func NewLocatorDNSSigningKey() (*LocatorDNSSigningKey, error) {
|
|
var nameBuf [256]C.char
|
|
var keyBuf [64]byte
|
|
keySize := int(C.ZT_GoLocator_makeSecureDNSName(&nameBuf[0], 256, (*C.uint8_t)(unsafe.Pointer(&keyBuf[0])), 128))
|
|
if keySize <= 0 {
|
|
return nil, ErrInternal
|
|
}
|
|
var sk LocatorDNSSigningKey
|
|
sk.SecureDNSName = C.GoString(&nameBuf[0])
|
|
sk.PrivateKey = keyBuf[0:keySize]
|
|
return &sk, nil
|
|
}
|
|
|
|
// Locator is a binary serialized record containing information about where a ZeroTier node is located on the network.
|
|
// Note that for JSON objects only Bytes needs to be specified. When JSON is deserialized only this field is used
|
|
// and the others are always reconstructed from it.
|
|
type Locator struct {
|
|
// Identity is the full identity of the node being located
|
|
Identity *Identity
|
|
|
|
// Physical is a list of static physical network addresses for this node
|
|
Physical []*InetAddress
|
|
|
|
// Virtual is a list of ZeroTier nodes that can relay to this node
|
|
Virtual []*Identity
|
|
|
|
// Bytes is the raw serialized Locator
|
|
Bytes []byte
|
|
}
|
|
|
|
// NewLocator creates a new locator with the given identity and addresses and the current time as timestamp.
|
|
// The identity must include its secret key so that it can sign the final locator.
|
|
func NewLocator(id *Identity, virtualAddresses []*Identity, physicalAddresses []*InetAddress) (*Locator, error) {
|
|
if !id.HasPrivate() {
|
|
return nil, ErrSecretKeyRequired
|
|
}
|
|
|
|
idstr := id.PrivateKeyString()
|
|
phy := make([]C.struct_sockaddr_storage, len(physicalAddresses))
|
|
virt := make([]*C.char, len(virtualAddresses))
|
|
idCstr := C.CString(idstr)
|
|
|
|
defer func() {
|
|
C.free(unsafe.Pointer(idCstr))
|
|
for _, v := range virt {
|
|
if uintptr(unsafe.Pointer(v)) != 0 {
|
|
C.free(unsafe.Pointer(v))
|
|
}
|
|
}
|
|
}()
|
|
|
|
for i := 0; i < len(physicalAddresses); i++ {
|
|
if !makeSockaddrStorage(physicalAddresses[i].IP, physicalAddresses[i].Port, &phy[i]) {
|
|
return nil, ErrInvalidParameter
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(virtualAddresses); i++ {
|
|
idstr := virtualAddresses[i].String()
|
|
virt[i] = C.CString(idstr)
|
|
}
|
|
|
|
var buf [65536]byte
|
|
var pPhy *C.struct_sockaddr_storage
|
|
var pVirt **C.char
|
|
if len(phy) > 0 {
|
|
pPhy = &phy[0]
|
|
}
|
|
if len(virt) > 0 {
|
|
pVirt = &virt[0]
|
|
}
|
|
locSize := C.ZT_GoLocator_makeLocator((*C.uint8_t)(unsafe.Pointer(&buf[0])), 65536, C.int64_t(TimeMs()), idCstr, pPhy, C.uint(len(phy)), pVirt, C.uint(len(virt)))
|
|
if locSize <= 0 {
|
|
return nil, ErrInvalidParameter
|
|
}
|
|
|
|
r := make([]byte, int(locSize))
|
|
copy(r[:], buf[0:int(locSize)])
|
|
return &Locator{
|
|
Identity: id,
|
|
Physical: physicalAddresses,
|
|
Virtual: virtualAddresses,
|
|
Bytes: r,
|
|
}, nil
|
|
}
|
|
|
|
// NewLocatorFromBytes decodes a locator from its serialized byte array form
|
|
func NewLocatorFromBytes(b []byte) (*Locator, error) {
|
|
if len(b) == 0 {
|
|
return nil, ErrInvalidParameter
|
|
}
|
|
var info C.struct_ZT_GoLocator_Info
|
|
res := C.ZT_GoLocator_decodeLocator((*C.uint8_t)(unsafe.Pointer(&b[0])), C.uint(len(b)), &info)
|
|
if res == -2 {
|
|
return nil, ErrInvalidSignature
|
|
} else if res <= 0 {
|
|
return nil, ErrInvalidParameter
|
|
}
|
|
|
|
var loc Locator
|
|
|
|
var err error
|
|
loc.Identity, err = NewIdentityFromString(C.GoString(&info.id[0]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < int(info.phyCount); i++ {
|
|
ua := sockaddrStorageToUDPAddr(&info.phy[i])
|
|
if ua != nil {
|
|
loc.Physical = append(loc.Physical, &InetAddress{IP: ua.IP, Port: ua.Port})
|
|
}
|
|
}
|
|
for i := 0; i < int(info.virtCount); i++ {
|
|
id, err := NewIdentityFromString(C.GoString(&info.virt[i][0]))
|
|
if err == nil {
|
|
loc.Virtual = append(loc.Virtual, id)
|
|
}
|
|
}
|
|
|
|
return &loc, nil
|
|
}
|
|
|
|
// MakeTXTRecords creates secure DNS TXT records for this locator
|
|
func (l *Locator) MakeTXTRecords(key *LocatorDNSSigningKey) ([]string, error) {
|
|
if key == nil || len(l.Bytes) == 0 || len(key.PrivateKey) == 0 {
|
|
return nil, ErrInvalidParameter
|
|
}
|
|
var results [256][256]C.char
|
|
cName := C.CString(key.SecureDNSName)
|
|
defer C.free(unsafe.Pointer(cName))
|
|
count := int(C.ZT_GoLocator_makeSignedTxtRecords((*C.uint8_t)(&l.Bytes[0]), C.uint(len(l.Bytes)), cName, (*C.uint8_t)(&key.PrivateKey[0]), C.uint(len(key.PrivateKey)), &results[0]))
|
|
if count > 0 {
|
|
var t []string
|
|
for i := 0; i < int(count); i++ {
|
|
t = append(t, C.GoString(&results[i][0]))
|
|
}
|
|
return t, nil
|
|
}
|
|
return nil, ErrInternal
|
|
}
|
|
|
|
// MarshalJSON marshals this Locator as its byte encoding
|
|
func (l *Locator) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(l)
|
|
}
|
|
|
|
// UnmarshalJSON unmarshals this Locator from a byte array in JSON.
|
|
func (l *Locator) UnmarshalJSON(j []byte) error {
|
|
err := json.Unmarshal(j, l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmp, err := NewLocatorFromBytes(l.Bytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Identity = tmp.Identity
|
|
l.Physical = tmp.Physical
|
|
l.Virtual = tmp.Virtual
|
|
return nil
|
|
}
|