auto.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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. "fmt"
  8. "sync"
  9. "time"
  10. "tailscale.com/health"
  11. "tailscale.com/logtail/backoff"
  12. "tailscale.com/tailcfg"
  13. "tailscale.com/types/empty"
  14. "tailscale.com/types/logger"
  15. "tailscale.com/types/netmap"
  16. "tailscale.com/types/persist"
  17. "tailscale.com/types/structs"
  18. "tailscale.com/types/wgkey"
  19. )
  20. type LoginGoal struct {
  21. _ structs.Incomparable
  22. wantLoggedIn bool // true if we *want* to be logged in
  23. token *tailcfg.Oauth2Token // oauth token to use when logging in
  24. flags LoginFlags // flags to use when logging in
  25. url string // auth url that needs to be visited
  26. loggedOutResult chan<- error
  27. }
  28. func (g *LoginGoal) sendLogoutError(err error) {
  29. if g.loggedOutResult == nil {
  30. return
  31. }
  32. select {
  33. case g.loggedOutResult <- err:
  34. default:
  35. }
  36. }
  37. // Auto connects to a tailcontrol server for a node.
  38. // It's a concrete implementation of the Client interface.
  39. type Auto struct {
  40. direct *Direct // our interface to the server APIs
  41. timeNow func() time.Time
  42. logf logger.Logf
  43. expiry *time.Time
  44. closed bool
  45. newMapCh chan struct{} // readable when we must restart a map request
  46. unregisterHealthWatch func()
  47. mu sync.Mutex // mutex guards the following fields
  48. statusFunc func(Status) // called to update Client status
  49. paused bool // whether we should stop making HTTP requests
  50. unpauseWaiters []chan struct{}
  51. loggedIn bool // true if currently logged in
  52. loginGoal *LoginGoal // non-nil if some login activity is desired
  53. synced bool // true if our netmap is up-to-date
  54. hostinfo *tailcfg.Hostinfo
  55. inPollNetMap bool // true if currently running a PollNetMap
  56. inLiteMapUpdate bool // true if a lite (non-streaming) map request is outstanding
  57. inSendStatus int // number of sendStatus calls currently in progress
  58. state State
  59. authCtx context.Context // context used for auth requests
  60. mapCtx context.Context // context used for netmap requests
  61. authCancel func() // cancel the auth context
  62. mapCancel func() // cancel the netmap context
  63. quit chan struct{} // when closed, goroutines should all exit
  64. authDone chan struct{} // when closed, auth goroutine is done
  65. mapDone chan struct{} // when closed, map goroutine is done
  66. }
  67. // New creates and starts a new Auto.
  68. func New(opts Options) (*Auto, error) {
  69. c, err := NewNoStart(opts)
  70. if c != nil {
  71. c.Start()
  72. }
  73. return c, err
  74. }
  75. // NewNoStart creates a new Auto, but without calling Start on it.
  76. func NewNoStart(opts Options) (*Auto, error) {
  77. direct, err := NewDirect(opts)
  78. if err != nil {
  79. return nil, err
  80. }
  81. if opts.Logf == nil {
  82. opts.Logf = func(fmt string, args ...interface{}) {}
  83. }
  84. if opts.TimeNow == nil {
  85. opts.TimeNow = time.Now
  86. }
  87. c := &Auto{
  88. direct: direct,
  89. timeNow: opts.TimeNow,
  90. logf: opts.Logf,
  91. newMapCh: make(chan struct{}, 1),
  92. quit: make(chan struct{}),
  93. authDone: make(chan struct{}),
  94. mapDone: make(chan struct{}),
  95. }
  96. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  97. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  98. c.unregisterHealthWatch = health.RegisterWatcher(c.onHealthChange)
  99. return c, nil
  100. }
  101. func (c *Auto) onHealthChange(sys health.Subsystem, err error) {
  102. if sys == health.SysOverall {
  103. return
  104. }
  105. c.logf("controlclient: restarting map request for %q health change to new state: %v", sys, err)
  106. c.cancelMapSafely()
  107. }
  108. // SetPaused controls whether HTTP activity should be paused.
  109. //
  110. // The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
  111. func (c *Auto) SetPaused(paused bool) {
  112. c.mu.Lock()
  113. defer c.mu.Unlock()
  114. if paused == c.paused {
  115. return
  116. }
  117. c.logf("setPaused(%v)", paused)
  118. c.paused = paused
  119. if paused {
  120. // Only cancel the map routine. (The auth routine isn't expensive
  121. // so it's fine to keep it running.)
  122. c.cancelMapLocked()
  123. } else {
  124. for _, ch := range c.unpauseWaiters {
  125. close(ch)
  126. }
  127. c.unpauseWaiters = nil
  128. }
  129. }
  130. // Start starts the client's goroutines.
  131. //
  132. // It should only be called for clients created by NewNoStart.
  133. func (c *Auto) Start() {
  134. go c.authRoutine()
  135. go c.mapRoutine()
  136. }
  137. // sendNewMapRequest either sends a new OmitPeers, non-streaming map request
  138. // (to just send Hostinfo/Netinfo/Endpoints info, while keeping an existing
  139. // streaming response open), or start a new streaming one if necessary.
  140. //
  141. // It should be called whenever there's something new to tell the server.
  142. func (c *Auto) sendNewMapRequest() {
  143. c.mu.Lock()
  144. // If we're not already streaming a netmap, or if we're already stuck
  145. // in a lite update, then tear down everything and start a new stream
  146. // (which starts by sending a new map request)
  147. if !c.inPollNetMap || c.inLiteMapUpdate || !c.loggedIn {
  148. c.mu.Unlock()
  149. c.cancelMapSafely()
  150. return
  151. }
  152. // Otherwise, send a lite update that doesn't keep a
  153. // long-running stream response.
  154. defer c.mu.Unlock()
  155. c.inLiteMapUpdate = true
  156. ctx, cancel := context.WithTimeout(c.mapCtx, 10*time.Second)
  157. go func() {
  158. defer cancel()
  159. t0 := time.Now()
  160. err := c.direct.SendLiteMapUpdate(ctx)
  161. d := time.Since(t0).Round(time.Millisecond)
  162. c.mu.Lock()
  163. c.inLiteMapUpdate = false
  164. c.mu.Unlock()
  165. if err == nil {
  166. c.logf("[v1] successful lite map update in %v", d)
  167. return
  168. }
  169. if ctx.Err() == nil {
  170. c.logf("lite map update after %v: %v", d, err)
  171. }
  172. // Fall back to restarting the long-polling map
  173. // request (the old heavy way) if the lite update
  174. // failed for any reason.
  175. c.cancelMapSafely()
  176. }()
  177. }
  178. func (c *Auto) cancelAuth() {
  179. c.mu.Lock()
  180. if c.authCancel != nil {
  181. c.authCancel()
  182. }
  183. if !c.closed {
  184. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  185. }
  186. c.mu.Unlock()
  187. }
  188. func (c *Auto) cancelMapLocked() {
  189. if c.mapCancel != nil {
  190. c.mapCancel()
  191. }
  192. if !c.closed {
  193. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  194. }
  195. }
  196. func (c *Auto) cancelMapUnsafely() {
  197. c.mu.Lock()
  198. c.cancelMapLocked()
  199. c.mu.Unlock()
  200. }
  201. func (c *Auto) cancelMapSafely() {
  202. c.mu.Lock()
  203. defer c.mu.Unlock()
  204. c.logf("[v1] cancelMapSafely: synced=%v", c.synced)
  205. if c.inPollNetMap {
  206. // received at least one netmap since the last
  207. // interruption. That means the server has already
  208. // fully processed our last request, which might
  209. // include UpdateEndpoints(). Interrupt it and try
  210. // again.
  211. c.cancelMapLocked()
  212. } else {
  213. // !synced means we either haven't done a netmap
  214. // request yet, or it hasn't answered yet. So the
  215. // server is in an undefined state. If we send
  216. // another netmap request too soon, it might race
  217. // with the last one, and if we're very unlucky,
  218. // the new request will be applied before the old one,
  219. // and the wrong endpoints will get registered. We
  220. // have to tell the client to abort politely, only
  221. // after it receives a response to its existing netmap
  222. // request.
  223. select {
  224. case c.newMapCh <- struct{}{}:
  225. c.logf("[v1] cancelMapSafely: wrote to channel")
  226. default:
  227. // if channel write failed, then there was already
  228. // an outstanding newMapCh request. One is enough,
  229. // since it'll always use the latest endpoints.
  230. c.logf("[v1] cancelMapSafely: channel was full")
  231. }
  232. }
  233. }
  234. func (c *Auto) authRoutine() {
  235. defer close(c.authDone)
  236. bo := backoff.NewBackoff("authRoutine", c.logf, 30*time.Second)
  237. for {
  238. c.mu.Lock()
  239. goal := c.loginGoal
  240. ctx := c.authCtx
  241. if goal != nil {
  242. c.logf("authRoutine: %s; wantLoggedIn=%v", c.state, goal.wantLoggedIn)
  243. } else {
  244. c.logf("authRoutine: %s; goal=nil paused=%v", c.state, c.paused)
  245. }
  246. c.mu.Unlock()
  247. select {
  248. case <-c.quit:
  249. c.logf("[v1] authRoutine: quit")
  250. return
  251. default:
  252. }
  253. report := func(err error, msg string) {
  254. c.logf("[v1] %s: %v", msg, err)
  255. err = fmt.Errorf("%s: %v", msg, err)
  256. // don't send status updates for context errors,
  257. // since context cancelation is always on purpose.
  258. if ctx.Err() == nil {
  259. c.sendStatus("authRoutine-report", err, "", nil)
  260. }
  261. }
  262. if goal == nil {
  263. // Wait for user to Login or Logout.
  264. <-ctx.Done()
  265. c.logf("[v1] authRoutine: context done.")
  266. continue
  267. }
  268. if !goal.wantLoggedIn {
  269. err := c.direct.TryLogout(ctx)
  270. goal.sendLogoutError(err)
  271. if err != nil {
  272. report(err, "TryLogout")
  273. bo.BackOff(ctx, err)
  274. continue
  275. }
  276. // success
  277. c.mu.Lock()
  278. c.loggedIn = false
  279. c.loginGoal = nil
  280. c.state = StateNotAuthenticated
  281. c.synced = false
  282. c.mu.Unlock()
  283. c.sendStatus("authRoutine-wantout", nil, "", nil)
  284. bo.BackOff(ctx, nil)
  285. } else { // ie. goal.wantLoggedIn
  286. c.mu.Lock()
  287. if goal.url != "" {
  288. c.state = StateURLVisitRequired
  289. } else {
  290. c.state = StateAuthenticating
  291. }
  292. c.mu.Unlock()
  293. var url string
  294. var err error
  295. var f string
  296. if goal.url != "" {
  297. url, err = c.direct.WaitLoginURL(ctx, goal.url)
  298. f = "WaitLoginURL"
  299. } else {
  300. url, err = c.direct.TryLogin(ctx, goal.token, goal.flags)
  301. f = "TryLogin"
  302. }
  303. if err != nil {
  304. report(err, f)
  305. bo.BackOff(ctx, err)
  306. continue
  307. }
  308. if url != "" {
  309. if goal.url != "" {
  310. err = fmt.Errorf("[unexpected] server required a new URL?")
  311. report(err, "WaitLoginURL")
  312. }
  313. c.mu.Lock()
  314. c.loginGoal = &LoginGoal{
  315. wantLoggedIn: true,
  316. flags: LoginDefault,
  317. url: url,
  318. }
  319. c.state = StateURLVisitRequired
  320. c.synced = false
  321. c.mu.Unlock()
  322. c.sendStatus("authRoutine-url", err, url, nil)
  323. bo.BackOff(ctx, err)
  324. continue
  325. }
  326. // success
  327. c.mu.Lock()
  328. c.loggedIn = true
  329. c.loginGoal = nil
  330. c.state = StateAuthenticated
  331. c.mu.Unlock()
  332. c.sendStatus("authRoutine-success", nil, "", nil)
  333. c.cancelMapSafely()
  334. bo.BackOff(ctx, nil)
  335. }
  336. }
  337. }
  338. // Expiry returns the credential expiration time, or the zero time if
  339. // the expiration time isn't known. Used in tests only.
  340. func (c *Auto) Expiry() *time.Time {
  341. c.mu.Lock()
  342. defer c.mu.Unlock()
  343. return c.expiry
  344. }
  345. // Direct returns the underlying direct client object. Used in tests
  346. // only.
  347. func (c *Auto) Direct() *Direct {
  348. return c.direct
  349. }
  350. // unpausedChanLocked returns a new channel that is closed when the
  351. // current Auto pause is unpaused.
  352. //
  353. // c.mu must be held
  354. func (c *Auto) unpausedChanLocked() <-chan struct{} {
  355. unpaused := make(chan struct{})
  356. c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
  357. return unpaused
  358. }
  359. func (c *Auto) mapRoutine() {
  360. defer close(c.mapDone)
  361. bo := backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second)
  362. for {
  363. c.mu.Lock()
  364. if c.paused {
  365. unpaused := c.unpausedChanLocked()
  366. c.mu.Unlock()
  367. c.logf("mapRoutine: awaiting unpause")
  368. select {
  369. case <-unpaused:
  370. c.logf("mapRoutine: unpaused")
  371. case <-c.quit:
  372. c.logf("mapRoutine: quit")
  373. return
  374. }
  375. continue
  376. }
  377. c.logf("mapRoutine: %s", c.state)
  378. loggedIn := c.loggedIn
  379. ctx := c.mapCtx
  380. c.mu.Unlock()
  381. select {
  382. case <-c.quit:
  383. c.logf("mapRoutine: quit")
  384. return
  385. default:
  386. }
  387. report := func(err error, msg string) {
  388. c.logf("[v1] %s: %v", msg, err)
  389. err = fmt.Errorf("%s: %v", msg, err)
  390. // don't send status updates for context errors,
  391. // since context cancelation is always on purpose.
  392. if ctx.Err() == nil {
  393. c.sendStatus("mapRoutine1", err, "", nil)
  394. }
  395. }
  396. if !loggedIn {
  397. // Wait for something interesting to happen
  398. c.mu.Lock()
  399. c.synced = false
  400. // c.state is set by authRoutine()
  401. c.mu.Unlock()
  402. select {
  403. case <-ctx.Done():
  404. c.logf("mapRoutine: context done.")
  405. case <-c.newMapCh:
  406. c.logf("mapRoutine: new map needed while idle.")
  407. }
  408. } else {
  409. // Be sure this is false when we're not inside
  410. // PollNetMap, so that cancelMapSafely() can notify
  411. // us correctly.
  412. c.mu.Lock()
  413. c.inPollNetMap = false
  414. c.mu.Unlock()
  415. health.SetInPollNetMap(false)
  416. err := c.direct.PollNetMap(ctx, -1, func(nm *netmap.NetworkMap) {
  417. health.SetInPollNetMap(true)
  418. c.mu.Lock()
  419. select {
  420. case <-c.newMapCh:
  421. c.logf("[v1] mapRoutine: new map request during PollNetMap. canceling.")
  422. c.cancelMapLocked()
  423. // Don't emit this netmap; we're
  424. // about to request a fresh one.
  425. c.mu.Unlock()
  426. return
  427. default:
  428. }
  429. c.synced = true
  430. c.inPollNetMap = true
  431. if c.loggedIn {
  432. c.state = StateSynchronized
  433. }
  434. exp := nm.Expiry
  435. c.expiry = &exp
  436. stillAuthed := c.loggedIn
  437. state := c.state
  438. c.mu.Unlock()
  439. c.logf("[v1] mapRoutine: netmap received: %s", state)
  440. if stillAuthed {
  441. c.sendStatus("mapRoutine-got-netmap", nil, "", nm)
  442. }
  443. })
  444. health.SetInPollNetMap(false)
  445. c.mu.Lock()
  446. c.synced = false
  447. c.inPollNetMap = false
  448. if c.state == StateSynchronized {
  449. c.state = StateAuthenticated
  450. }
  451. paused := c.paused
  452. c.mu.Unlock()
  453. if paused {
  454. c.logf("mapRoutine: paused")
  455. continue
  456. }
  457. if err != nil {
  458. report(err, "PollNetMap")
  459. bo.BackOff(ctx, err)
  460. continue
  461. }
  462. bo.BackOff(ctx, nil)
  463. }
  464. }
  465. }
  466. func (c *Auto) AuthCantContinue() bool {
  467. if c == nil {
  468. return true
  469. }
  470. c.mu.Lock()
  471. defer c.mu.Unlock()
  472. return !c.loggedIn && (c.loginGoal == nil || c.loginGoal.url != "")
  473. }
  474. // SetStatusFunc sets fn as the callback to run on any status change.
  475. func (c *Auto) SetStatusFunc(fn func(Status)) {
  476. c.mu.Lock()
  477. c.statusFunc = fn
  478. c.mu.Unlock()
  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. c.logf("NetInfo: %v", ni)
  499. // Send new Hostinfo (which includes NetInfo) to server
  500. c.sendNewMapRequest()
  501. }
  502. func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
  503. c.mu.Lock()
  504. state := c.state
  505. loggedIn := c.loggedIn
  506. synced := c.synced
  507. statusFunc := c.statusFunc
  508. hi := c.hostinfo
  509. c.inSendStatus++
  510. c.mu.Unlock()
  511. c.logf("[v1] sendStatus: %s: %v", who, state)
  512. var p *persist.Persist
  513. var loginFin, logoutFin *empty.Message
  514. if state == StateAuthenticated {
  515. loginFin = new(empty.Message)
  516. }
  517. if state == StateNotAuthenticated {
  518. logoutFin = new(empty.Message)
  519. }
  520. if nm != nil && loggedIn && synced {
  521. pp := c.direct.GetPersist()
  522. p = &pp
  523. } else {
  524. // don't send netmap status, as it's misleading when we're
  525. // not logged in.
  526. nm = nil
  527. }
  528. new := Status{
  529. LoginFinished: loginFin,
  530. LogoutFinished: logoutFin,
  531. URL: url,
  532. Persist: p,
  533. NetMap: nm,
  534. Hostinfo: hi,
  535. State: state,
  536. }
  537. if err != nil {
  538. new.Err = err.Error()
  539. }
  540. if statusFunc != nil {
  541. statusFunc(new)
  542. }
  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. // UpdateEndpoints sets the client's discovered endpoints and sends
  589. // them to the control server if they've changed.
  590. //
  591. // It does not retain the provided slice.
  592. //
  593. // The localPort field is unused except for integration tests in
  594. // another repo.
  595. func (c *Auto) UpdateEndpoints(localPort uint16, endpoints []tailcfg.Endpoint) {
  596. changed := c.direct.SetEndpoints(localPort, 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. if !closed {
  607. c.closed = true
  608. c.statusFunc = nil
  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. c.logf("Client.Shutdown done.")
  620. }
  621. }
  622. // NodePublicKey returns the node public key currently in use. This is
  623. // used exclusively in tests.
  624. func (c *Auto) TestOnlyNodePublicKey() wgkey.Key {
  625. priv := c.direct.GetPersist()
  626. return priv.PrivateNodeKey.Public()
  627. }
  628. func (c *Auto) TestOnlySetAuthKey(authkey string) {
  629. c.direct.mu.Lock()
  630. defer c.direct.mu.Unlock()
  631. c.direct.authKey = authkey
  632. }
  633. func (c *Auto) TestOnlyTimeNow() time.Time {
  634. return c.timeNow()
  635. }
  636. // SetDNS sends the SetDNSRequest request to the control plane server,
  637. // requesting a DNS record be created or updated.
  638. func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
  639. return c.direct.SetDNS(ctx, req)
  640. }