2023-01-27 21:37:20 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
package ipn
|
|
|
|
|
|
|
|
import (
|
2021-04-30 03:18:50 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2020-02-03 18:57:34 +00:00
|
|
|
"time"
|
|
|
|
|
2024-04-02 20:32:30 +00:00
|
|
|
"tailscale.com/drive"
|
2020-03-27 20:26:35 +00:00
|
|
|
"tailscale.com/ipn/ipnstate"
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/tailcfg"
|
2020-02-14 21:09:19 +00:00
|
|
|
"tailscale.com/types/empty"
|
2021-11-02 01:40:39 +00:00
|
|
|
"tailscale.com/types/key"
|
2021-02-05 23:44:46 +00:00
|
|
|
"tailscale.com/types/netmap"
|
2020-05-03 20:58:39 +00:00
|
|
|
"tailscale.com/types/structs"
|
2024-03-07 16:56:11 +00:00
|
|
|
"tailscale.com/types/views"
|
2020-02-05 22:16:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type State int
|
|
|
|
|
|
|
|
const (
|
2022-11-09 05:58:10 +00:00
|
|
|
NoState State = 0
|
|
|
|
InUseOtherUser State = 1
|
|
|
|
NeedsLogin State = 2
|
|
|
|
NeedsMachineAuth State = 3
|
|
|
|
Stopped State = 4
|
|
|
|
Starting State = 5
|
|
|
|
Running State = 6
|
2020-02-05 22:16:58 +00:00
|
|
|
)
|
|
|
|
|
2021-03-19 17:21:33 +00:00
|
|
|
// GoogleIDToken Type is the tailcfg.Oauth2Token.TokenType for the Google
|
2020-07-13 20:13:11 +00:00
|
|
|
// ID tokens used by the Android client.
|
|
|
|
const GoogleIDTokenType = "ts_android_google_login"
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
func (s State) String() string {
|
2020-11-02 17:52:59 +00:00
|
|
|
return [...]string{
|
|
|
|
"NoState",
|
|
|
|
"InUseOtherUser",
|
|
|
|
"NeedsLogin",
|
|
|
|
"NeedsMachineAuth",
|
|
|
|
"Stopped",
|
|
|
|
"Starting",
|
|
|
|
"Running"}[s]
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-25 20:30:28 +00:00
|
|
|
// EngineStatus contains WireGuard engine stats.
|
2020-02-05 22:16:58 +00:00
|
|
|
type EngineStatus struct {
|
2021-02-04 21:12:42 +00:00
|
|
|
RBytes, WBytes int64
|
2020-02-05 22:16:58 +00:00
|
|
|
NumLive int
|
2020-03-19 06:55:14 +00:00
|
|
|
LiveDERPs int // number of active DERP connections
|
2021-11-02 01:40:39 +00:00
|
|
|
LivePeers map[key.NodePublic]ipnstate.PeerStatusLite
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2022-11-26 20:19:16 +00:00
|
|
|
// NotifyWatchOpt is a bitmask of options about what type of Notify messages
|
|
|
|
// to subscribe to.
|
|
|
|
type NotifyWatchOpt uint64
|
|
|
|
|
|
|
|
const (
|
|
|
|
// NotifyWatchEngineUpdates, if set, causes Engine updates to be sent to the
|
|
|
|
// client either regularly or when they change, without having to ask for
|
|
|
|
// each one via RequestEngineStatus.
|
|
|
|
NotifyWatchEngineUpdates NotifyWatchOpt = 1 << iota
|
2022-12-01 01:39:43 +00:00
|
|
|
|
2023-08-31 16:43:54 +00:00
|
|
|
NotifyInitialState // if set, the first Notify message (sent immediately) will contain the current State + BrowseToURL + SessionID
|
2022-12-01 01:39:43 +00:00
|
|
|
NotifyInitialPrefs // if set, the first Notify message (sent immediately) will contain the current Prefs
|
|
|
|
NotifyInitialNetMap // if set, the first Notify message (sent immediately) will contain the current NetMap
|
2022-12-21 20:44:51 +00:00
|
|
|
|
2024-03-20 02:54:37 +00:00
|
|
|
NotifyNoPrivateKeys // if set, private keys that would normally be sent in updates are zeroed out
|
2024-04-03 17:09:58 +00:00
|
|
|
NotifyInitialDriveShares // if set, the first Notify message (sent immediately) will contain the current Taildrive Shares
|
2024-03-20 02:54:37 +00:00
|
|
|
NotifyInitialOutgoingFiles // if set, the first Notify message (sent immediately) will contain the current Taildrop OutgoingFiles
|
2022-11-26 20:19:16 +00:00
|
|
|
)
|
|
|
|
|
2020-02-25 18:04:20 +00:00
|
|
|
// Notify is a communication from a backend (e.g. tailscaled) to a frontend
|
|
|
|
// (cmd/tailscale, iOS, macOS, Win Tasktray).
|
2020-02-05 22:16:58 +00:00
|
|
|
// In any given notification, any or all of these may be nil, meaning
|
|
|
|
// that they have not changed.
|
2020-02-25 18:04:20 +00:00
|
|
|
// They are JSON-encoded on the wire, despite the lack of struct tags.
|
2020-02-05 22:16:58 +00:00
|
|
|
type Notify struct {
|
2021-04-23 17:26:25 +00:00
|
|
|
_ structs.Incomparable
|
|
|
|
Version string // version number of IPN backend
|
|
|
|
|
2023-08-31 16:43:54 +00:00
|
|
|
// SessionID identifies the unique WatchIPNBus session.
|
|
|
|
// This field is only set in the first message when requesting
|
|
|
|
// NotifyInitialState. Clients must store it on their side as
|
|
|
|
// following notifications will not include this field.
|
|
|
|
SessionID string `json:",omitempty"`
|
|
|
|
|
2021-04-23 17:26:25 +00:00
|
|
|
// ErrMessage, if non-nil, contains a critical error message.
|
|
|
|
// For State InUseOtherUser, ErrMessage is not critical and just contains the details.
|
|
|
|
ErrMessage *string
|
|
|
|
|
2022-05-03 21:16:34 +00:00
|
|
|
LoginFinished *empty.Message // non-nil when/if the login process succeeded
|
|
|
|
State *State // if non-nil, the new or current IPN state
|
2022-11-03 22:46:17 +00:00
|
|
|
Prefs *PrefsView // if non-nil && Valid, the new or current preferences
|
2022-05-03 21:16:34 +00:00
|
|
|
NetMap *netmap.NetworkMap // if non-nil, the new or current netmap
|
2022-09-25 18:29:55 +00:00
|
|
|
Engine *EngineStatus // if non-nil, the new or current wireguard stats
|
2022-05-03 21:16:34 +00:00
|
|
|
BrowseToURL *string // if non-nil, UI should open a browser right now
|
|
|
|
BackendLogID *string // if non-nil, the public logtail ID used by backend
|
2020-02-25 18:04:20 +00:00
|
|
|
|
2021-04-09 14:57:32 +00:00
|
|
|
// FilesWaiting if non-nil means that files are buffered in
|
|
|
|
// the Tailscale daemon and ready for local transfer to the
|
|
|
|
// user's preferred storage location.
|
2022-11-20 21:25:54 +00:00
|
|
|
//
|
|
|
|
// Deprecated: use LocalClient.AwaitWaitingFiles instead.
|
2021-04-09 14:57:32 +00:00
|
|
|
FilesWaiting *empty.Message `json:",omitempty"`
|
|
|
|
|
|
|
|
// IncomingFiles, if non-nil, specifies which files are in the
|
|
|
|
// process of being received. A nil IncomingFiles means this
|
|
|
|
// Notify should not update the state of file transfers. A non-nil
|
|
|
|
// but empty IncomingFiles means that no files are in the middle
|
|
|
|
// of being transferred.
|
2022-11-20 21:25:54 +00:00
|
|
|
//
|
|
|
|
// Deprecated: use LocalClient.AwaitWaitingFiles instead.
|
2021-04-09 14:57:32 +00:00
|
|
|
IncomingFiles []PartialFile `json:",omitempty"`
|
2021-03-30 18:19:42 +00:00
|
|
|
|
2024-03-20 02:54:37 +00:00
|
|
|
// OutgoingFiles, if non-nil, tracks which files are in the process of
|
|
|
|
// being sent via TailDrop, including files that finished, whether
|
|
|
|
// successful or failed. This slice is sorted by Started time, then Name.
|
|
|
|
OutgoingFiles []*OutgoingFile `json:",omitempty"`
|
|
|
|
|
2020-07-09 16:08:54 +00:00
|
|
|
// LocalTCPPort, if non-nil, informs the UI frontend which
|
|
|
|
// (non-zero) localhost TCP port it's listening on.
|
|
|
|
// This is currently only used by Tailscale when run in the
|
|
|
|
// macOS Network Extension.
|
|
|
|
LocalTCPPort *uint16 `json:",omitempty"`
|
|
|
|
|
2022-11-24 03:13:41 +00:00
|
|
|
// ClientVersion, if non-nil, describes whether a client version update
|
|
|
|
// is available.
|
|
|
|
ClientVersion *tailcfg.ClientVersion `json:",omitempty"`
|
|
|
|
|
2024-04-03 17:09:58 +00:00
|
|
|
// DriveShares tracks the full set of current DriveShares that we're
|
2024-03-07 16:56:11 +00:00
|
|
|
// publishing. Some client applications, like the MacOS and Windows clients,
|
|
|
|
// will listen for updates to this and handle serving these shares under
|
|
|
|
// the identity of the unprivileged user that is running the application. A
|
|
|
|
// nil value here means that we're not broadcasting shares information, an
|
|
|
|
// empty value means that there are no shares.
|
2024-04-03 17:09:58 +00:00
|
|
|
DriveShares views.SliceView[*drive.Share, drive.ShareView]
|
2024-02-02 18:45:32 +00:00
|
|
|
|
2020-02-25 18:04:20 +00:00
|
|
|
// type is mirrored in xcode/Shared/IPN.swift
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2021-04-30 03:18:50 +00:00
|
|
|
func (n Notify) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("Notify{")
|
|
|
|
if n.ErrMessage != nil {
|
|
|
|
fmt.Fprintf(&sb, "err=%q ", *n.ErrMessage)
|
|
|
|
}
|
|
|
|
if n.LoginFinished != nil {
|
|
|
|
sb.WriteString("LoginFinished ")
|
|
|
|
}
|
|
|
|
if n.State != nil {
|
|
|
|
fmt.Fprintf(&sb, "state=%v ", *n.State)
|
|
|
|
}
|
2022-11-03 23:06:25 +00:00
|
|
|
if n.Prefs != nil && n.Prefs.Valid() {
|
2021-04-30 03:18:50 +00:00
|
|
|
fmt.Fprintf(&sb, "%v ", n.Prefs.Pretty())
|
|
|
|
}
|
|
|
|
if n.NetMap != nil {
|
|
|
|
sb.WriteString("NetMap{...} ")
|
|
|
|
}
|
|
|
|
if n.Engine != nil {
|
|
|
|
fmt.Fprintf(&sb, "wg=%v ", *n.Engine)
|
|
|
|
}
|
|
|
|
if n.BrowseToURL != nil {
|
|
|
|
sb.WriteString("URL=<...> ")
|
|
|
|
}
|
|
|
|
if n.BackendLogID != nil {
|
|
|
|
sb.WriteString("BackendLogID ")
|
|
|
|
}
|
|
|
|
if n.FilesWaiting != nil {
|
|
|
|
sb.WriteString("FilesWaiting ")
|
|
|
|
}
|
|
|
|
if len(n.IncomingFiles) != 0 {
|
|
|
|
sb.WriteString("IncomingFiles ")
|
|
|
|
}
|
|
|
|
if n.LocalTCPPort != nil {
|
|
|
|
fmt.Fprintf(&sb, "tcpport=%v ", n.LocalTCPPort)
|
|
|
|
}
|
|
|
|
s := sb.String()
|
|
|
|
return s[0:len(s)-1] + "}"
|
|
|
|
}
|
|
|
|
|
2024-03-20 02:54:37 +00:00
|
|
|
// PartialFile represents an in-progress incoming file transfer.
|
2021-04-08 21:54:25 +00:00
|
|
|
type PartialFile struct {
|
|
|
|
Name string // e.g. "foo.jpg"
|
|
|
|
Started time.Time // time transfer started
|
|
|
|
DeclaredSize int64 // or -1 if unknown
|
|
|
|
Received int64 // bytes copied thus far
|
2021-04-12 21:05:44 +00:00
|
|
|
|
2021-04-16 19:33:04 +00:00
|
|
|
// PartialPath is set non-empty in "direct" file mode to the
|
|
|
|
// in-progress '*.partial' file's path when the peerapi isn't
|
|
|
|
// being used; see LocalBackend.SetDirectFileRoot.
|
|
|
|
PartialPath string `json:",omitempty"`
|
2024-01-09 20:11:34 +00:00
|
|
|
FinalPath string `json:",omitempty"`
|
2021-04-16 19:33:04 +00:00
|
|
|
|
|
|
|
// Done is set in "direct" mode when the partial file has been
|
|
|
|
// closed and is ready for the caller to rename away the
|
|
|
|
// ".partial" suffix.
|
|
|
|
Done bool `json:",omitempty"`
|
2021-04-08 21:54:25 +00:00
|
|
|
}
|
|
|
|
|
2024-03-20 02:54:37 +00:00
|
|
|
// OutgoingFile represents an in-progress outgoing file transfer.
|
|
|
|
type OutgoingFile struct {
|
2024-03-26 12:27:58 +00:00
|
|
|
ID string `json:",omitempty"` // unique identifier for this transfer (a type 4 UUID)
|
|
|
|
PeerID tailcfg.StableNodeID `json:",omitempty"` // identifier for the peer to which this is being transferred
|
2024-03-20 02:54:37 +00:00
|
|
|
Name string `json:",omitempty"` // e.g. "foo.jpg"
|
|
|
|
Started time.Time // time transfer started
|
|
|
|
DeclaredSize int64 // or -1 if unknown
|
|
|
|
Sent int64 // bytes copied thus far
|
|
|
|
Finished bool // indicates whether or not the transfer finished
|
|
|
|
Succeeded bool // for a finished transfer, indicates whether or not it was successful
|
|
|
|
}
|
|
|
|
|
2020-02-03 18:35:52 +00:00
|
|
|
// StateKey is an opaque identifier for a set of LocalBackend state
|
2022-11-09 05:58:10 +00:00
|
|
|
// (preferences, private keys, etc.). It is also used as a key for
|
|
|
|
// the various LoginProfiles that the instance may be signed into.
|
2022-10-04 03:39:45 +00:00
|
|
|
//
|
|
|
|
// Additionally, the StateKey can be debug setting name:
|
|
|
|
//
|
|
|
|
// - "_debug_magicsock_until" with value being a unix timestamp stringified
|
|
|
|
// - "_debug_<component>_until" with value being a unix timestamp stringified
|
2020-02-03 18:35:52 +00:00
|
|
|
type StateKey string
|
|
|
|
|
2023-10-03 10:31:56 +00:00
|
|
|
// DebuggableComponents is a list of components whose debugging can be turned on
|
|
|
|
// and off individually using the tailscale debug command.
|
|
|
|
var DebuggableComponents = []string{
|
|
|
|
"magicsock",
|
|
|
|
"sockstats",
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
type Options struct {
|
2020-02-14 00:07:50 +00:00
|
|
|
// FrontendLogID is the public logtail id used by the frontend.
|
2020-02-03 18:35:52 +00:00
|
|
|
FrontendLogID string
|
2022-11-09 05:58:10 +00:00
|
|
|
// LegacyMigrationPrefs are used to migrate preferences from the
|
|
|
|
// frontend to the backend.
|
|
|
|
// If non-nil, they are imported as a new profile.
|
|
|
|
LegacyMigrationPrefs *Prefs `json:"Prefs"`
|
2022-12-02 19:32:26 +00:00
|
|
|
// UpdatePrefs, if provided, overrides Options.LegacyMigrationPrefs
|
|
|
|
// *and* the Prefs already stored in the backend state, *except* for
|
|
|
|
// the Persist member. If you just want to provide prefs, this is
|
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 08:26:07 +00:00
|
|
|
// probably what you want.
|
|
|
|
//
|
2022-12-02 19:32:26 +00:00
|
|
|
// TODO(apenwarr): Rename this to Prefs, and possibly move Prefs.Persist
|
|
|
|
// elsewhere entirely (as it always should have been). Or, move the
|
|
|
|
// fancy state migration stuff out of Start().
|
ipnlocal: accept a new opts.UpdatePrefs field.
This is needed because the original opts.Prefs field was at some point
subverted for use in frontend->backend state migration for backward
compatibility on some platforms. We still need that feature, but we
also need the feature of providing the full set of prefs from
`tailscale up`, *not* including overwriting the prefs.Persist keys, so
we can't use the original field from `tailscale up`.
`tailscale up` had attempted to compensate for that by doing SetPrefs()
before Start(), but that violates the ipn.Backend contract, which says
you should call Start() before anything else (that's why it's called
Start()). As a result, doing SetPrefs({ControlURL=...,
WantRunning=true}) would cause a connection to the *previous* control
server (because WantRunning=true), and then connect to the *new*
control server only after running Start().
This problem may have been avoided before, but only by pure luck.
It turned out to be relatively harmless since the connection to the old
control server was immediately closed and replaced anyway, but it
created a race condition that could have caused spurious notifications
or rejected keys if the server responded quickly.
As already covered by existing TODOs, a better fix would be to have
Start() get out of the business of state migration altogether. But
we're approaching a release so I want to make the minimum possible fix.
Fixes #1840.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-04 08:26:07 +00:00
|
|
|
UpdatePrefs *Prefs
|
2020-04-09 07:16:43 +00:00
|
|
|
// AuthKey is an optional node auth key used to authorize a
|
|
|
|
// new node key without user interaction.
|
|
|
|
AuthKey string
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|