dns_server_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. package nebula
  2. import (
  3. "context"
  4. "log/slog"
  5. "net"
  6. "net/netip"
  7. "strconv"
  8. "testing"
  9. "time"
  10. "github.com/gaissmai/bart"
  11. "github.com/miekg/dns"
  12. "github.com/slackhq/nebula/cert"
  13. "github.com/slackhq/nebula/cert_test"
  14. "github.com/slackhq/nebula/config"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. type stubDNSWriter struct{}
  19. func (stubDNSWriter) LocalAddr() net.Addr { return &net.UDPAddr{} }
  20. func (stubDNSWriter) RemoteAddr() net.Addr {
  21. return &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5353}
  22. }
  23. func (stubDNSWriter) Write([]byte) (int, error) { return 0, nil }
  24. func (stubDNSWriter) WriteMsg(*dns.Msg) error { return nil }
  25. func (stubDNSWriter) Close() error { return nil }
  26. func (stubDNSWriter) TsigStatus() error { return nil }
  27. func (stubDNSWriter) TsigTimersOnly(bool) {}
  28. func (stubDNSWriter) Hijack() {}
  29. func TestParsequery(t *testing.T) {
  30. l := slog.New(slog.DiscardHandler)
  31. hostMap := &HostMap{}
  32. ds := &dnsServer{
  33. l: l,
  34. dnsMap4: make(map[string]netip.Addr),
  35. dnsMap6: make(map[string]netip.Addr),
  36. hostMap: hostMap,
  37. }
  38. ds.enabled.Store(true)
  39. addrs := []netip.Addr{
  40. netip.MustParseAddr("1.2.3.4"),
  41. netip.MustParseAddr("1.2.3.5"),
  42. netip.MustParseAddr("fd01::24"),
  43. netip.MustParseAddr("fd01::25"),
  44. }
  45. ds.Add("test.com.com", addrs)
  46. ds.Add("v4only.com.com", []netip.Addr{netip.MustParseAddr("1.2.3.6")})
  47. ds.Add("v6only.com.com", []netip.Addr{netip.MustParseAddr("fd01::26")})
  48. m := &dns.Msg{}
  49. m.SetQuestion("test.com.com", dns.TypeA)
  50. ds.parseQuery(m, nil)
  51. assert.NotNil(t, m.Answer)
  52. assert.Equal(t, "1.2.3.4", m.Answer[0].(*dns.A).A.String())
  53. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  54. m = &dns.Msg{}
  55. m.SetQuestion("test.com.com", dns.TypeAAAA)
  56. ds.parseQuery(m, nil)
  57. assert.NotNil(t, m.Answer)
  58. assert.Equal(t, "fd01::24", m.Answer[0].(*dns.AAAA).AAAA.String())
  59. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  60. // A known name with no record of the requested type should return NODATA
  61. // (NOERROR with empty answer), not NXDOMAIN.
  62. m = &dns.Msg{}
  63. m.SetQuestion("v4only.com.com", dns.TypeAAAA)
  64. ds.parseQuery(m, nil)
  65. assert.Empty(t, m.Answer)
  66. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  67. m = &dns.Msg{}
  68. m.SetQuestion("v6only.com.com", dns.TypeA)
  69. ds.parseQuery(m, nil)
  70. assert.Empty(t, m.Answer)
  71. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  72. // An unknown name should still return NXDOMAIN.
  73. m = &dns.Msg{}
  74. m.SetQuestion("unknown.com.com", dns.TypeA)
  75. ds.parseQuery(m, nil)
  76. assert.Empty(t, m.Answer)
  77. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  78. // short lookups should not fail
  79. m = &dns.Msg{}
  80. m.Question = []dns.Question{{Name: "", Qtype: dns.TypeTXT, Qclass: dns.ClassINET}}
  81. ds.parseQuery(m, stubDNSWriter{})
  82. assert.Empty(t, m.Answer)
  83. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  84. m = &dns.Msg{}
  85. m.Question = []dns.Question{{Name: ".", Qtype: dns.TypeTXT, Qclass: dns.ClassINET}}
  86. ds.parseQuery(m, stubDNSWriter{})
  87. assert.Empty(t, m.Answer)
  88. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  89. }
  90. func Test_getDnsServerAddr(t *testing.T) {
  91. c := config.NewC(nil)
  92. c.Settings["lighthouse"] = map[string]any{
  93. "dns": map[string]any{
  94. "host": "0.0.0.0",
  95. "port": "1",
  96. },
  97. }
  98. assert.Equal(t, "0.0.0.0:1", getDnsServerAddr(c))
  99. c.Settings["lighthouse"] = map[string]any{
  100. "dns": map[string]any{
  101. "host": "::",
  102. "port": "1",
  103. },
  104. }
  105. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  106. c.Settings["lighthouse"] = map[string]any{
  107. "dns": map[string]any{
  108. "host": "[::]",
  109. "port": "1",
  110. },
  111. }
  112. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  113. // Make sure whitespace doesn't mess us up
  114. c.Settings["lighthouse"] = map[string]any{
  115. "dns": map[string]any{
  116. "host": "[::] ",
  117. "port": "1",
  118. },
  119. }
  120. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  121. }
  122. func newTestDnsServer(t *testing.T) (*dnsServer, *config.C) {
  123. t.Helper()
  124. sl := slog.New(slog.DiscardHandler)
  125. ds := &dnsServer{
  126. l: sl,
  127. ctx: context.Background(),
  128. dnsMap4: make(map[string]netip.Addr),
  129. dnsMap6: make(map[string]netip.Addr),
  130. hostMap: &HostMap{},
  131. }
  132. ds.mux = dns.NewServeMux()
  133. ds.mux.HandleFunc(".", ds.handleDnsRequest)
  134. return ds, config.NewC(nil)
  135. }
  136. func setDnsConfig(c *config.C, host string, port string, amLighthouse, serveDns bool) {
  137. c.Settings["lighthouse"] = map[string]any{
  138. "am_lighthouse": amLighthouse,
  139. "serve_dns": serveDns,
  140. "dns": map[string]any{
  141. "host": host,
  142. "port": port,
  143. },
  144. }
  145. }
  146. func TestDnsServer_reload_initial_disabled(t *testing.T) {
  147. ds, c := newTestDnsServer(t)
  148. setDnsConfig(c, "127.0.0.1", "0", true, false)
  149. require.NoError(t, ds.reload(c, true))
  150. assert.False(t, ds.enabled.Load())
  151. assert.Equal(t, "127.0.0.1:0", ds.addr)
  152. assert.Nil(t, ds.server)
  153. }
  154. func TestDnsServer_reload_initial_enabled(t *testing.T) {
  155. ds, c := newTestDnsServer(t)
  156. setDnsConfig(c, "127.0.0.1", "0", true, true)
  157. require.NoError(t, ds.reload(c, true))
  158. assert.True(t, ds.enabled.Load())
  159. assert.Equal(t, "127.0.0.1:0", ds.addr)
  160. // initial never starts a runner; that's Control.Start's job
  161. assert.Nil(t, ds.server)
  162. }
  163. func TestDnsServer_reload_initial_serveDnsWithoutLighthouse(t *testing.T) {
  164. ds, c := newTestDnsServer(t)
  165. setDnsConfig(c, "127.0.0.1", "0", false, true)
  166. require.NoError(t, ds.reload(c, true))
  167. // Wants DNS but isn't a lighthouse: gated off, no runner.
  168. assert.False(t, ds.enabled.Load())
  169. }
  170. func TestDnsServer_reload_sameAddr_noOp(t *testing.T) {
  171. port := freeUDPPort(t)
  172. ds, c := newTestDnsServer(t)
  173. setDnsConfig(c, "127.0.0.1", port, true, true)
  174. require.NoError(t, ds.reload(c, true))
  175. go ds.Start()
  176. waitForBind(t, ds)
  177. ds.serverMu.Lock()
  178. before := ds.server
  179. ds.serverMu.Unlock()
  180. require.NotNil(t, before)
  181. // Same address, so the running listener must be left alone rather than rebuilt under live queries
  182. require.NoError(t, ds.reload(c, false))
  183. assert.True(t, ds.enabled.Load())
  184. ds.serverMu.Lock()
  185. after := ds.server
  186. ds.serverMu.Unlock()
  187. assert.Same(t, before, after, "a same-address reload must not restart the listener")
  188. ds.Stop()
  189. }
  190. // The branch the old sameAddr test was accidentally hitting: enabled with nothing running means reload starts it.
  191. func TestDnsServer_reload_whenNotRunning_starts(t *testing.T) {
  192. port := freeUDPPort(t)
  193. ds, c := newTestDnsServer(t)
  194. setDnsConfig(c, "127.0.0.1", port, true, true)
  195. // initial only records config, it never starts anything
  196. require.NoError(t, ds.reload(c, true))
  197. ds.serverMu.Lock()
  198. assert.Nil(t, ds.server, "the initial reload must not start a listener")
  199. ds.serverMu.Unlock()
  200. require.NoError(t, ds.reload(c, false))
  201. waitForBind(t, ds)
  202. ds.serverMu.Lock()
  203. assert.NotNil(t, ds.server, "a reload with nothing running should bring DNS up")
  204. ds.serverMu.Unlock()
  205. ds.Stop()
  206. }
  207. func TestDnsServer_StartStop_lifecycle(t *testing.T) {
  208. // Bind to a real (random) UDP port so we exercise the actual
  209. // ListenAndServe + Shutdown plumbing including the started-chan race fix.
  210. port := freeUDPPort(t)
  211. ds, c := newTestDnsServer(t)
  212. setDnsConfig(c, "127.0.0.1", port, true, true)
  213. require.NoError(t, ds.reload(c, true))
  214. done := make(chan struct{})
  215. go func() {
  216. ds.Start()
  217. close(done)
  218. }()
  219. waitFor(t, func() bool {
  220. ds.serverMu.Lock()
  221. started := ds.started
  222. ds.serverMu.Unlock()
  223. if started == nil {
  224. return false
  225. }
  226. select {
  227. case <-started:
  228. return true
  229. default:
  230. return false
  231. }
  232. })
  233. ds.Stop()
  234. select {
  235. case <-done:
  236. case <-time.After(5 * time.Second):
  237. t.Fatal("Start did not return after Stop")
  238. }
  239. }
  240. func TestDnsServer_Stop_beforeBind_doesNotHang(t *testing.T) {
  241. // Stop called immediately after Start should not deadlock even if bind
  242. // hasn't completed yet. This exercises the started-chan close-on-bind-fail
  243. // path: by binding to an obviously bad port (privileged) we get a fast
  244. // bind error before NotifyStartedFunc fires.
  245. ds, c := newTestDnsServer(t)
  246. // Use a port that should fail to bind (negative would be invalid, use a
  247. // host that won't resolve to ensure listenUDP fails quickly).
  248. setDnsConfig(c, "256.256.256.256", "53", true, true)
  249. require.NoError(t, ds.reload(c, true))
  250. done := make(chan struct{})
  251. go func() {
  252. ds.Start()
  253. close(done)
  254. }()
  255. // Give Start a moment to attempt the bind and fail.
  256. select {
  257. case <-done:
  258. // Bind failed and Start returned; Stop should be a no-op.
  259. case <-time.After(time.Second):
  260. t.Fatal("Start did not return after a bad bind")
  261. }
  262. stopped := make(chan struct{})
  263. go func() {
  264. ds.Stop()
  265. close(stopped)
  266. }()
  267. select {
  268. case <-stopped:
  269. case <-time.After(time.Second):
  270. t.Fatal("Stop hung after a failed bind")
  271. }
  272. }
  273. // newTestPKI builds a minimal *PKI with a single v1 cert whose name and
  274. // VPN addresses are caller-provided, suitable for exercising seedSelf and
  275. // QueryCert self handling.
  276. func newTestPKI(t *testing.T, name string, addrs []netip.Addr) *PKI {
  277. t.Helper()
  278. networks := make([]netip.Prefix, 0, len(addrs))
  279. for _, a := range addrs {
  280. bits := 32
  281. if a.Is6() {
  282. bits = 128
  283. }
  284. networks = append(networks, netip.PrefixFrom(a, bits))
  285. }
  286. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil)
  287. c, _, _, _ := cert_test.NewTestCert(cert.Version2, cert.Curve_CURVE25519, ca, caKey, name, time.Time{}, time.Time{}, networks, nil, nil)
  288. addrsTable := new(bart.Lite)
  289. for _, a := range addrs {
  290. addrsTable.Insert(netip.PrefixFrom(a, a.BitLen()))
  291. }
  292. cs := &CertState{
  293. v2Cert: c,
  294. initiatingVersion: cert.Version2,
  295. myVpnAddrs: addrs,
  296. myVpnAddrsTable: addrsTable,
  297. }
  298. pki := &PKI{}
  299. pki.cs.Store(cs)
  300. return pki
  301. }
  302. func TestDnsServer_seedSelf_addsOwnRecord(t *testing.T) {
  303. ds, c := newTestDnsServer(t)
  304. myV4 := netip.MustParseAddr("10.0.0.1")
  305. myV6 := netip.MustParseAddr("fd00::1")
  306. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{myV4, myV6})
  307. setDnsConfig(c, "127.0.0.1", "0", true, true)
  308. require.NoError(t, ds.reload(c, true))
  309. ds.seedSelf()
  310. got4, exists := ds.Query(dns.TypeA, "lighthouse.")
  311. assert.True(t, exists)
  312. assert.Equal(t, myV4, got4)
  313. got6, exists := ds.Query(dns.TypeAAAA, "lighthouse.")
  314. assert.True(t, exists)
  315. assert.Equal(t, myV6, got6)
  316. }
  317. func TestDnsServer_seedSelf_disabled_noOp(t *testing.T) {
  318. ds, c := newTestDnsServer(t)
  319. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{netip.MustParseAddr("10.0.0.1")})
  320. setDnsConfig(c, "127.0.0.1", "0", true, false)
  321. require.NoError(t, ds.reload(c, true))
  322. ds.seedSelf()
  323. _, exists := ds.Query(dns.TypeA, "lighthouse.")
  324. assert.False(t, exists)
  325. }
  326. func TestDnsServer_clearRecords_dropsSelfHost(t *testing.T) {
  327. ds, c := newTestDnsServer(t)
  328. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{netip.MustParseAddr("10.0.0.1")})
  329. setDnsConfig(c, "127.0.0.1", "0", true, true)
  330. require.NoError(t, ds.reload(c, true))
  331. ds.seedSelf()
  332. require.NotEmpty(t, ds.selfHost)
  333. ds.clearRecords()
  334. assert.Empty(t, ds.selfHost)
  335. _, exists := ds.Query(dns.TypeA, "lighthouse.")
  336. assert.False(t, exists)
  337. }
  338. func TestDnsServer_QueryCert_returnsOwnCert(t *testing.T) {
  339. ds, _ := newTestDnsServer(t)
  340. myV4 := netip.MustParseAddr("10.0.0.1")
  341. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{myV4})
  342. got := ds.QueryCert(myV4.String() + ".")
  343. assert.NotEmpty(t, got, "TXT lookup of our own VPN address should return our cert")
  344. other := netip.MustParseAddr("10.0.0.99")
  345. assert.Empty(t, ds.QueryCert(other.String()+"."), "unknown peer IP should return nothing")
  346. }
  347. func TestDnsServer_reload_disable_stopsRunningServer(t *testing.T) {
  348. port := freeUDPPort(t)
  349. ds, c := newTestDnsServer(t)
  350. setDnsConfig(c, "127.0.0.1", port, true, true)
  351. require.NoError(t, ds.reload(c, true))
  352. startReturned := make(chan struct{})
  353. go func() {
  354. ds.Start()
  355. close(startReturned)
  356. }()
  357. waitForBind(t, ds)
  358. // Toggle serve_dns off; reload should shut the running server down.
  359. setDnsConfig(c, "127.0.0.1", port, true, false)
  360. require.NoError(t, ds.reload(c, false))
  361. select {
  362. case <-startReturned:
  363. case <-time.After(5 * time.Second):
  364. t.Fatal("Start did not return after reload disabled DNS")
  365. }
  366. assert.False(t, ds.enabled.Load())
  367. }
  368. func freeUDPPort(t *testing.T) string {
  369. t.Helper()
  370. conn, err := net.ListenPacket("udp", "127.0.0.1:0")
  371. require.NoError(t, err)
  372. port := conn.LocalAddr().(*net.UDPAddr).Port
  373. require.NoError(t, conn.Close())
  374. return strconv.Itoa(port)
  375. }
  376. func waitForBind(t *testing.T, ds *dnsServer) {
  377. t.Helper()
  378. waitFor(t, func() bool {
  379. ds.serverMu.Lock()
  380. started := ds.started
  381. ds.serverMu.Unlock()
  382. if started == nil {
  383. return false
  384. }
  385. select {
  386. case <-started:
  387. return true
  388. default:
  389. return false
  390. }
  391. })
  392. }
  393. func waitFor(t *testing.T, cond func() bool) {
  394. t.Helper()
  395. deadline := time.Now().Add(5 * time.Second)
  396. for time.Now().Before(deadline) {
  397. if cond() {
  398. return
  399. }
  400. time.Sleep(5 * time.Millisecond)
  401. }
  402. t.Fatal("timed out waiting for condition")
  403. }
  404. // Two reloads in quick succession, or a HUP before Control.Start, can race two Starts at the same listener.
  405. func TestDnsServer_Start_isIdempotent(t *testing.T) {
  406. port := freeUDPPort(t)
  407. ds, c := newTestDnsServer(t)
  408. setDnsConfig(c, "127.0.0.1", port, true, true)
  409. require.NoError(t, ds.reload(c, true))
  410. go ds.Start()
  411. waitForBind(t, ds)
  412. ds.serverMu.Lock()
  413. first := ds.server
  414. ds.serverMu.Unlock()
  415. require.NotNil(t, first)
  416. // If the second Start replaces the tracked server, Stop kills the wrong one and the port leaks
  417. done := make(chan struct{})
  418. go func() {
  419. ds.Start()
  420. close(done)
  421. }()
  422. select {
  423. case <-done:
  424. case <-time.After(time.Second * 5):
  425. t.Fatal("second Start never returned")
  426. }
  427. ds.serverMu.Lock()
  428. second := ds.server
  429. ds.serverMu.Unlock()
  430. assert.Same(t, first, second, "a second Start must not replace the running server")
  431. // The real proof, after Stop the port must actually be free
  432. ds.Stop()
  433. waitFor(t, func() bool {
  434. pc, err := net.ListenPacket("udp", "127.0.0.1:"+port)
  435. if err != nil {
  436. return false
  437. }
  438. _ = pc.Close()
  439. return true
  440. })
  441. }
  442. // An address change must actually end up listening on the new port. Start's guard refuses when a server is already
  443. // installed, so reload has to clear the slot before shutting the old one down.
  444. func TestDnsServer_reload_addrChange_restarts(t *testing.T) {
  445. first := freeUDPPort(t)
  446. second := freeUDPPort(t)
  447. ds, c := newTestDnsServer(t)
  448. setDnsConfig(c, "127.0.0.1", first, true, true)
  449. require.NoError(t, ds.reload(c, true))
  450. go ds.Start()
  451. waitForBind(t, ds)
  452. // Cycle a few times, the failure this guards against depends on which goroutine wins serverMu
  453. for i := range 8 {
  454. want := second
  455. if i%2 == 1 {
  456. want = first
  457. }
  458. setDnsConfig(c, "127.0.0.1", want, true, true)
  459. require.NoError(t, ds.reload(c, false))
  460. waitForBind(t, ds)
  461. ds.serverMu.Lock()
  462. srv := ds.server
  463. ds.serverMu.Unlock()
  464. require.NotNil(t, srv, "reload left DNS down instead of restarting it")
  465. require.Equal(t, "127.0.0.1:"+want, srv.Addr, "reload should be serving the new address")
  466. }
  467. // Land back on second so the port assertions below are meaningful
  468. setDnsConfig(c, "127.0.0.1", second, true, true)
  469. require.NoError(t, ds.reload(c, false))
  470. waitForBind(t, ds)
  471. // The old port must be released and the new one actually held
  472. waitFor(t, func() bool {
  473. pc, err := net.ListenPacket("udp", "127.0.0.1:"+first)
  474. if err != nil {
  475. return false
  476. }
  477. _ = pc.Close()
  478. return true
  479. })
  480. _, err := net.ListenPacket("udp", "127.0.0.1:"+second)
  481. require.Error(t, err, "the new address should be bound by the DNS responder")
  482. ds.Stop()
  483. }
  484. // A listener that dies on its own must release the slot, or a later same-addr reload sees it as running and no-ops.
  485. func TestDnsServer_Start_bindFailure_releasesSlot(t *testing.T) {
  486. port := freeUDPPort(t)
  487. blocker, err := net.ListenPacket("udp", "127.0.0.1:"+port)
  488. require.NoError(t, err)
  489. ds, c := newTestDnsServer(t)
  490. setDnsConfig(c, "127.0.0.1", port, true, true)
  491. require.NoError(t, ds.reload(c, true))
  492. ds.Start() // returns once the bind fails
  493. ds.serverMu.Lock()
  494. assert.Nil(t, ds.server, "a listener that failed to bind must not stay parked in the slot")
  495. ds.serverMu.Unlock()
  496. // With the slot released, a reload can retry once the port frees up
  497. require.NoError(t, blocker.Close())
  498. require.NoError(t, ds.reload(c, false))
  499. waitForBind(t, ds)
  500. ds.serverMu.Lock()
  501. assert.NotNil(t, ds.server, "a same-addr reload should retry after a failed bind")
  502. ds.serverMu.Unlock()
  503. ds.Stop()
  504. }
  505. // A disable that lands while Start is between its unlocked check and the guard must not leave a listener behind.
  506. func TestDnsServer_Start_refusesWhenDisabledUnderLock(t *testing.T) {
  507. port := freeUDPPort(t)
  508. ds, c := newTestDnsServer(t)
  509. setDnsConfig(c, "127.0.0.1", port, true, true)
  510. require.NoError(t, ds.reload(c, true))
  511. require.True(t, ds.enabled.Load())
  512. // Holding serverMu parks Start on the lock, the only way to land the disable in that window on purpose
  513. ds.serverMu.Lock()
  514. done := make(chan struct{})
  515. go func() {
  516. ds.Start()
  517. close(done)
  518. }()
  519. select {
  520. case <-done:
  521. ds.serverMu.Unlock()
  522. t.Fatal("Start returned early, the test never exercised the window")
  523. case <-time.After(time.Millisecond * 100):
  524. }
  525. // The disable reload's critical section. It sees nothing running, so it never calls Stop.
  526. ds.enabled.Store(false)
  527. ds.serverMu.Unlock()
  528. select {
  529. case <-done:
  530. case <-time.After(time.Second * 5):
  531. t.Fatal("Start never returned")
  532. }
  533. ds.serverMu.Lock()
  534. assert.Nil(t, ds.server, "Start must not install a listener a disable already cancelled")
  535. ds.serverMu.Unlock()
  536. pc, err := net.ListenPacket("udp", "127.0.0.1:"+port)
  537. require.NoError(t, err, "an orphaned listener is still holding the port")
  538. _ = pc.Close()
  539. }
  540. // A reload that can't bind says so, rather than finding out in the background
  541. func TestDnsServer_reload_busyPortFails(t *testing.T) {
  542. busy, err := net.ListenPacket("udp", "127.0.0.1:0")
  543. require.NoError(t, err)
  544. t.Cleanup(func() { _ = busy.Close() })
  545. _, port, err := net.SplitHostPort(busy.LocalAddr().String())
  546. require.NoError(t, err)
  547. ds, c := newTestDnsServer(t)
  548. setDnsConfig(c, "127.0.0.1", port, false, true)
  549. require.NoError(t, ds.reload(c, true))
  550. setDnsConfig(c, "127.0.0.1", port, true, true)
  551. require.Error(t, ds.reload(c, false))
  552. ds.serverMu.Lock()
  553. assert.Nil(t, ds.server, "a failed bind kept the slot")
  554. ds.serverMu.Unlock()
  555. }