dns_server_test.go 7.7 KB

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