dns_server_test.go 8.7 KB

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