auto.go 18 KB

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