Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+71
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package derp
|
||||
@@ -33,6 +33,7 @@ type Client struct {
|
||||
meshKey key.DERPMesh
|
||||
canAckPings bool
|
||||
isProber bool
|
||||
appName string
|
||||
|
||||
wmu sync.Mutex // hold while writing to bw
|
||||
bw *bufio.Writer
|
||||
@@ -60,6 +61,7 @@ type clientOpt struct {
|
||||
ServerPub key.NodePublic
|
||||
CanAckPings bool
|
||||
IsProber bool
|
||||
AppName string
|
||||
}
|
||||
|
||||
// MeshKey returns a ClientOpt to pass to the DERP server during connect to get
|
||||
@@ -69,7 +71,7 @@ type clientOpt struct {
|
||||
func MeshKey(k key.DERPMesh) ClientOpt { return clientOptFunc(func(o *clientOpt) { o.MeshKey = k }) }
|
||||
|
||||
// IsProber returns a ClientOpt to pass to the DERP server during connect to
|
||||
// declare that this client is a a prober.
|
||||
// declare that this client is a prober.
|
||||
func IsProber(v bool) ClientOpt { return clientOptFunc(func(o *clientOpt) { o.IsProber = v }) }
|
||||
|
||||
// ServerPublicKey returns a ClientOpt to declare that the server's DERP public key is known.
|
||||
@@ -84,6 +86,31 @@ func CanAckPings(v bool) ClientOpt {
|
||||
return clientOptFunc(func(o *clientOpt) { o.CanAckPings = v })
|
||||
}
|
||||
|
||||
// AppName returns a ClientOpt to set an opaque app name string to
|
||||
// advertise to the DERP server for stats purposes. It is sent to the
|
||||
// server in the ClientInfo. The name must be valid per [ValidAppName]
|
||||
// or NewClient returns an error.
|
||||
func AppName(name string) ClientOpt {
|
||||
return clientOptFunc(func(o *clientOpt) { o.AppName = name })
|
||||
}
|
||||
|
||||
// MaxAppNameLen is the maximum length in bytes of a [ClientInfo.AppName].
|
||||
const MaxAppNameLen = 32
|
||||
|
||||
// ValidAppName reports whether name is a valid app name: at most
|
||||
// [MaxAppNameLen] bytes of printable ASCII. The empty string is valid.
|
||||
func ValidAppName(name string) bool {
|
||||
if len(name) > MaxAppNameLen {
|
||||
return false
|
||||
}
|
||||
for i := range len(name) {
|
||||
if b := name[i]; b < ' ' || b > '~' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func NewClient(privateKey key.NodePrivate, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opts ...ClientOpt) (*Client, error) {
|
||||
var opt clientOpt
|
||||
for _, o := range opts {
|
||||
@@ -92,6 +119,9 @@ func NewClient(privateKey key.NodePrivate, nc Conn, brw *bufio.ReadWriter, logf
|
||||
}
|
||||
o.update(&opt)
|
||||
}
|
||||
if !ValidAppName(opt.AppName) {
|
||||
return nil, fmt.Errorf("invalid AppName %.40q", opt.AppName)
|
||||
}
|
||||
return newClient(privateKey, nc, brw, logf, opt)
|
||||
}
|
||||
|
||||
@@ -106,6 +136,7 @@ func newClient(privateKey key.NodePrivate, nc Conn, brw *bufio.ReadWriter, logf
|
||||
meshKey: opt.MeshKey,
|
||||
canAckPings: opt.CanAckPings,
|
||||
isProber: opt.IsProber,
|
||||
appName: opt.AppName,
|
||||
clock: tstime.StdClock{},
|
||||
}
|
||||
if opt.ServerPub.IsZero() {
|
||||
@@ -167,7 +198,7 @@ type ClientInfo struct {
|
||||
// trusted clients. It's required to subscribe to the
|
||||
// connection list & forward packets. It's empty for regular
|
||||
// users.
|
||||
MeshKey key.DERPMesh `json:"meshKey,omitempty,omitzero"`
|
||||
MeshKey key.DERPMesh `json:"meshKey,omitzero"`
|
||||
|
||||
// Version is the DERP protocol version that the client was built with.
|
||||
// See the ProtocolVersion const.
|
||||
@@ -179,6 +210,12 @@ type ClientInfo struct {
|
||||
|
||||
// IsProber is whether this client is a prober.
|
||||
IsProber bool `json:",omitempty"`
|
||||
|
||||
// AppName is an optional opaque app name string the client
|
||||
// advertises to the server for stats purposes. It must be
|
||||
// valid per [ValidAppName] or the server rejects the
|
||||
// connection.
|
||||
AppName string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Equal reports if two clientInfo values are equal.
|
||||
@@ -186,7 +223,7 @@ func (c *ClientInfo) Equal(other *ClientInfo) bool {
|
||||
if c == nil || other == nil {
|
||||
return c == other
|
||||
}
|
||||
if c.Version != other.Version || c.CanAckPings != other.CanAckPings || c.IsProber != other.IsProber {
|
||||
if c.Version != other.Version || c.CanAckPings != other.CanAckPings || c.IsProber != other.IsProber || c.AppName != other.AppName {
|
||||
return false
|
||||
}
|
||||
return c.MeshKey.Equal(other.MeshKey)
|
||||
@@ -198,6 +235,7 @@ func (c *Client) sendClientKey() error {
|
||||
MeshKey: c.meshKey,
|
||||
CanAckPings: c.canAckPings,
|
||||
IsProber: c.isProber,
|
||||
AppName: c.appName,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -249,15 +287,18 @@ func (c *Client) send(dstKey key.NodePublic, pkt []byte) (ret error) {
|
||||
return c.bw.Flush()
|
||||
}
|
||||
|
||||
func (c *Client) ForwardPacket(srcKey, dstKey key.NodePublic, pkt []byte) (err error) {
|
||||
// ForwardPacket sends a forward packet frame carrying pkt from srcKey
|
||||
// to dstKey. It writes and flushes pkt before returning and does not
|
||||
// retain it.
|
||||
func (c *Client) ForwardPacket(srcKey, dstKey key.NodePublic, pkt LoanedBytes) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("derp.ForwardPacket: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if len(pkt) > MaxPacketSize {
|
||||
return fmt.Errorf("packet too big: %d", len(pkt))
|
||||
if pkt.Len() > MaxPacketSize {
|
||||
return fmt.Errorf("packet too big: %d", pkt.Len())
|
||||
}
|
||||
|
||||
c.wmu.Lock()
|
||||
@@ -266,7 +307,7 @@ func (c *Client) ForwardPacket(srcKey, dstKey key.NodePublic, pkt []byte) (err e
|
||||
timer := c.clock.AfterFunc(5*time.Second, c.writeTimeoutFired)
|
||||
defer timer.Stop()
|
||||
|
||||
if err := WriteFrameHeader(c.bw, FrameForwardPacket, uint32(KeyLen*2+len(pkt))); err != nil {
|
||||
if err := WriteFrameHeader(c.bw, FrameForwardPacket, uint32(KeyLen*2+pkt.Len())); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.bw.Write(srcKey.AppendTo(nil)); err != nil {
|
||||
@@ -275,7 +316,7 @@ func (c *Client) ForwardPacket(srcKey, dstKey key.NodePublic, pkt []byte) (err e
|
||||
if _, err := c.bw.Write(dstKey.AppendTo(nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.bw.Write(pkt); err != nil {
|
||||
if _, err := pkt.WriteTo(c.bw); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bw.Flush()
|
||||
@@ -394,6 +435,10 @@ type PeerPresentMessage struct {
|
||||
IPPort netip.AddrPort
|
||||
// Flags is a bitmask of info about the client.
|
||||
Flags PeerPresentFlags
|
||||
// AppName is the optional app name the client advertised in
|
||||
// its ClientInfo, if any. It's empty if the client didn't
|
||||
// send one or the server is too old to relay it.
|
||||
AppName string
|
||||
}
|
||||
|
||||
func (PeerPresentMessage) msg() {}
|
||||
@@ -554,7 +599,7 @@ func (c *Client) recvTimeout(timeout time.Duration) (m ReceivedMessage, err erro
|
||||
return sm, nil
|
||||
case FrameKeepAlive:
|
||||
// A one-way keep-alive message that doesn't require an acknowledgement.
|
||||
// This predated framePing/framePong.
|
||||
// This predated FramePing/FramePong.
|
||||
return KeepAliveMessage{}, nil
|
||||
case FramePeerGone:
|
||||
if n < KeyLen {
|
||||
@@ -594,12 +639,27 @@ func (c *Client) recvTimeout(timeout time.Duration) (m ReceivedMessage, err erro
|
||||
binary.BigEndian.Uint16(chunk[ipLen:]),
|
||||
)
|
||||
|
||||
chunk, _, ok = cutLeadingN(remain, 1)
|
||||
chunk, remain, ok = cutLeadingN(remain, 1)
|
||||
if !ok {
|
||||
// Older server which doesn't send PeerPresentFlags.
|
||||
return msg, nil
|
||||
}
|
||||
msg.Flags = PeerPresentFlags(chunk[0])
|
||||
|
||||
chunk, remain, ok = cutLeadingN(remain, 1)
|
||||
if !ok {
|
||||
// Older server which doesn't send the app name.
|
||||
return msg, nil
|
||||
}
|
||||
nameLen := int(chunk[0])
|
||||
chunk, _, ok = cutLeadingN(remain, nameLen)
|
||||
if !ok {
|
||||
c.logf("[unexpected] short peerPresent app name from DERP server")
|
||||
return msg, nil
|
||||
}
|
||||
if name := string(chunk); ValidAppName(name) {
|
||||
msg.AppName = name
|
||||
}
|
||||
return msg, nil
|
||||
|
||||
case FrameRecvPacket:
|
||||
|
||||
Reference in new issue
Block a user