From 35c10373b5a33169a5e3dfd938bb8f5ddb4c6d82 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Mon, 28 Nov 2022 15:25:47 -0800 Subject: [PATCH] types/logid: move logtail ID types here (#6336) Many packages reference the logtail ID types, but unfortunately pull in the transitive dependencies of logtail. Fix this problem by putting the log ID types in its own package with minimal dependencies. Signed-off-by: Joe Tsai --- cmd/tailscaled/depaware.txt | 1 + logtail/id.go | 169 ++----------------------- types/logid/id.go | 183 ++++++++++++++++++++++++++++ {logtail => types/logid}/id_test.go | 2 +- wgengine/wgcfg/wgcfg_clone.go | 6 +- 5 files changed, 200 insertions(+), 161 deletions(-) create mode 100644 types/logid/id.go rename {logtail => types/logid}/id_test.go (99%) diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 515ed200c..ee6af9952 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -267,6 +267,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/ipproto from tailscale.com/net/flowtrack+ tailscale.com/types/key from tailscale.com/control/controlbase+ tailscale.com/types/logger from tailscale.com/control/controlclient+ + tailscale.com/types/logid from tailscale.com/logtail+ tailscale.com/types/netlogtype from tailscale.com/net/tstun+ tailscale.com/types/netmap from tailscale.com/control/controlclient+ tailscale.com/types/nettype from tailscale.com/wgengine/magicsock+ diff --git a/logtail/id.go b/logtail/id.go index 8261905f2..3c1470612 100644 --- a/logtail/id.go +++ b/logtail/id.go @@ -4,170 +4,25 @@ package logtail -import ( - "crypto/rand" - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" -) +import "tailscale.com/types/logid" -// PrivateID represents an instance that write logs. -// Private IDs are only shared with the server when writing logs. -type PrivateID [32]byte +// Deprecated: Use "tailscale.com/types/logid".PrivateID instead. +type PrivateID = logid.PrivateID -// Safely generate a new PrivateId for use in Config objects. -// You should persist this across runs of an instance of your app, so that -// it can append to the same log file on each run. -func NewPrivateID() (id PrivateID, err error) { - _, err = rand.Read(id[:]) - if err != nil { - return PrivateID{}, err - } - // Clamping, for future use. - id[0] &= 248 - id[31] = (id[31] & 127) | 64 - return id, nil +// Deprecated: Use "tailscale.com/types/logid".NewPrivateID instead. +func NewPrivateID() (PrivateID, error) { + return logid.NewPrivateID() } -func (id PrivateID) MarshalText() ([]byte, error) { - b := make([]byte, hex.EncodedLen(len(id))) - if i := hex.Encode(b, id[:]); i != len(b) { - return nil, fmt.Errorf("logtail.PrivateID.MarshalText: i=%d", i) - } - return b, nil -} - -// ParsePrivateID returns a PrivateID from its hex (String) representation. +// Deprecated: Use "tailscale.com/types/logid".ParsePrivateID instead. func ParsePrivateID(s string) (PrivateID, error) { - if len(s) != 64 { - return PrivateID{}, errors.New("invalid length") - } - var p PrivateID - for i := range p { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return PrivateID{}, errors.New("invalid hex character") - } - p[i] = (a << 4) | b - } - return p, nil -} - -// IsZero reports whether id is the zero value. -func (id PrivateID) IsZero() bool { return id == PrivateID{} } - -func (id *PrivateID) UnmarshalText(s []byte) error { - b, err := hex.DecodeString(string(s)) - if err != nil { - return fmt.Errorf("logtail.PrivateID.UnmarshalText: %v", err) - } - if len(b) != len(id) { - return fmt.Errorf("logtail.PrivateID.UnmarshalText: invalid hex length: %d", len(b)) - } - copy(id[:], b) - return nil -} - -func (id PrivateID) String() string { - b, err := id.MarshalText() - if err != nil { - panic(err) - } - return string(b) -} - -func (id PrivateID) Public() (pub PublicID) { - h := sha256.New() - h.Write(id[:]) - if n := copy(pub[:], h.Sum(pub[:0])); n != len(pub) { - panic(fmt.Sprintf("public id short copy: %d", n)) - } - return pub + return logid.ParsePrivateID(s) } -// PublicID represents an instance in the logs service for reading and adoption. -// The public ID value is a SHA-256 hash of a private ID. -type PublicID [sha256.Size]byte +// Deprecated: Use "tailscale.com/types/logid".PublicID instead. +type PublicID = logid.PublicID -// ParsePublicID returns a PublicID from its hex (String) representation. +// Deprecated: Use "tailscale.com/types/logid".ParsePublicID instead. func ParsePublicID(s string) (PublicID, error) { - if len(s) != sha256.Size*2 { - return PublicID{}, errors.New("invalid length") - } - var p PublicID - for i := range p { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return PublicID{}, errors.New("invalid hex character") - } - p[i] = (a << 4) | b - } - return p, nil -} - -func (id PublicID) MarshalText() ([]byte, error) { - b := make([]byte, hex.EncodedLen(len(id))) - if i := hex.Encode(b, id[:]); i != len(b) { - return nil, fmt.Errorf("logtail.PublicID.MarshalText: i=%d", i) - } - return b, nil -} - -func (id *PublicID) UnmarshalText(s []byte) error { - if len(s) != len(id)*2 { - return fmt.Errorf("logtail.PublicID.UnmarshalText: invalid hex length: %d", len(s)) - } - for i := range id { - a, ok1 := fromHexChar(s[i*2+0]) - b, ok2 := fromHexChar(s[i*2+1]) - if !ok1 || !ok2 { - return errors.New("invalid hex character") - } - id[i] = (a << 4) | b - } - return nil -} - -func (id PublicID) String() string { - b, err := id.MarshalText() - if err != nil { - panic(err) - } - return string(b) -} - -// fromHexChar converts a hex character into its value and a success flag. -func fromHexChar(c byte) (byte, bool) { - switch { - case '0' <= c && c <= '9': - return c - '0', true - case 'a' <= c && c <= 'f': - return c - 'a' + 10, true - case 'A' <= c && c <= 'F': - return c - 'A' + 10, true - } - - return 0, false -} - -func (id1 PublicID) Less(id2 PublicID) bool { - for i, c1 := range id1[:] { - c2 := id2[i] - if c1 != c2 { - return c1 < c2 - } - } - return false // equal -} - -func (id PublicID) IsZero() bool { - return id == PublicID{} -} - -func (id PublicID) Prefix64() uint64 { - return binary.BigEndian.Uint64(id[:8]) + return logid.ParsePublicID(s) } diff --git a/types/logid/id.go b/types/logid/id.go new file mode 100644 index 000000000..a55784052 --- /dev/null +++ b/types/logid/id.go @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Tailscale Inc & 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 logid + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" +) + +// PrivateID represents an instance that write logs. +// Private IDs are only shared with the server when writing logs. +type PrivateID [32]byte + +// Safely generate a new PrivateId for use in Config objects. +// You should persist this across runs of an instance of your app, so that +// it can append to the same log file on each run. +func NewPrivateID() (id PrivateID, err error) { + _, err = rand.Read(id[:]) + if err != nil { + return PrivateID{}, err + } + // Clamping, for future use. + id[0] &= 248 + id[31] = (id[31] & 127) | 64 + return id, nil +} + +func (id PrivateID) MarshalText() ([]byte, error) { + b := make([]byte, hex.EncodedLen(len(id))) + if i := hex.Encode(b, id[:]); i != len(b) { + return nil, fmt.Errorf("logtail.PrivateID.MarshalText: i=%d", i) + } + return b, nil +} + +// ParsePrivateID returns a PrivateID from its hex (String) representation. +func ParsePrivateID(s string) (PrivateID, error) { + if len(s) != 64 { + return PrivateID{}, errors.New("invalid length") + } + var p PrivateID + for i := range p { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return PrivateID{}, errors.New("invalid hex character") + } + p[i] = (a << 4) | b + } + return p, nil +} + +// IsZero reports whether id is the zero value. +func (id PrivateID) IsZero() bool { return id == PrivateID{} } + +func (id *PrivateID) UnmarshalText(s []byte) error { + b, err := hex.DecodeString(string(s)) + if err != nil { + return fmt.Errorf("logtail.PrivateID.UnmarshalText: %v", err) + } + if len(b) != len(id) { + return fmt.Errorf("logtail.PrivateID.UnmarshalText: invalid hex length: %d", len(b)) + } + copy(id[:], b) + return nil +} + +func (id PrivateID) String() string { + b, err := id.MarshalText() + if err != nil { + panic(err) + } + return string(b) +} + +func (id PrivateID) Public() (pub PublicID) { + h := sha256.New() + h.Write(id[:]) + if n := copy(pub[:], h.Sum(pub[:0])); n != len(pub) { + panic(fmt.Sprintf("public id short copy: %d", n)) + } + return pub +} + +// PublicID represents an instance in the logs service for reading and adoption. +// The public ID value is a SHA-256 hash of a private ID. +type PublicID [sha256.Size]byte + +// ParsePublicID returns a PublicID from its hex (String) representation. +func ParsePublicID(s string) (PublicID, error) { + if len(s) != sha256.Size*2 { + return PublicID{}, errors.New("invalid length") + } + var p PublicID + for i := range p { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return PublicID{}, errors.New("invalid hex character") + } + p[i] = (a << 4) | b + } + return p, nil +} + +// MustParsePublicID calls ParsePublicID and panics in case of an error. +// It is intended for use with constant strings, typically in tests. +func MustParsePublicID(s string) PublicID { + id, err := ParsePublicID(s) + if err != nil { + panic(err) + } + return id +} + +func (id PublicID) MarshalText() ([]byte, error) { + b := make([]byte, hex.EncodedLen(len(id))) + if i := hex.Encode(b, id[:]); i != len(b) { + return nil, fmt.Errorf("logtail.PublicID.MarshalText: i=%d", i) + } + return b, nil +} + +func (id *PublicID) UnmarshalText(s []byte) error { + if len(s) != len(id)*2 { + return fmt.Errorf("logtail.PublicID.UnmarshalText: invalid hex length: %d", len(s)) + } + for i := range id { + a, ok1 := fromHexChar(s[i*2+0]) + b, ok2 := fromHexChar(s[i*2+1]) + if !ok1 || !ok2 { + return errors.New("invalid hex character") + } + id[i] = (a << 4) | b + } + return nil +} + +func (id PublicID) String() string { + b, err := id.MarshalText() + if err != nil { + panic(err) + } + return string(b) +} + +// fromHexChar converts a hex character into its value and a success flag. +func fromHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +func (id1 PublicID) Less(id2 PublicID) bool { + for i, c1 := range id1[:] { + c2 := id2[i] + if c1 != c2 { + return c1 < c2 + } + } + return false // equal +} + +func (id PublicID) IsZero() bool { + return id == PublicID{} +} + +func (id PublicID) Prefix64() uint64 { + return binary.BigEndian.Uint64(id[:8]) +} diff --git a/logtail/id_test.go b/types/logid/id_test.go similarity index 99% rename from logtail/id_test.go rename to types/logid/id_test.go index 1a7798dc6..dbe16d95d 100644 --- a/logtail/id_test.go +++ b/types/logid/id_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package logtail +package logid import ( "testing" diff --git a/wgengine/wgcfg/wgcfg_clone.go b/wgengine/wgcfg/wgcfg_clone.go index e556fe5aa..ed040cde4 100644 --- a/wgengine/wgcfg/wgcfg_clone.go +++ b/wgengine/wgcfg/wgcfg_clone.go @@ -9,9 +9,9 @@ package wgcfg import ( "net/netip" - "tailscale.com/logtail" "tailscale.com/tailcfg" "tailscale.com/types/key" + "tailscale.com/types/logid" ) // Clone makes a deep copy of Config. @@ -41,8 +41,8 @@ var _ConfigCloneNeedsRegeneration = Config(struct { DNS []netip.Addr Peers []Peer NetworkLogging struct { - NodeID logtail.PrivateID - DomainID logtail.PrivateID + NodeID logid.PrivateID + DomainID logid.PrivateID } }{})