ZeroTierOne/go/pkg/zerotier/locator.go
2019-09-30 16:12:08 -07:00

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
}