diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 2638d666c..19bcc78d6 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -113,7 +113,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/strbuilder from tailscale.com/net/packet tailscale.com/types/structs from tailscale.com/control/controlclient+ tailscale.com/types/wgkey from tailscale.com/control/controlclient+ - tailscale.com/util/dnsname from tailscale.com/wgengine/tsdns + tailscale.com/util/dnsname from tailscale.com/wgengine/tsdns+ LW tailscale.com/util/endian from tailscale.com/net/netns+ tailscale.com/util/lineread from tailscale.com/control/controlclient+ tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 85267fae6..b3a9f61ac 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -775,6 +775,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*Netw MachineKey: machinePubKey, Expiry: resp.Node.KeyExpiry, Name: resp.Node.Name, + DisplayName: resp.Node.DisplayName, Addresses: resp.Node.Addresses, Peers: resp.Peers, LocalPort: localPort, @@ -799,6 +800,9 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*Netw } addUserProfile(nm.User) for _, peer := range resp.Peers { + if peer.DisplayName == "" { + peer.DisplayName = peer.DefaultDisplayName() + } if !peer.Sharer.IsZero() { if c.keepSharerAndUserSplit { addUserProfile(peer.Sharer) @@ -808,6 +812,9 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*Netw } addUserProfile(peer.User) } + if resp.Node.DisplayName == "" { + nm.DisplayName = resp.Node.DefaultDisplayName() + } if resp.Node.MachineAuthorized { nm.MachineStatus = tailcfg.MachineAuthorized } else { diff --git a/control/controlclient/netmap.go b/control/controlclient/netmap.go index ff574a39b..fe8553b66 100644 --- a/control/controlclient/netmap.go +++ b/control/controlclient/netmap.go @@ -28,7 +28,9 @@ type NetworkMap struct { PrivateKey wgkey.Private Expiry time.Time // Name is the DNS name assigned to this node. - Name string + Name string + // DisplayName is the title to show for the node in client UIs. + DisplayName string Addresses []netaddr.IPPrefix LocalPort uint16 // used for debugging MachineStatus tailcfg.MachineStatus diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index ba948125c..098c95d56 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -20,6 +20,7 @@ "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" + "tailscale.com/util/dnsname" ) // CurrentMapRequestVersion is the current MapRequest.Version value. @@ -160,6 +161,12 @@ type Node struct { StableID StableNodeID Name string // DNS + // DisplayName is the title to show for the node in client + // UIs. This field is assigned by default in controlclient, + // but can be overriden by providing this field non-empty + // in a MapResponse. + DisplayName string `json:",omitempty"` + // User is the user who created the node. If ACL tags are in // use for the node then it doesn't reflect the ACL identity // that the node is running as. @@ -185,6 +192,21 @@ type Node struct { MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus } +// DefaultDisplayName returns a value suitable +// for using as the default value for n.DisplayName. +func (n *Node) DefaultDisplayName() string { + if n.Name != "" { + // Use the Magic DNS prefix as the default display name. + return dnsname.ToBaseName(n.Name) + } + if n.Hostinfo.Hostname != "" { + // When no Magic DNS name is present, use the hostname. + return n.Hostinfo.Hostname + } + // When we've exhausted all other name options, use the node's ID. + return n.ID.String() +} + type MachineStatus int const ( @@ -796,6 +818,7 @@ func (n *Node) Equal(n2 *Node) bool { n.ID == n2.ID && n.StableID == n2.StableID && n.Name == n2.Name && + n.DisplayName == n2.DisplayName && n.User == n2.User && n.Sharer == n2.Sharer && n.Key == n2.Key && diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 32e097bc5..3101048b4 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -64,6 +64,7 @@ func (src *Node) Clone() *Node { ID NodeID StableID StableNodeID Name string + DisplayName string User UserID Sharer UserID Key NodeKey diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 02bc499af..794f7d4a1 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -189,7 +189,7 @@ func TestHostinfoEqual(t *testing.T) { func TestNodeEqual(t *testing.T) { nodeHandles := []string{ - "ID", "StableID", "Name", "User", "Sharer", + "ID", "StableID", "Name", "DisplayName", "User", "Sharer", "Key", "KeyExpiry", "Machine", "DiscoKey", "Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo", "Created", "LastSeen", "KeepAlive", "MachineAuthorized", diff --git a/util/dnsname/dnsname.go b/util/dnsname/dnsname.go index 1488272a4..633471e2f 100644 --- a/util/dnsname/dnsname.go +++ b/util/dnsname/dnsname.go @@ -17,3 +17,11 @@ func HasSuffix(name, suffix string) bool { nameBase := strings.TrimSuffix(name, suffix) return len(nameBase) < len(name) && strings.HasSuffix(nameBase, ".") } + +// ToBaseName removes the domain ending from a DNS name of a node. +func ToBaseName(name string) string { + if i := strings.Index(name, "."); i != -1 { + return name[:i] + } + return name +} diff --git a/util/dnsname/dnsname_test.go b/util/dnsname/dnsname_test.go index da4e51384..a8c97ed8b 100644 --- a/util/dnsname/dnsname_test.go +++ b/util/dnsname/dnsname_test.go @@ -26,3 +26,21 @@ func TestHasSuffix(t *testing.T) { } } } + +func TestToBaseName(t *testing.T) { + tests := []struct { + name string + want string + }{ + {"foo", "foo"}, + {"foo.com", "foo"}, + {"foo.example.com.beta.tailscale.net", "foo"}, + {"computer-a.test.gmail.com.beta.tailscale.net", "computer-a"}, + } + for _, tt := range tests { + got := ToBaseName(tt.name) + if got != tt.want { + t.Errorf("ToBaseName(%q) = %q; want %q", tt.name, got, tt.want) + } + } +}