auto.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  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. "time"
  11. "tailscale.com/health"
  12. "tailscale.com/logtail/backoff"
  13. "tailscale.com/net/sockstats"
  14. "tailscale.com/tailcfg"
  15. "tailscale.com/types/empty"
  16. "tailscale.com/types/key"
  17. "tailscale.com/types/logger"
  18. "tailscale.com/types/netmap"
  19. "tailscale.com/types/persist"
  20. "tailscale.com/types/structs"
  21. )
  22. type LoginGoal struct {
  23. _ structs.Incomparable
  24. wantLoggedIn bool // true if we *want* to be logged in
  25. token *tailcfg.Oauth2Token // oauth token to use when logging in
  26. flags LoginFlags // flags to use when logging in
  27. url string // auth url that needs to be visited
  28. loggedOutResult chan<- error
  29. }
  30. func (g *LoginGoal) sendLogoutError(err error) {
  31. if g.loggedOutResult == nil {
  32. return
  33. }
  34. select {
  35. case g.loggedOutResult <- err:
  36. default:
  37. }
  38. }
  39. var _ Client = (*Auto)(nil)
  40. // Auto connects to a tailcontrol server for a node.
  41. // It's a concrete implementation of the Client interface.
  42. type Auto struct {
  43. direct *Direct // our interface to the server APIs
  44. timeNow func() time.Time
  45. logf logger.Logf
  46. expiry *time.Time
  47. closed bool
  48. newMapCh chan struct{} // readable when we must restart a map request
  49. statusFunc func(Status) // called to update Client status; always non-nil
  50. unregisterHealthWatch func()
  51. mu sync.Mutex // mutex guards the following fields
  52. paused bool // whether we should stop making HTTP requests
  53. unpauseWaiters []chan struct{}
  54. loggedIn bool // true if currently logged in
  55. loginGoal *LoginGoal // non-nil if some login activity is desired
  56. synced bool // true if our netmap is up-to-date
  57. inPollNetMap bool // true if currently running a PollNetMap
  58. inLiteMapUpdate bool // true if a lite (non-streaming) map request is outstanding
  59. liteMapUpdateCancel context.CancelFunc // cancels a lite map update, may be nil
  60. liteMapUpdateCancels int // how many times we've canceled a lite map update
  61. inSendStatus int // number of sendStatus calls currently in progress
  62. state State
  63. authCtx context.Context // context used for auth requests
  64. mapCtx context.Context // context used for netmap requests
  65. authCancel func() // cancel the auth context
  66. mapCancel func() // cancel the netmap context
  67. quit chan struct{} // when closed, goroutines should all exit
  68. authDone chan struct{} // when closed, auth goroutine is done
  69. mapDone chan struct{} // when closed, map goroutine is done
  70. }
  71. // New creates and starts a new Auto.
  72. func New(opts Options) (*Auto, error) {
  73. c, err := NewNoStart(opts)
  74. if c != nil {
  75. c.Start()
  76. }
  77. return c, err
  78. }
  79. // NewNoStart creates a new Auto, but without calling Start on it.
  80. func NewNoStart(opts Options) (_ *Auto, err error) {
  81. direct, err := NewDirect(opts)
  82. if err != nil {
  83. return nil, err
  84. }
  85. defer func() {
  86. if err != nil {
  87. direct.Close()
  88. }
  89. }()
  90. if opts.Status == nil {
  91. return nil, errors.New("missing required Options.Status")
  92. }
  93. if opts.Logf == nil {
  94. opts.Logf = func(fmt string, args ...any) {}
  95. }
  96. if opts.TimeNow == nil {
  97. opts.TimeNow = time.Now
  98. }
  99. c := &Auto{
  100. direct: direct,
  101. timeNow: opts.TimeNow,
  102. logf: opts.Logf,
  103. newMapCh: make(chan struct{}, 1),
  104. quit: make(chan struct{}),
  105. authDone: make(chan struct{}),
  106. mapDone: make(chan struct{}),
  107. statusFunc: opts.Status,
  108. }
  109. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  110. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto)
  111. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  112. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto)
  113. c.unregisterHealthWatch = health.RegisterWatcher(direct.ReportHealthChange)
  114. return c, nil
  115. }
  116. // SetPaused controls whether HTTP activity should be paused.
  117. //
  118. // The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
  119. func (c *Auto) SetPaused(paused bool) {
  120. c.mu.Lock()
  121. defer c.mu.Unlock()
  122. if paused == c.paused {
  123. return
  124. }
  125. c.logf("setPaused(%v)", paused)
  126. c.paused = paused
  127. if paused {
  128. // Only cancel the map routine. (The auth routine isn't expensive
  129. // so it's fine to keep it running.)
  130. c.cancelMapLocked()
  131. } else {
  132. for _, ch := range c.unpauseWaiters {
  133. close(ch)
  134. }
  135. c.unpauseWaiters = nil
  136. }
  137. }
  138. // Start starts the client's goroutines.
  139. //
  140. // It should only be called for clients created by NewNoStart.
  141. func (c *Auto) Start() {
  142. go c.authRoutine()
  143. go c.mapRoutine()
  144. }
  145. // sendNewMapRequest either sends a new OmitPeers, non-streaming map request
  146. // (to just send Hostinfo/Netinfo/Endpoints info, while keeping an existing
  147. // streaming response open), or start a new streaming one if necessary.
  148. //
  149. // It should be called whenever there's something new to tell the server.
  150. func (c *Auto) sendNewMapRequest() {
  151. c.mu.Lock()
  152. // If we're not already streaming a netmap, then tear down everything
  153. // and start a new stream (which starts by sending a new map request)
  154. if !c.inPollNetMap || !c.loggedIn {
  155. c.mu.Unlock()
  156. c.cancelMapSafely()
  157. return
  158. }
  159. // If we are already in process of doing a LiteMapUpdate, cancel it and
  160. // try a new one. If this is the 10th time we have done this
  161. // cancelation, tear down everything and start again.
  162. const maxLiteMapUpdateAttempts = 10
  163. if c.inLiteMapUpdate {
  164. // Always cancel the in-flight lite map update, regardless of
  165. // whether we cancel the streaming map request or not.
  166. c.liteMapUpdateCancel()
  167. c.inLiteMapUpdate = false
  168. if c.liteMapUpdateCancels >= maxLiteMapUpdateAttempts {
  169. // Not making progress
  170. c.mu.Unlock()
  171. c.cancelMapSafely()
  172. return
  173. }
  174. // Increment our cancel counter and continue below to start a
  175. // new lite update.
  176. c.liteMapUpdateCancels++
  177. }
  178. // Otherwise, send a lite update that doesn't keep a
  179. // long-running stream response.
  180. defer c.mu.Unlock()
  181. c.inLiteMapUpdate = true
  182. ctx, cancel := context.WithTimeout(c.mapCtx, 10*time.Second)
  183. c.liteMapUpdateCancel = cancel
  184. go func() {
  185. defer cancel()
  186. t0 := time.Now()
  187. err := c.direct.SendLiteMapUpdate(ctx)
  188. d := time.Since(t0).Round(time.Millisecond)
  189. c.mu.Lock()
  190. c.inLiteMapUpdate = false
  191. c.liteMapUpdateCancel = nil
  192. if err == nil {
  193. c.liteMapUpdateCancels = 0
  194. }
  195. c.mu.Unlock()
  196. if err == nil {
  197. c.logf("[v1] successful lite map update in %v", d)
  198. return
  199. }
  200. if ctx.Err() == nil {
  201. c.logf("lite map update after %v: %v", d, err)
  202. }
  203. if !errors.Is(ctx.Err(), context.Canceled) {
  204. // Fall back to restarting the long-polling map
  205. // request (the old heavy way) if the lite update
  206. // failed for reasons other than the context being
  207. // canceled.
  208. c.cancelMapSafely()
  209. }
  210. }()
  211. }
  212. func (c *Auto) cancelAuth() {
  213. c.mu.Lock()
  214. if c.authCancel != nil {
  215. c.authCancel()
  216. }
  217. if !c.closed {
  218. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  219. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto)
  220. }
  221. c.mu.Unlock()
  222. }
  223. func (c *Auto) cancelMapLocked() {
  224. if c.mapCancel != nil {
  225. c.mapCancel()
  226. }
  227. if !c.closed {
  228. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  229. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto)
  230. }
  231. }
  232. func (c *Auto) cancelMapUnsafely() {
  233. c.mu.Lock()
  234. c.cancelMapLocked()
  235. c.mu.Unlock()
  236. }
  237. func (c *Auto) cancelMapSafely() {
  238. c.mu.Lock()
  239. defer c.mu.Unlock()
  240. // Always reset our lite map cancels counter if we're canceling
  241. // everything, since we're about to restart with a new map update; this
  242. // allows future calls to sendNewMapRequest to retry sending lite
  243. // updates.
  244. c.liteMapUpdateCancels = 0
  245. c.logf("[v1] cancelMapSafely: synced=%v", c.synced)
  246. if c.inPollNetMap {
  247. // received at least one netmap since the last
  248. // interruption. That means the server has already
  249. // fully processed our last request, which might
  250. // include UpdateEndpoints(). Interrupt it and try
  251. // again.
  252. c.cancelMapLocked()
  253. } else {
  254. // !synced means we either haven't done a netmap
  255. // request yet, or it hasn't answered yet. So the
  256. // server is in an undefined state. If we send
  257. // another netmap request too soon, it might race
  258. // with the last one, and if we're very unlucky,
  259. // the new request will be applied before the old one,
  260. // and the wrong endpoints will get registered. We
  261. // have to tell the client to abort politely, only
  262. // after it receives a response to its existing netmap
  263. // request.
  264. select {
  265. case c.newMapCh <- struct{}{}:
  266. c.logf("[v1] cancelMapSafely: wrote to channel")
  267. default:
  268. // if channel write failed, then there was already
  269. // an outstanding newMapCh request. One is enough,
  270. // since it'll always use the latest endpoints.
  271. c.logf("[v1] cancelMapSafely: channel was full")
  272. }
  273. }
  274. }
  275. func (c *Auto) authRoutine() {
  276. defer close(c.authDone)
  277. bo := backoff.NewBackoff("authRoutine", c.logf, 30*time.Second)
  278. for {
  279. c.mu.Lock()
  280. goal := c.loginGoal
  281. ctx := c.authCtx
  282. if goal != nil {
  283. c.logf("[v1] authRoutine: %s; wantLoggedIn=%v", c.state, goal.wantLoggedIn)
  284. } else {
  285. c.logf("[v1] authRoutine: %s; goal=nil paused=%v", c.state, c.paused)
  286. }
  287. c.mu.Unlock()
  288. select {
  289. case <-c.quit:
  290. c.logf("[v1] authRoutine: quit")
  291. return
  292. default:
  293. }
  294. report := func(err error, msg string) {
  295. c.logf("[v1] %s: %v", msg, err)
  296. // don't send status updates for context errors,
  297. // since context cancelation is always on purpose.
  298. if ctx.Err() == nil {
  299. c.sendStatus("authRoutine-report", err, "", nil)
  300. }
  301. }
  302. if goal == nil {
  303. health.SetAuthRoutineInError(nil)
  304. // Wait for user to Login or Logout.
  305. <-ctx.Done()
  306. c.logf("[v1] authRoutine: context done.")
  307. continue
  308. }
  309. if !goal.wantLoggedIn {
  310. health.SetAuthRoutineInError(nil)
  311. err := c.direct.TryLogout(ctx)
  312. goal.sendLogoutError(err)
  313. if err != nil {
  314. report(err, "TryLogout")
  315. bo.BackOff(ctx, err)
  316. continue
  317. }
  318. // success
  319. c.mu.Lock()
  320. c.loggedIn = false
  321. c.loginGoal = nil
  322. c.state = StateNotAuthenticated
  323. c.synced = false
  324. c.mu.Unlock()
  325. c.sendStatus("authRoutine-wantout", nil, "", nil)
  326. bo.BackOff(ctx, nil)
  327. } else { // ie. goal.wantLoggedIn
  328. c.mu.Lock()
  329. if goal.url != "" {
  330. c.state = StateURLVisitRequired
  331. } else {
  332. c.state = StateAuthenticating
  333. }
  334. c.mu.Unlock()
  335. var url string
  336. var err error
  337. var f string
  338. if goal.url != "" {
  339. url, err = c.direct.WaitLoginURL(ctx, goal.url)
  340. f = "WaitLoginURL"
  341. } else {
  342. url, err = c.direct.TryLogin(ctx, goal.token, goal.flags)
  343. f = "TryLogin"
  344. }
  345. if err != nil {
  346. health.SetAuthRoutineInError(err)
  347. report(err, f)
  348. bo.BackOff(ctx, err)
  349. continue
  350. }
  351. if url != "" {
  352. // goal.url ought to be empty here.
  353. // However, not all control servers get this right,
  354. // and logging about it here just generates noise.
  355. c.mu.Lock()
  356. c.loginGoal = &LoginGoal{
  357. wantLoggedIn: true,
  358. flags: LoginDefault,
  359. url: url,
  360. }
  361. c.state = StateURLVisitRequired
  362. c.synced = false
  363. c.mu.Unlock()
  364. c.sendStatus("authRoutine-url", err, url, nil)
  365. if goal.url == url {
  366. // The server sent us the same URL we already tried,
  367. // backoff to avoid a busy loop.
  368. bo.BackOff(ctx, errors.New("login URL not changing"))
  369. } else {
  370. bo.BackOff(ctx, nil)
  371. }
  372. continue
  373. }
  374. // success
  375. health.SetAuthRoutineInError(nil)
  376. c.mu.Lock()
  377. c.loggedIn = true
  378. c.loginGoal = nil
  379. c.state = StateAuthenticated
  380. c.mu.Unlock()
  381. c.sendStatus("authRoutine-success", nil, "", nil)
  382. c.cancelMapSafely()
  383. bo.BackOff(ctx, nil)
  384. }
  385. }
  386. }
  387. // Expiry returns the credential expiration time, or the zero time if
  388. // the expiration time isn't known. Used in tests only.
  389. func (c *Auto) Expiry() *time.Time {
  390. c.mu.Lock()
  391. defer c.mu.Unlock()
  392. return c.expiry
  393. }
  394. // Direct returns the underlying direct client object. Used in tests
  395. // only.
  396. func (c *Auto) Direct() *Direct {
  397. return c.direct
  398. }
  399. // unpausedChanLocked returns a new channel that is closed when the
  400. // current Auto pause is unpaused.
  401. //
  402. // c.mu must be held
  403. func (c *Auto) unpausedChanLocked() <-chan struct{} {
  404. unpaused := make(chan struct{})
  405. c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
  406. return unpaused
  407. }
  408. func (c *Auto) mapRoutine() {
  409. defer close(c.mapDone)
  410. bo := backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second)
  411. for {
  412. c.mu.Lock()
  413. if c.paused {
  414. unpaused := c.unpausedChanLocked()
  415. c.mu.Unlock()
  416. c.logf("mapRoutine: awaiting unpause")
  417. select {
  418. case <-unpaused:
  419. c.logf("mapRoutine: unpaused")
  420. case <-c.quit:
  421. c.logf("mapRoutine: quit")
  422. return
  423. }
  424. continue
  425. }
  426. c.logf("[v1] mapRoutine: %s", c.state)
  427. loggedIn := c.loggedIn
  428. ctx := c.mapCtx
  429. c.mu.Unlock()
  430. select {
  431. case <-c.quit:
  432. c.logf("mapRoutine: quit")
  433. return
  434. default:
  435. }
  436. report := func(err error, msg string) {
  437. c.logf("[v1] %s: %v", msg, err)
  438. err = fmt.Errorf("%s: %w", msg, err)
  439. // don't send status updates for context errors,
  440. // since context cancelation is always on purpose.
  441. if ctx.Err() == nil {
  442. c.sendStatus("mapRoutine1", err, "", nil)
  443. }
  444. }
  445. if !loggedIn {
  446. // Wait for something interesting to happen
  447. c.mu.Lock()
  448. c.synced = false
  449. // c.state is set by authRoutine()
  450. c.mu.Unlock()
  451. select {
  452. case <-ctx.Done():
  453. c.logf("[v1] mapRoutine: context done.")
  454. case <-c.newMapCh:
  455. c.logf("[v1] mapRoutine: new map needed while idle.")
  456. }
  457. } else {
  458. // Be sure this is false when we're not inside
  459. // PollNetMap, so that cancelMapSafely() can notify
  460. // us correctly.
  461. c.mu.Lock()
  462. c.inPollNetMap = false
  463. c.mu.Unlock()
  464. health.SetInPollNetMap(false)
  465. err := c.direct.PollNetMap(ctx, func(nm *netmap.NetworkMap) {
  466. health.SetInPollNetMap(true)
  467. c.mu.Lock()
  468. select {
  469. case <-c.newMapCh:
  470. c.logf("[v1] mapRoutine: new map request during PollNetMap. canceling.")
  471. c.cancelMapLocked()
  472. // Don't emit this netmap; we're
  473. // about to request a fresh one.
  474. c.mu.Unlock()
  475. return
  476. default:
  477. }
  478. c.synced = true
  479. c.inPollNetMap = true
  480. if c.loggedIn {
  481. c.state = StateSynchronized
  482. }
  483. exp := nm.Expiry
  484. c.expiry = &exp
  485. stillAuthed := c.loggedIn
  486. state := c.state
  487. c.mu.Unlock()
  488. c.logf("[v1] mapRoutine: netmap received: %s", state)
  489. if stillAuthed {
  490. c.sendStatus("mapRoutine-got-netmap", nil, "", nm)
  491. }
  492. })
  493. health.SetInPollNetMap(false)
  494. c.mu.Lock()
  495. c.synced = false
  496. c.inPollNetMap = false
  497. if c.state == StateSynchronized {
  498. c.state = StateAuthenticated
  499. }
  500. paused := c.paused
  501. c.mu.Unlock()
  502. if paused {
  503. c.logf("mapRoutine: paused")
  504. continue
  505. }
  506. if err != nil {
  507. report(err, "PollNetMap")
  508. bo.BackOff(ctx, err)
  509. continue
  510. }
  511. bo.BackOff(ctx, nil)
  512. }
  513. }
  514. }
  515. func (c *Auto) AuthCantContinue() bool {
  516. if c == nil {
  517. return true
  518. }
  519. c.mu.Lock()
  520. defer c.mu.Unlock()
  521. return !c.loggedIn && (c.loginGoal == nil || c.loginGoal.url != "")
  522. }
  523. func (c *Auto) SetHostinfo(hi *tailcfg.Hostinfo) {
  524. if hi == nil {
  525. panic("nil Hostinfo")
  526. }
  527. if !c.direct.SetHostinfo(hi) {
  528. // No changes. Don't log.
  529. return
  530. }
  531. // Send new Hostinfo to server
  532. c.sendNewMapRequest()
  533. }
  534. func (c *Auto) SetNetInfo(ni *tailcfg.NetInfo) {
  535. if ni == nil {
  536. panic("nil NetInfo")
  537. }
  538. if !c.direct.SetNetInfo(ni) {
  539. return
  540. }
  541. // Send new NetInfo to server
  542. c.sendNewMapRequest()
  543. }
  544. // SetTKAHead updates the TKA head hash that map-request infrastructure sends.
  545. func (c *Auto) SetTKAHead(headHash string) {
  546. c.direct.SetTKAHead(headHash)
  547. }
  548. func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
  549. c.mu.Lock()
  550. if c.closed {
  551. c.mu.Unlock()
  552. return
  553. }
  554. state := c.state
  555. loggedIn := c.loggedIn
  556. synced := c.synced
  557. c.inSendStatus++
  558. c.mu.Unlock()
  559. c.logf("[v1] sendStatus: %s: %v", who, state)
  560. var p *persist.PersistView
  561. var loginFin, logoutFin *empty.Message
  562. if state == StateAuthenticated {
  563. loginFin = new(empty.Message)
  564. }
  565. if state == StateNotAuthenticated {
  566. logoutFin = new(empty.Message)
  567. }
  568. if nm != nil && loggedIn && synced {
  569. pp := c.direct.GetPersist()
  570. p = &pp
  571. } else {
  572. // don't send netmap status, as it's misleading when we're
  573. // not logged in.
  574. nm = nil
  575. }
  576. new := Status{
  577. LoginFinished: loginFin,
  578. LogoutFinished: logoutFin,
  579. URL: url,
  580. Persist: p,
  581. NetMap: nm,
  582. State: state,
  583. Err: err,
  584. }
  585. c.statusFunc(new)
  586. c.mu.Lock()
  587. c.inSendStatus--
  588. c.mu.Unlock()
  589. }
  590. func (c *Auto) Login(t *tailcfg.Oauth2Token, flags LoginFlags) {
  591. c.logf("client.Login(%v, %v)", t != nil, flags)
  592. c.mu.Lock()
  593. c.loginGoal = &LoginGoal{
  594. wantLoggedIn: true,
  595. token: t,
  596. flags: flags,
  597. }
  598. c.mu.Unlock()
  599. c.cancelAuth()
  600. }
  601. func (c *Auto) StartLogout() {
  602. c.logf("client.StartLogout()")
  603. c.mu.Lock()
  604. c.loginGoal = &LoginGoal{
  605. wantLoggedIn: false,
  606. }
  607. c.mu.Unlock()
  608. c.cancelAuth()
  609. }
  610. func (c *Auto) Logout(ctx context.Context) error {
  611. c.logf("client.Logout()")
  612. errc := make(chan error, 1)
  613. c.mu.Lock()
  614. c.loginGoal = &LoginGoal{
  615. wantLoggedIn: false,
  616. loggedOutResult: errc,
  617. }
  618. c.mu.Unlock()
  619. c.cancelAuth()
  620. timer := time.NewTimer(10 * time.Second)
  621. defer timer.Stop()
  622. select {
  623. case err := <-errc:
  624. return err
  625. case <-ctx.Done():
  626. return ctx.Err()
  627. case <-timer.C:
  628. return context.DeadlineExceeded
  629. }
  630. }
  631. func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
  632. return c.direct.SetExpirySooner(ctx, expiry)
  633. }
  634. // UpdateEndpoints sets the client's discovered endpoints and sends
  635. // them to the control server if they've changed.
  636. //
  637. // It does not retain the provided slice.
  638. func (c *Auto) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
  639. changed := c.direct.SetEndpoints(endpoints)
  640. if changed {
  641. c.sendNewMapRequest()
  642. }
  643. }
  644. func (c *Auto) Shutdown() {
  645. c.logf("client.Shutdown()")
  646. c.mu.Lock()
  647. inSendStatus := c.inSendStatus
  648. closed := c.closed
  649. direct := c.direct
  650. if !closed {
  651. c.closed = true
  652. }
  653. c.mu.Unlock()
  654. c.logf("client.Shutdown: inSendStatus=%v", inSendStatus)
  655. if !closed {
  656. c.unregisterHealthWatch()
  657. close(c.quit)
  658. c.cancelAuth()
  659. <-c.authDone
  660. c.cancelMapUnsafely()
  661. <-c.mapDone
  662. if direct != nil {
  663. direct.Close()
  664. }
  665. c.logf("Client.Shutdown done.")
  666. }
  667. }
  668. // NodePublicKey returns the node public key currently in use. This is
  669. // used exclusively in tests.
  670. func (c *Auto) TestOnlyNodePublicKey() key.NodePublic {
  671. priv := c.direct.GetPersist()
  672. return priv.PrivateNodeKey().Public()
  673. }
  674. func (c *Auto) TestOnlySetAuthKey(authkey string) {
  675. c.direct.mu.Lock()
  676. defer c.direct.mu.Unlock()
  677. c.direct.authKey = authkey
  678. }
  679. func (c *Auto) TestOnlyTimeNow() time.Time {
  680. return c.timeNow()
  681. }
  682. // SetDNS sends the SetDNSRequest request to the control plane server,
  683. // requesting a DNS record be created or updated.
  684. func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
  685. return c.direct.SetDNS(ctx, req)
  686. }
  687. func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
  688. return c.direct.DoNoiseRequest(req)
  689. }
  690. // GetSingleUseNoiseRoundTripper returns a RoundTripper that can be only be used
  691. // once (and must be used once) to make a single HTTP request over the noise
  692. // channel to the coordination server.
  693. //
  694. // In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
  695. // payload, if any.
  696. func (c *Auto) GetSingleUseNoiseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
  697. return c.direct.GetSingleUseNoiseRoundTripper(ctx)
  698. }