direct_linux_test.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux
  4. package dns
  5. import (
  6. "context"
  7. "fmt"
  8. "net/netip"
  9. "os"
  10. "path/filepath"
  11. "testing"
  12. "testing/synctest"
  13. "github.com/illarion/gonotify/v3"
  14. "tailscale.com/util/dnsname"
  15. "tailscale.com/util/eventbus/eventbustest"
  16. )
  17. func TestDNSTrampleRecovery(t *testing.T) {
  18. HookWatchFile.Set(watchFile)
  19. synctest.Test(t, func(t *testing.T) {
  20. tmp := t.TempDir()
  21. if err := os.MkdirAll(filepath.Join(tmp, "etc"), 0700); err != nil {
  22. t.Fatal(err)
  23. }
  24. const resolvPath = "/etc/resolv.conf"
  25. fs := directFS{prefix: tmp}
  26. readFile := func(t *testing.T, path string) string {
  27. t.Helper()
  28. b, err := fs.ReadFile(path)
  29. if err != nil {
  30. t.Errorf("Reading DNS config: %v", err)
  31. }
  32. return string(b)
  33. }
  34. bus := eventbustest.NewBus(t)
  35. eventbustest.LogAllEvents(t, bus)
  36. m := newDirectManagerOnFS(t.Logf, nil, bus, fs)
  37. defer m.Close()
  38. if err := m.SetDNS(OSConfig{
  39. Nameservers: []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("8.8.4.4")},
  40. SearchDomains: []dnsname.FQDN{"ts.net.", "ts-dns.test."},
  41. MatchDomains: []dnsname.FQDN{"ignored."},
  42. }); err != nil {
  43. t.Fatal(err)
  44. }
  45. const want = `# resolv.conf(5) file generated by tailscale
  46. # For more info, see https://tailscale.com/s/resolvconf-overwrite
  47. # DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN
  48. nameserver 8.8.8.8
  49. nameserver 8.8.4.4
  50. search ts.net ts-dns.test
  51. `
  52. if got := readFile(t, resolvPath); got != want {
  53. t.Fatalf("resolv.conf:\n%s, want:\n%s", got, want)
  54. }
  55. tw := eventbustest.NewWatcher(t, bus)
  56. const trample = "Hvem er det som tramper på min bro?"
  57. if err := fs.WriteFile(resolvPath, []byte(trample), 0644); err != nil {
  58. t.Fatal(err)
  59. }
  60. synctest.Wait()
  61. if err := eventbustest.Expect(tw, eventbustest.Type[TrampleDNS]()); err != nil {
  62. t.Errorf("did not see trample event: %s", err)
  63. }
  64. })
  65. }
  66. // watchFile is generally copied from linuxtrample, but cancels the context
  67. // after the first call to cb() after the first trample to end the test.
  68. func watchFile(ctx context.Context, dir, filename string, cb func()) error {
  69. ctx, cancel := context.WithCancel(ctx)
  70. defer cancel()
  71. const events = gonotify.IN_ATTRIB |
  72. gonotify.IN_CLOSE_WRITE |
  73. gonotify.IN_CREATE |
  74. gonotify.IN_DELETE |
  75. gonotify.IN_MODIFY |
  76. gonotify.IN_MOVE
  77. watcher, err := gonotify.NewDirWatcher(ctx, events, dir)
  78. if err != nil {
  79. return fmt.Errorf("NewDirWatcher: %w", err)
  80. }
  81. for {
  82. select {
  83. case event := <-watcher.C:
  84. if event.Name == filename {
  85. cb()
  86. cancel()
  87. }
  88. case <-ctx.Done():
  89. return ctx.Err()
  90. }
  91. }
  92. }