dns_server_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package nebula
  2. import (
  3. "context"
  4. "io"
  5. "net"
  6. "net/netip"
  7. "strconv"
  8. "testing"
  9. "time"
  10. "github.com/miekg/dns"
  11. "github.com/sirupsen/logrus"
  12. "github.com/slackhq/nebula/config"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. )
  16. func TestParsequery(t *testing.T) {
  17. l := logrus.New()
  18. hostMap := &HostMap{}
  19. ds := &dnsServer{
  20. l: l,
  21. dnsMap4: make(map[string]netip.Addr),
  22. dnsMap6: make(map[string]netip.Addr),
  23. hostMap: hostMap,
  24. }
  25. ds.enabled.Store(true)
  26. addrs := []netip.Addr{
  27. netip.MustParseAddr("1.2.3.4"),
  28. netip.MustParseAddr("1.2.3.5"),
  29. netip.MustParseAddr("fd01::24"),
  30. netip.MustParseAddr("fd01::25"),
  31. }
  32. ds.Add("test.com.com", addrs)
  33. m := &dns.Msg{}
  34. m.SetQuestion("test.com.com", dns.TypeA)
  35. ds.parseQuery(m, nil)
  36. assert.NotNil(t, m.Answer)
  37. assert.Equal(t, "1.2.3.4", m.Answer[0].(*dns.A).A.String())
  38. m = &dns.Msg{}
  39. m.SetQuestion("test.com.com", dns.TypeAAAA)
  40. ds.parseQuery(m, nil)
  41. assert.NotNil(t, m.Answer)
  42. assert.Equal(t, "fd01::24", m.Answer[0].(*dns.AAAA).AAAA.String())
  43. }
  44. func Test_getDnsServerAddr(t *testing.T) {
  45. c := config.NewC(nil)
  46. c.Settings["lighthouse"] = map[string]any{
  47. "dns": map[string]any{
  48. "host": "0.0.0.0",
  49. "port": "1",
  50. },
  51. }
  52. assert.Equal(t, "0.0.0.0:1", getDnsServerAddr(c))
  53. c.Settings["lighthouse"] = map[string]any{
  54. "dns": map[string]any{
  55. "host": "::",
  56. "port": "1",
  57. },
  58. }
  59. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  60. c.Settings["lighthouse"] = map[string]any{
  61. "dns": map[string]any{
  62. "host": "[::]",
  63. "port": "1",
  64. },
  65. }
  66. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  67. // Make sure whitespace doesn't mess us up
  68. c.Settings["lighthouse"] = map[string]any{
  69. "dns": map[string]any{
  70. "host": "[::] ",
  71. "port": "1",
  72. },
  73. }
  74. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  75. }
  76. func newTestDnsServer(t *testing.T) (*dnsServer, *config.C) {
  77. t.Helper()
  78. l := logrus.New()
  79. l.Out = io.Discard
  80. ds := &dnsServer{
  81. l: l,
  82. ctx: context.Background(),
  83. dnsMap4: make(map[string]netip.Addr),
  84. dnsMap6: make(map[string]netip.Addr),
  85. hostMap: &HostMap{},
  86. }
  87. ds.mux = dns.NewServeMux()
  88. ds.mux.HandleFunc(".", ds.handleDnsRequest)
  89. return ds, config.NewC(l)
  90. }
  91. func setDnsConfig(c *config.C, host string, port string, amLighthouse, serveDns bool) {
  92. c.Settings["lighthouse"] = map[string]any{
  93. "am_lighthouse": amLighthouse,
  94. "serve_dns": serveDns,
  95. "dns": map[string]any{
  96. "host": host,
  97. "port": port,
  98. },
  99. }
  100. }
  101. func TestDnsServer_reload_initial_disabled(t *testing.T) {
  102. ds, c := newTestDnsServer(t)
  103. setDnsConfig(c, "127.0.0.1", "0", true, false)
  104. require.NoError(t, ds.reload(c, true))
  105. assert.False(t, ds.enabled.Load())
  106. assert.Equal(t, "127.0.0.1:0", ds.addr)
  107. assert.Nil(t, ds.server)
  108. }
  109. func TestDnsServer_reload_initial_enabled(t *testing.T) {
  110. ds, c := newTestDnsServer(t)
  111. setDnsConfig(c, "127.0.0.1", "0", true, true)
  112. require.NoError(t, ds.reload(c, true))
  113. assert.True(t, ds.enabled.Load())
  114. assert.Equal(t, "127.0.0.1:0", ds.addr)
  115. // initial never starts a runner; that's Control.Start's job
  116. assert.Nil(t, ds.server)
  117. }
  118. func TestDnsServer_reload_initial_serveDnsWithoutLighthouse(t *testing.T) {
  119. ds, c := newTestDnsServer(t)
  120. setDnsConfig(c, "127.0.0.1", "0", false, true)
  121. require.NoError(t, ds.reload(c, true))
  122. // Wants DNS but isn't a lighthouse: gated off, no runner.
  123. assert.False(t, ds.enabled.Load())
  124. }
  125. func TestDnsServer_reload_sameAddr_noOp(t *testing.T) {
  126. ds, c := newTestDnsServer(t)
  127. setDnsConfig(c, "127.0.0.1", "0", true, true)
  128. require.NoError(t, ds.reload(c, true))
  129. // No server running yet, no addr change. Reload should not spawn anything.
  130. require.NoError(t, ds.reload(c, false))
  131. assert.True(t, ds.enabled.Load())
  132. assert.Nil(t, ds.server)
  133. }
  134. func TestDnsServer_StartStop_lifecycle(t *testing.T) {
  135. // Bind to a real (random) UDP port so we exercise the actual
  136. // ListenAndServe + Shutdown plumbing including the started-chan race fix.
  137. port := freeUDPPort(t)
  138. ds, c := newTestDnsServer(t)
  139. setDnsConfig(c, "127.0.0.1", port, true, true)
  140. require.NoError(t, ds.reload(c, true))
  141. done := make(chan struct{})
  142. go func() {
  143. ds.Start()
  144. close(done)
  145. }()
  146. waitFor(t, func() bool {
  147. ds.serverMu.Lock()
  148. started := ds.started
  149. ds.serverMu.Unlock()
  150. if started == nil {
  151. return false
  152. }
  153. select {
  154. case <-started:
  155. return true
  156. default:
  157. return false
  158. }
  159. })
  160. ds.Stop()
  161. select {
  162. case <-done:
  163. case <-time.After(5 * time.Second):
  164. t.Fatal("Start did not return after Stop")
  165. }
  166. }
  167. func TestDnsServer_Stop_beforeBind_doesNotHang(t *testing.T) {
  168. // Stop called immediately after Start should not deadlock even if bind
  169. // hasn't completed yet. This exercises the started-chan close-on-bind-fail
  170. // path: by binding to an obviously bad port (privileged) we get a fast
  171. // bind error before NotifyStartedFunc fires.
  172. ds, c := newTestDnsServer(t)
  173. // Use a port that should fail to bind (negative would be invalid, use a
  174. // host that won't resolve to ensure listenUDP fails quickly).
  175. setDnsConfig(c, "256.256.256.256", "53", true, true)
  176. require.NoError(t, ds.reload(c, true))
  177. done := make(chan struct{})
  178. go func() {
  179. ds.Start()
  180. close(done)
  181. }()
  182. // Give Start a moment to attempt the bind and fail.
  183. select {
  184. case <-done:
  185. // Bind failed and Start returned; Stop should be a no-op.
  186. case <-time.After(time.Second):
  187. t.Fatal("Start did not return after a bad bind")
  188. }
  189. stopped := make(chan struct{})
  190. go func() {
  191. ds.Stop()
  192. close(stopped)
  193. }()
  194. select {
  195. case <-stopped:
  196. case <-time.After(time.Second):
  197. t.Fatal("Stop hung after a failed bind")
  198. }
  199. }
  200. func TestDnsServer_reload_disable_stopsRunningServer(t *testing.T) {
  201. port := freeUDPPort(t)
  202. ds, c := newTestDnsServer(t)
  203. setDnsConfig(c, "127.0.0.1", port, true, true)
  204. require.NoError(t, ds.reload(c, true))
  205. startReturned := make(chan struct{})
  206. go func() {
  207. ds.Start()
  208. close(startReturned)
  209. }()
  210. waitForBind(t, ds)
  211. // Toggle serve_dns off; reload should shut the running server down.
  212. setDnsConfig(c, "127.0.0.1", port, true, false)
  213. require.NoError(t, ds.reload(c, false))
  214. select {
  215. case <-startReturned:
  216. case <-time.After(5 * time.Second):
  217. t.Fatal("Start did not return after reload disabled DNS")
  218. }
  219. assert.False(t, ds.enabled.Load())
  220. }
  221. func freeUDPPort(t *testing.T) string {
  222. t.Helper()
  223. conn, err := net.ListenPacket("udp", "127.0.0.1:0")
  224. require.NoError(t, err)
  225. port := conn.LocalAddr().(*net.UDPAddr).Port
  226. require.NoError(t, conn.Close())
  227. return strconv.Itoa(port)
  228. }
  229. func waitForBind(t *testing.T, ds *dnsServer) {
  230. t.Helper()
  231. waitFor(t, func() bool {
  232. ds.serverMu.Lock()
  233. started := ds.started
  234. ds.serverMu.Unlock()
  235. if started == nil {
  236. return false
  237. }
  238. select {
  239. case <-started:
  240. return true
  241. default:
  242. return false
  243. }
  244. })
  245. }
  246. func waitFor(t *testing.T, cond func() bool) {
  247. t.Helper()
  248. deadline := time.Now().Add(5 * time.Second)
  249. for time.Now().Before(deadline) {
  250. if cond() {
  251. return
  252. }
  253. time.Sleep(5 * time.Millisecond)
  254. }
  255. t.Fatal("timed out waiting for condition")
  256. }