auto.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "tailscale.com/health"
  13. "tailscale.com/logtail/backoff"
  14. "tailscale.com/net/sockstats"
  15. "tailscale.com/tailcfg"
  16. "tailscale.com/tstime"
  17. "tailscale.com/types/key"
  18. "tailscale.com/types/logger"
  19. "tailscale.com/types/netmap"
  20. "tailscale.com/types/persist"
  21. "tailscale.com/types/structs"
  22. "tailscale.com/util/execqueue"
  23. )
  24. type LoginGoal struct {
  25. _ structs.Incomparable
  26. token *tailcfg.Oauth2Token // oauth token to use when logging in
  27. flags LoginFlags // flags to use when logging in
  28. url string // auth url that needs to be visited
  29. }
  30. var _ Client = (*Auto)(nil)
  31. // waitUnpause waits until either the client is unpaused or the Auto client is
  32. // shut down. It reports whether the client should keep running (i.e. it's not
  33. // closed).
  34. func (c *Auto) waitUnpause(routineLogName string) (keepRunning bool) {
  35. c.mu.Lock()
  36. if !c.paused || c.closed {
  37. defer c.mu.Unlock()
  38. return !c.closed
  39. }
  40. unpaused := c.unpausedChanLocked()
  41. c.mu.Unlock()
  42. c.logf("%s: awaiting unpause", routineLogName)
  43. return <-unpaused
  44. }
  45. // updateRoutine is responsible for informing the server of worthy changes to
  46. // our local state. It runs in its own goroutine.
  47. func (c *Auto) updateRoutine() {
  48. defer close(c.updateDone)
  49. bo := backoff.NewBackoff("updateRoutine", c.logf, 30*time.Second)
  50. // lastUpdateGenInformed is the value of lastUpdateAt that we've successfully
  51. // informed the server of.
  52. var lastUpdateGenInformed updateGen
  53. for {
  54. if !c.waitUnpause("updateRoutine") {
  55. c.logf("updateRoutine: exiting")
  56. return
  57. }
  58. c.mu.Lock()
  59. gen := c.lastUpdateGen
  60. ctx := c.mapCtx
  61. needUpdate := gen > 0 && gen != lastUpdateGenInformed && c.loggedIn
  62. c.mu.Unlock()
  63. if !needUpdate {
  64. // Nothing to do, wait for a signal.
  65. select {
  66. case <-ctx.Done():
  67. continue
  68. case <-c.updateCh:
  69. continue
  70. }
  71. }
  72. t0 := c.clock.Now()
  73. err := c.direct.SendUpdate(ctx)
  74. d := time.Since(t0).Round(time.Millisecond)
  75. if err != nil {
  76. if ctx.Err() == nil {
  77. c.direct.logf("lite map update error after %v: %v", d, err)
  78. }
  79. bo.BackOff(ctx, err)
  80. continue
  81. }
  82. bo.BackOff(ctx, nil)
  83. c.direct.logf("[v1] successful lite map update in %v", d)
  84. lastUpdateGenInformed = gen
  85. }
  86. }
  87. // atomicGen is an atomic int64 generator. It is used to generate monotonically
  88. // increasing numbers for updateGen.
  89. var atomicGen atomic.Int64
  90. func nextUpdateGen() updateGen {
  91. return updateGen(atomicGen.Add(1))
  92. }
  93. // updateGen is a monotonically increasing number that represents a particular
  94. // update to the local state.
  95. type updateGen int64
  96. // Auto connects to a tailcontrol server for a node.
  97. // It's a concrete implementation of the Client interface.
  98. type Auto struct {
  99. direct *Direct // our interface to the server APIs
  100. clock tstime.Clock
  101. logf logger.Logf
  102. closed bool
  103. updateCh chan struct{} // readable when we should inform the server of a change
  104. observer Observer // called to update Client status; always non-nil
  105. observerQueue execqueue.ExecQueue
  106. unregisterHealthWatch func()
  107. mu sync.Mutex // mutex guards the following fields
  108. wantLoggedIn bool // whether the user wants to be logged in per last method call
  109. urlToVisit string // the last url we were told to visit
  110. expiry time.Time
  111. // lastUpdateGen is the gen of last update we had an update worth sending to
  112. // the server.
  113. lastUpdateGen updateGen
  114. paused bool // whether we should stop making HTTP requests
  115. unpauseWaiters []chan bool // chans that gets sent true (once) on wake, or false on Shutdown
  116. loggedIn bool // true if currently logged in
  117. loginGoal *LoginGoal // non-nil if some login activity is desired
  118. inMapPoll bool // true once we get the first MapResponse in a stream; false when HTTP response ends
  119. state State // TODO(bradfitz): delete this, make it computed by method from other state
  120. authCtx context.Context // context used for auth requests
  121. mapCtx context.Context // context used for netmap and update requests
  122. authCancel func() // cancel authCtx
  123. mapCancel func() // cancel mapCtx
  124. authDone chan struct{} // when closed, authRoutine is done
  125. mapDone chan struct{} // when closed, mapRoutine is done
  126. updateDone chan struct{} // when closed, updateRoutine is done
  127. }
  128. // New creates and starts a new Auto.
  129. func New(opts Options) (*Auto, error) {
  130. c, err := NewNoStart(opts)
  131. if c != nil {
  132. c.Start()
  133. }
  134. return c, err
  135. }
  136. // NewNoStart creates a new Auto, but without calling Start on it.
  137. func NewNoStart(opts Options) (_ *Auto, err error) {
  138. direct, err := NewDirect(opts)
  139. if err != nil {
  140. return nil, err
  141. }
  142. defer func() {
  143. if err != nil {
  144. direct.Close()
  145. }
  146. }()
  147. if opts.Observer == nil {
  148. return nil, errors.New("missing required Options.Observer")
  149. }
  150. if opts.Logf == nil {
  151. opts.Logf = func(fmt string, args ...any) {}
  152. }
  153. if opts.Clock == nil {
  154. opts.Clock = tstime.StdClock{}
  155. }
  156. c := &Auto{
  157. direct: direct,
  158. clock: opts.Clock,
  159. logf: opts.Logf,
  160. updateCh: make(chan struct{}, 1),
  161. authDone: make(chan struct{}),
  162. mapDone: make(chan struct{}),
  163. updateDone: make(chan struct{}),
  164. observer: opts.Observer,
  165. }
  166. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  167. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, opts.Logf)
  168. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  169. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, opts.Logf)
  170. c.unregisterHealthWatch = health.RegisterWatcher(direct.ReportHealthChange)
  171. return c, nil
  172. }
  173. // SetPaused controls whether HTTP activity should be paused.
  174. //
  175. // The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
  176. func (c *Auto) SetPaused(paused bool) {
  177. c.mu.Lock()
  178. defer c.mu.Unlock()
  179. if paused == c.paused || c.closed {
  180. return
  181. }
  182. c.logf("setPaused(%v)", paused)
  183. c.paused = paused
  184. if paused {
  185. c.cancelMapCtxLocked()
  186. c.cancelAuthCtxLocked()
  187. return
  188. }
  189. for _, ch := range c.unpauseWaiters {
  190. ch <- true
  191. }
  192. c.unpauseWaiters = nil
  193. }
  194. // Start starts the client's goroutines.
  195. //
  196. // It should only be called for clients created by NewNoStart.
  197. func (c *Auto) Start() {
  198. go c.authRoutine()
  199. go c.mapRoutine()
  200. go c.updateRoutine()
  201. }
  202. // updateControl sends a new OmitPeers, non-streaming map request (to just send
  203. // Hostinfo/Netinfo/Endpoints info, while keeping an existing streaming response
  204. // open).
  205. //
  206. // It should be called whenever there's something new to tell the server.
  207. func (c *Auto) updateControl() {
  208. gen := nextUpdateGen()
  209. c.mu.Lock()
  210. if gen < c.lastUpdateGen {
  211. // This update is out of date.
  212. c.mu.Unlock()
  213. return
  214. }
  215. c.lastUpdateGen = gen
  216. c.mu.Unlock()
  217. select {
  218. case c.updateCh <- struct{}{}:
  219. default:
  220. }
  221. }
  222. // cancelAuthCtxLocked is like cancelAuthCtx, but assumes the caller holds c.mu.
  223. func (c *Auto) cancelAuthCtxLocked() {
  224. if c.authCancel != nil {
  225. c.authCancel()
  226. }
  227. if !c.closed {
  228. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  229. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, c.logf)
  230. }
  231. }
  232. // cancelMapCtxLocked is like cancelMapCtx, but assumes the caller holds c.mu.
  233. func (c *Auto) cancelMapCtxLocked() {
  234. if c.mapCancel != nil {
  235. c.mapCancel()
  236. }
  237. if !c.closed {
  238. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  239. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, c.logf)
  240. }
  241. }
  242. // restartMap cancels the existing mapPoll and liteUpdates, and then starts a
  243. // new one.
  244. func (c *Auto) restartMap() {
  245. c.mu.Lock()
  246. c.cancelMapCtxLocked()
  247. synced := c.inMapPoll
  248. c.mu.Unlock()
  249. c.logf("[v1] restartMap: synced=%v", synced)
  250. c.updateControl()
  251. }
  252. func (c *Auto) authRoutine() {
  253. defer close(c.authDone)
  254. bo := backoff.NewBackoff("authRoutine", c.logf, 30*time.Second)
  255. for {
  256. if !c.waitUnpause("authRoutine") {
  257. c.logf("authRoutine: exiting")
  258. return
  259. }
  260. c.mu.Lock()
  261. goal := c.loginGoal
  262. ctx := c.authCtx
  263. if goal != nil {
  264. c.logf("[v1] authRoutine: %s; wantLoggedIn=%v", c.state, true)
  265. } else {
  266. c.logf("[v1] authRoutine: %s; goal=nil paused=%v", c.state, c.paused)
  267. }
  268. c.mu.Unlock()
  269. report := func(err error, msg string) {
  270. c.logf("[v1] %s: %v", msg, err)
  271. // don't send status updates for context errors,
  272. // since context cancelation is always on purpose.
  273. if ctx.Err() == nil {
  274. c.sendStatus("authRoutine-report", err, "", nil)
  275. }
  276. }
  277. if goal == nil {
  278. health.SetAuthRoutineInError(nil)
  279. // Wait for user to Login or Logout.
  280. <-ctx.Done()
  281. c.logf("[v1] authRoutine: context done.")
  282. continue
  283. }
  284. c.mu.Lock()
  285. c.urlToVisit = goal.url
  286. if goal.url != "" {
  287. c.state = StateURLVisitRequired
  288. } else {
  289. c.state = StateAuthenticating
  290. }
  291. c.mu.Unlock()
  292. var url string
  293. var err error
  294. var f string
  295. if goal.url != "" {
  296. url, err = c.direct.WaitLoginURL(ctx, goal.url)
  297. f = "WaitLoginURL"
  298. } else {
  299. url, err = c.direct.TryLogin(ctx, goal.token, goal.flags)
  300. f = "TryLogin"
  301. }
  302. if err != nil {
  303. health.SetAuthRoutineInError(err)
  304. report(err, f)
  305. bo.BackOff(ctx, err)
  306. continue
  307. }
  308. if url != "" {
  309. // goal.url ought to be empty here.
  310. // However, not all control servers get this right,
  311. // and logging about it here just generates noise.
  312. c.mu.Lock()
  313. c.urlToVisit = url
  314. c.loginGoal = &LoginGoal{
  315. flags: LoginDefault,
  316. url: url,
  317. }
  318. c.state = StateURLVisitRequired
  319. c.mu.Unlock()
  320. c.sendStatus("authRoutine-url", err, url, nil)
  321. if goal.url == url {
  322. // The server sent us the same URL we already tried,
  323. // backoff to avoid a busy loop.
  324. bo.BackOff(ctx, errors.New("login URL not changing"))
  325. } else {
  326. bo.BackOff(ctx, nil)
  327. }
  328. continue
  329. }
  330. // success
  331. health.SetAuthRoutineInError(nil)
  332. c.mu.Lock()
  333. c.urlToVisit = ""
  334. c.loggedIn = true
  335. c.loginGoal = nil
  336. c.state = StateAuthenticated
  337. c.mu.Unlock()
  338. c.sendStatus("authRoutine-success", nil, "", nil)
  339. c.restartMap()
  340. bo.BackOff(ctx, nil)
  341. }
  342. }
  343. // ExpiryForTests returns the credential expiration time, or the zero value if
  344. // the expiration time isn't known. It's used in tests only.
  345. func (c *Auto) ExpiryForTests() time.Time {
  346. c.mu.Lock()
  347. defer c.mu.Unlock()
  348. return c.expiry
  349. }
  350. // DirectForTest returns the underlying direct client object.
  351. // It's used in tests only.
  352. func (c *Auto) DirectForTest() *Direct {
  353. return c.direct
  354. }
  355. // unpausedChanLocked returns a new channel that gets sent
  356. // either a true when unpaused or false on Auto.Shutdown.
  357. //
  358. // c.mu must be held
  359. func (c *Auto) unpausedChanLocked() <-chan bool {
  360. unpaused := make(chan bool, 1)
  361. c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
  362. return unpaused
  363. }
  364. // mapRoutineState is the state of Auto.mapRoutine while it's running.
  365. type mapRoutineState struct {
  366. c *Auto
  367. bo *backoff.Backoff
  368. }
  369. var _ NetmapDeltaUpdater = mapRoutineState{}
  370. func (mrs mapRoutineState) UpdateFullNetmap(nm *netmap.NetworkMap) {
  371. c := mrs.c
  372. c.mu.Lock()
  373. ctx := c.mapCtx
  374. c.inMapPoll = true
  375. if c.loggedIn {
  376. c.state = StateSynchronized
  377. }
  378. c.expiry = nm.Expiry
  379. stillAuthed := c.loggedIn
  380. c.logf("[v1] mapRoutine: netmap received: %s", c.state)
  381. c.mu.Unlock()
  382. if stillAuthed {
  383. c.sendStatus("mapRoutine-got-netmap", nil, "", nm)
  384. }
  385. // Reset the backoff timer if we got a netmap.
  386. mrs.bo.BackOff(ctx, nil)
  387. }
  388. func (mrs mapRoutineState) UpdateNetmapDelta(muts []netmap.NodeMutation) bool {
  389. c := mrs.c
  390. c.mu.Lock()
  391. goodState := c.loggedIn && c.inMapPoll
  392. ndu, canDelta := c.observer.(NetmapDeltaUpdater)
  393. c.mu.Unlock()
  394. if !goodState || !canDelta {
  395. return false
  396. }
  397. ctx, cancel := context.WithTimeout(c.mapCtx, 2*time.Second)
  398. defer cancel()
  399. var ok bool
  400. err := c.observerQueue.RunSync(ctx, func() {
  401. ok = ndu.UpdateNetmapDelta(muts)
  402. })
  403. return err == nil && ok
  404. }
  405. // mapRoutine is responsible for keeping a read-only streaming connection to the
  406. // control server, and keeping the netmap up to date.
  407. func (c *Auto) mapRoutine() {
  408. defer close(c.mapDone)
  409. mrs := mapRoutineState{
  410. c: c,
  411. bo: backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second),
  412. }
  413. for {
  414. if !c.waitUnpause("mapRoutine") {
  415. c.logf("mapRoutine: exiting")
  416. return
  417. }
  418. c.mu.Lock()
  419. c.logf("[v1] mapRoutine: %s", c.state)
  420. loggedIn := c.loggedIn
  421. ctx := c.mapCtx
  422. c.mu.Unlock()
  423. report := func(err error, msg string) {
  424. c.logf("[v1] %s: %v", msg, err)
  425. err = fmt.Errorf("%s: %w", msg, err)
  426. // don't send status updates for context errors,
  427. // since context cancelation is always on purpose.
  428. if ctx.Err() == nil {
  429. c.sendStatus("mapRoutine1", err, "", nil)
  430. }
  431. }
  432. if !loggedIn {
  433. // Wait for something interesting to happen
  434. c.mu.Lock()
  435. c.inMapPoll = false
  436. c.mu.Unlock()
  437. <-ctx.Done()
  438. c.logf("[v1] mapRoutine: context done.")
  439. continue
  440. }
  441. health.SetOutOfPollNetMap()
  442. err := c.direct.PollNetMap(ctx, mrs)
  443. health.SetOutOfPollNetMap()
  444. c.mu.Lock()
  445. c.inMapPoll = false
  446. if c.state == StateSynchronized {
  447. c.state = StateAuthenticated
  448. }
  449. paused := c.paused
  450. c.mu.Unlock()
  451. if paused {
  452. mrs.bo.BackOff(ctx, nil)
  453. c.logf("mapRoutine: paused")
  454. } else {
  455. mrs.bo.BackOff(ctx, err)
  456. report(err, "PollNetMap")
  457. }
  458. }
  459. }
  460. func (c *Auto) AuthCantContinue() bool {
  461. if c == nil {
  462. return true
  463. }
  464. c.mu.Lock()
  465. defer c.mu.Unlock()
  466. return !c.loggedIn && (c.loginGoal == nil || c.loginGoal.url != "")
  467. }
  468. func (c *Auto) SetHostinfo(hi *tailcfg.Hostinfo) {
  469. if hi == nil {
  470. panic("nil Hostinfo")
  471. }
  472. if !c.direct.SetHostinfo(hi) {
  473. // No changes. Don't log.
  474. return
  475. }
  476. // Send new Hostinfo to server
  477. c.updateControl()
  478. }
  479. func (c *Auto) SetNetInfo(ni *tailcfg.NetInfo) {
  480. if ni == nil {
  481. panic("nil NetInfo")
  482. }
  483. if !c.direct.SetNetInfo(ni) {
  484. return
  485. }
  486. // Send new NetInfo to server
  487. c.updateControl()
  488. }
  489. // SetTKAHead updates the TKA head hash that map-request infrastructure sends.
  490. func (c *Auto) SetTKAHead(headHash string) {
  491. if !c.direct.SetTKAHead(headHash) {
  492. return
  493. }
  494. // Send new TKAHead to server
  495. c.updateControl()
  496. }
  497. // sendStatus can not be called with the c.mu held.
  498. func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
  499. c.mu.Lock()
  500. if c.closed {
  501. c.mu.Unlock()
  502. return
  503. }
  504. state := c.state
  505. loggedIn := c.loggedIn
  506. inMapPoll := c.inMapPoll
  507. c.mu.Unlock()
  508. c.logf("[v1] sendStatus: %s: %v", who, state)
  509. var p persist.PersistView
  510. if nm != nil && loggedIn && inMapPoll {
  511. p = c.direct.GetPersist()
  512. } else {
  513. // don't send netmap status, as it's misleading when we're
  514. // not logged in.
  515. nm = nil
  516. }
  517. new := Status{
  518. URL: url,
  519. Persist: p,
  520. NetMap: nm,
  521. Err: err,
  522. state: state,
  523. }
  524. // Launch a new goroutine to avoid blocking the caller while the observer
  525. // does its thing, which may result in a call back into the client.
  526. c.observerQueue.Add(func() {
  527. c.observer.SetControlClientStatus(c, new)
  528. })
  529. }
  530. func (c *Auto) Login(t *tailcfg.Oauth2Token, flags LoginFlags) {
  531. c.logf("client.Login(%v, %v)", t != nil, flags)
  532. c.mu.Lock()
  533. defer c.mu.Unlock()
  534. if c.closed {
  535. return
  536. }
  537. c.wantLoggedIn = true
  538. c.loginGoal = &LoginGoal{
  539. token: t,
  540. flags: flags,
  541. }
  542. c.cancelMapCtxLocked()
  543. c.cancelAuthCtxLocked()
  544. }
  545. var ErrClientClosed = errors.New("client closed")
  546. func (c *Auto) Logout(ctx context.Context) error {
  547. c.logf("client.Logout()")
  548. c.mu.Lock()
  549. c.wantLoggedIn = false
  550. c.loginGoal = nil
  551. closed := c.closed
  552. c.mu.Unlock()
  553. if closed {
  554. return ErrClientClosed
  555. }
  556. if err := c.direct.TryLogout(ctx); err != nil {
  557. return err
  558. }
  559. c.mu.Lock()
  560. c.loggedIn = false
  561. c.state = StateNotAuthenticated
  562. c.cancelAuthCtxLocked()
  563. c.cancelMapCtxLocked()
  564. c.mu.Unlock()
  565. c.sendStatus("authRoutine-wantout", nil, "", nil)
  566. return nil
  567. }
  568. func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
  569. return c.direct.SetExpirySooner(ctx, expiry)
  570. }
  571. // UpdateEndpoints sets the client's discovered endpoints and sends
  572. // them to the control server if they've changed.
  573. //
  574. // It does not retain the provided slice.
  575. func (c *Auto) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
  576. changed := c.direct.SetEndpoints(endpoints)
  577. if changed {
  578. c.updateControl()
  579. }
  580. }
  581. func (c *Auto) Shutdown() {
  582. c.logf("client.Shutdown()")
  583. c.mu.Lock()
  584. closed := c.closed
  585. direct := c.direct
  586. if !closed {
  587. c.closed = true
  588. c.observerQueue.Shutdown()
  589. c.cancelAuthCtxLocked()
  590. c.cancelMapCtxLocked()
  591. for _, w := range c.unpauseWaiters {
  592. w <- false
  593. }
  594. c.unpauseWaiters = nil
  595. }
  596. c.mu.Unlock()
  597. c.logf("client.Shutdown")
  598. if !closed {
  599. c.unregisterHealthWatch()
  600. <-c.authDone
  601. <-c.mapDone
  602. <-c.updateDone
  603. if direct != nil {
  604. direct.Close()
  605. }
  606. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  607. defer cancel()
  608. c.observerQueue.Wait(ctx)
  609. c.logf("Client.Shutdown done.")
  610. }
  611. }
  612. // NodePublicKey returns the node public key currently in use. This is
  613. // used exclusively in tests.
  614. func (c *Auto) TestOnlyNodePublicKey() key.NodePublic {
  615. priv := c.direct.GetPersist()
  616. return priv.PrivateNodeKey().Public()
  617. }
  618. func (c *Auto) TestOnlySetAuthKey(authkey string) {
  619. c.direct.mu.Lock()
  620. defer c.direct.mu.Unlock()
  621. c.direct.authKey = authkey
  622. }
  623. func (c *Auto) TestOnlyTimeNow() time.Time {
  624. return c.clock.Now()
  625. }
  626. // SetDNS sends the SetDNSRequest request to the control plane server,
  627. // requesting a DNS record be created or updated.
  628. func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
  629. return c.direct.SetDNS(ctx, req)
  630. }
  631. func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
  632. return c.direct.DoNoiseRequest(req)
  633. }
  634. // GetSingleUseNoiseRoundTripper returns a RoundTripper that can be only be used
  635. // once (and must be used once) to make a single HTTP request over the noise
  636. // channel to the coordination server.
  637. //
  638. // In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
  639. // payload, if any.
  640. func (c *Auto) GetSingleUseNoiseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
  641. return c.direct.GetSingleUseNoiseRoundTripper(ctx)
  642. }