backend.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipn
  4. import (
  5. "fmt"
  6. "strings"
  7. "time"
  8. "tailscale.com/drive"
  9. "tailscale.com/health"
  10. "tailscale.com/ipn/ipnstate"
  11. "tailscale.com/tailcfg"
  12. "tailscale.com/types/empty"
  13. "tailscale.com/types/key"
  14. "tailscale.com/types/netmap"
  15. "tailscale.com/types/structs"
  16. "tailscale.com/types/views"
  17. )
  18. type State int
  19. const (
  20. NoState State = 0
  21. InUseOtherUser State = 1
  22. NeedsLogin State = 2
  23. NeedsMachineAuth State = 3
  24. Stopped State = 4
  25. Starting State = 5
  26. Running State = 6
  27. )
  28. // GoogleIDToken Type is the tailcfg.Oauth2Token.TokenType for the Google
  29. // ID tokens used by the Android client.
  30. const GoogleIDTokenType = "ts_android_google_login"
  31. func (s State) String() string {
  32. return [...]string{
  33. "NoState",
  34. "InUseOtherUser",
  35. "NeedsLogin",
  36. "NeedsMachineAuth",
  37. "Stopped",
  38. "Starting",
  39. "Running"}[s]
  40. }
  41. // EngineStatus contains WireGuard engine stats.
  42. type EngineStatus struct {
  43. RBytes, WBytes int64
  44. NumLive int
  45. LiveDERPs int // number of active DERP connections
  46. LivePeers map[key.NodePublic]ipnstate.PeerStatusLite
  47. }
  48. // NotifyWatchOpt is a bitmask of options about what type of Notify messages
  49. // to subscribe to.
  50. type NotifyWatchOpt uint64
  51. // NotifyWatchOpt values.
  52. //
  53. // These aren't declared using Go's iota because they're not purely internal to
  54. // the process and iota should not be used for values that are serialized to
  55. // disk or network. In this case, these values come over the network via the
  56. // LocalAPI, a mostly stable API.
  57. const (
  58. // NotifyWatchEngineUpdates, if set, causes Engine updates to be sent to the
  59. // client either regularly or when they change, without having to ask for
  60. // each one via Engine.RequestStatus.
  61. NotifyWatchEngineUpdates NotifyWatchOpt = 1 << 0
  62. NotifyInitialState NotifyWatchOpt = 1 << 1 // if set, the first Notify message (sent immediately) will contain the current State + BrowseToURL + SessionID
  63. NotifyInitialPrefs NotifyWatchOpt = 1 << 2 // if set, the first Notify message (sent immediately) will contain the current Prefs
  64. NotifyInitialNetMap NotifyWatchOpt = 1 << 3 // if set, the first Notify message (sent immediately) will contain the current NetMap
  65. NotifyNoPrivateKeys NotifyWatchOpt = 1 << 4 // (no-op) it used to redact private keys; now they always are and this does nothing
  66. NotifyInitialDriveShares NotifyWatchOpt = 1 << 5 // if set, the first Notify message (sent immediately) will contain the current Taildrive Shares
  67. NotifyInitialOutgoingFiles NotifyWatchOpt = 1 << 6 // if set, the first Notify message (sent immediately) will contain the current Taildrop OutgoingFiles
  68. NotifyInitialHealthState NotifyWatchOpt = 1 << 7 // if set, the first Notify message (sent immediately) will contain the current health.State of the client
  69. NotifyRateLimit NotifyWatchOpt = 1 << 8 // if set, rate limit spammy netmap updates to every few seconds
  70. NotifyHealthActions NotifyWatchOpt = 1 << 9 // if set, include PrimaryActions in health.State. Otherwise append the action URL to the text
  71. NotifyInitialSuggestedExitNode NotifyWatchOpt = 1 << 10 // if set, the first Notify message (sent immediately) will contain the current SuggestedExitNode if available
  72. )
  73. // Notify is a communication from a backend (e.g. tailscaled) to a frontend
  74. // (cmd/tailscale, iOS, macOS, Win Tasktray).
  75. // In any given notification, any or all of these may be nil, meaning
  76. // that they have not changed.
  77. // They are JSON-encoded on the wire, despite the lack of struct tags.
  78. type Notify struct {
  79. _ structs.Incomparable
  80. Version string // version number of IPN backend
  81. // SessionID identifies the unique WatchIPNBus session.
  82. // This field is only set in the first message when requesting
  83. // NotifyInitialState. Clients must store it on their side as
  84. // following notifications will not include this field.
  85. SessionID string `json:",omitzero"`
  86. // ErrMessage, if non-nil, contains a critical error message.
  87. // For State InUseOtherUser, ErrMessage is not critical and just contains the details.
  88. ErrMessage *string
  89. LoginFinished *empty.Message // non-nil when/if the login process succeeded
  90. State *State // if non-nil, the new or current IPN state
  91. Prefs *PrefsView // if non-nil && Valid, the new or current preferences
  92. NetMap *netmap.NetworkMap // if non-nil, the new or current netmap
  93. Engine *EngineStatus // if non-nil, the new or current wireguard stats
  94. BrowseToURL *string // if non-nil, UI should open a browser right now
  95. // FilesWaiting if non-nil means that files are buffered in
  96. // the Tailscale daemon and ready for local transfer to the
  97. // user's preferred storage location.
  98. //
  99. // Deprecated: use LocalClient.AwaitWaitingFiles instead.
  100. FilesWaiting *empty.Message `json:",omitzero"`
  101. // IncomingFiles, if non-nil, specifies which files are in the
  102. // process of being received. A nil IncomingFiles means this
  103. // Notify should not update the state of file transfers. A non-nil
  104. // but empty IncomingFiles means that no files are in the middle
  105. // of being transferred.
  106. //
  107. // Deprecated: use LocalClient.AwaitWaitingFiles instead.
  108. IncomingFiles []PartialFile `json:",omitzero"`
  109. // OutgoingFiles, if non-nil, tracks which files are in the process of
  110. // being sent via TailDrop, including files that finished, whether
  111. // successful or failed. This slice is sorted by Started time, then Name.
  112. OutgoingFiles []*OutgoingFile `json:",omitzero"`
  113. // LocalTCPPort, if non-nil, informs the UI frontend which
  114. // (non-zero) localhost TCP port it's listening on.
  115. // This is currently only used by Tailscale when run in the
  116. // macOS Network Extension.
  117. LocalTCPPort *uint16 `json:",omitzero"`
  118. // ClientVersion, if non-nil, describes whether a client version update
  119. // is available.
  120. ClientVersion *tailcfg.ClientVersion `json:",omitzero"`
  121. // DriveShares tracks the full set of current DriveShares that we're
  122. // publishing. Some client applications, like the MacOS and Windows clients,
  123. // will listen for updates to this and handle serving these shares under
  124. // the identity of the unprivileged user that is running the application. A
  125. // nil value here means that we're not broadcasting shares information, an
  126. // empty value means that there are no shares.
  127. DriveShares views.SliceView[*drive.Share, drive.ShareView]
  128. // Health is the last-known health state of the backend. When this field is
  129. // non-nil, a change in health verified, and the API client should surface
  130. // any changes to the user in the UI.
  131. Health *health.State `json:",omitzero"`
  132. // SuggestedExitNode, if non-nil, is the node that the backend has determined to
  133. // be the best exit node for the current network conditions.
  134. SuggestedExitNode *tailcfg.StableNodeID `json:",omitzero"`
  135. // type is mirrored in xcode/IPN/Core/LocalAPI/Model/LocalAPIModel.swift
  136. }
  137. func (n Notify) String() string {
  138. var sb strings.Builder
  139. sb.WriteString("Notify{")
  140. if n.ErrMessage != nil {
  141. fmt.Fprintf(&sb, "err=%q ", *n.ErrMessage)
  142. }
  143. if n.LoginFinished != nil {
  144. sb.WriteString("LoginFinished ")
  145. }
  146. if n.State != nil {
  147. fmt.Fprintf(&sb, "state=%v ", *n.State)
  148. }
  149. if n.Prefs != nil && n.Prefs.Valid() {
  150. fmt.Fprintf(&sb, "%v ", n.Prefs.Pretty())
  151. }
  152. if n.NetMap != nil {
  153. sb.WriteString("NetMap{...} ")
  154. }
  155. if n.Engine != nil {
  156. fmt.Fprintf(&sb, "wg=%v ", *n.Engine)
  157. }
  158. if n.BrowseToURL != nil {
  159. sb.WriteString("URL=<...> ")
  160. }
  161. if n.FilesWaiting != nil {
  162. sb.WriteString("FilesWaiting ")
  163. }
  164. if len(n.IncomingFiles) != 0 {
  165. sb.WriteString("IncomingFiles ")
  166. }
  167. if n.LocalTCPPort != nil {
  168. fmt.Fprintf(&sb, "tcpport=%v ", n.LocalTCPPort)
  169. }
  170. if n.Health != nil {
  171. sb.WriteString("Health{...} ")
  172. }
  173. if n.SuggestedExitNode != nil {
  174. fmt.Fprintf(&sb, "SuggestedExitNode=%v ", *n.SuggestedExitNode)
  175. }
  176. s := sb.String()
  177. if s == "Notify{" {
  178. return "Notify{}"
  179. } else {
  180. return s[0:len(s)-1] + "}"
  181. }
  182. }
  183. // PartialFile represents an in-progress incoming file transfer.
  184. type PartialFile struct {
  185. Name string // e.g. "foo.jpg"
  186. Started time.Time // time transfer started
  187. DeclaredSize int64 // or -1 if unknown
  188. Received int64 // bytes copied thus far
  189. // PartialPath is set non-empty in "direct" file mode to the
  190. // in-progress '*.partial' file's path when the peerapi isn't
  191. // being used; see LocalBackend.SetDirectFileRoot.
  192. PartialPath string `json:",omitempty"`
  193. FinalPath string `json:",omitempty"`
  194. // Done is set in "direct" mode when the partial file has been
  195. // closed and is ready for the caller to rename away the
  196. // ".partial" suffix.
  197. Done bool `json:",omitempty"`
  198. }
  199. // OutgoingFile represents an in-progress outgoing file transfer.
  200. type OutgoingFile struct {
  201. ID string `json:",omitempty"` // unique identifier for this transfer (a type 4 UUID)
  202. PeerID tailcfg.StableNodeID `json:",omitempty"` // identifier for the peer to which this is being transferred
  203. Name string `json:",omitempty"` // e.g. "foo.jpg"
  204. Started time.Time // time transfer started
  205. DeclaredSize int64 // or -1 if unknown
  206. Sent int64 // bytes copied thus far
  207. Finished bool // indicates whether or not the transfer finished
  208. Succeeded bool // for a finished transfer, indicates whether or not it was successful
  209. }
  210. // StateKey is an opaque identifier for a set of LocalBackend state
  211. // (preferences, private keys, etc.). It is also used as a key for
  212. // the various LoginProfiles that the instance may be signed into.
  213. //
  214. // Additionally, the StateKey can be debug setting name:
  215. //
  216. // - "_debug_magicsock_until" with value being a unix timestamp stringified
  217. // - "_debug_<component>_until" with value being a unix timestamp stringified
  218. type StateKey string
  219. // DebuggableComponents is a list of components whose debugging can be turned on
  220. // and off individually using the tailscale debug command.
  221. var DebuggableComponents = []string{
  222. "magicsock",
  223. "sockstats",
  224. "syspolicy",
  225. }
  226. type Options struct {
  227. // FrontendLogID is the public logtail id used by the frontend.
  228. FrontendLogID string
  229. // UpdatePrefs, if provided, overrides the Prefs already stored in the
  230. // backend state, *except* for the Persist member.
  231. //
  232. // TODO(apenwarr): Rename this to Prefs, and possibly move Prefs.Persist
  233. // elsewhere entirely (as it always should have been).
  234. UpdatePrefs *Prefs
  235. // AuthKey is an optional node auth key used to authorize a
  236. // new node key without user interaction.
  237. AuthKey string
  238. }