1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package tunservice
import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
)
// fakeDNSSetup answers like networksetup for Wi-Fi (the user's own DNS
// server), Ethernet (the network's), and a disabled Bluetooth PAN.
type fakeDNSSetup struct {
dns map[string][]string
calls []string
// fail makes setting this service's servers fail, the way networksetup
// does: a message, and no error.
fail string
}
func newFakeDNSSetup(t *testing.T) (*fakeDNSSetup, macDNS) {
f := &fakeDNSSetup{dns: map[string][]string{
"Wi-Fi": {"1.1.1.1", "2606:4700:4700::1111"},
"Ethernet": nil,
"Bluetooth PAN": nil,
}}
return f, macDNS{run: f.run, backup: filepath.Join(t.TempDir(), "dns-backup.json"), server: "198.18.0.2"}
}
func (f *fakeDNSSetup) run(args ...string) (string, error) {
f.calls = append(f.calls, strings.Join(args, " "))
if args[0] == "-listallnetworkservices" {
return "An asterisk (*) denotes that a network service is disabled.\nWi-Fi\nEthernet\n*Bluetooth PAN", nil
}
servers, ok := f.dns[args[1]]
if !ok {
return "** Error: " + args[1] + " is not a recognized network service.", nil
}
switch args[0] {
case "-getdnsservers":
if len(servers) == 0 {
return "There aren't any DNS Servers set on " + args[1] + ".", nil
}
return strings.Join(servers, "\n"), nil
case "-setdnsservers":
if args[1] == f.fail {
return "** Error: The parameters were not valid.", nil
}
if args[2] == "Empty" {
f.dns[args[1]] = nil
} else {
f.dns[args[1]] = append([]string(nil), args[2:]...)
}
return "", nil
}
return "", fmt.Errorf("unexpected %v", args)
}
func TestTheTunsDNSServerIsSetOnEveryServiceAndSetBack(t *testing.T) {
f, m := newFakeDNSSetup(t)
if err := m.apply(); err != nil {
t.Fatal(err)
}
for s, servers := range f.dns {
if !slices.Equal(servers, []string{"198.18.0.2"}) {
t.Errorf("%s has %v", s, servers)
}
}
if _, err := os.Stat(m.backup); err != nil {
t.Fatalf("no backup: %v", err)
}
// Checked again while the TUN runs: nothing to change.
f.calls = nil
if err := m.apply(); err != nil {
t.Fatal(err)
}
for _, c := range f.calls {
if strings.HasPrefix(c, "-setdnsservers") {
t.Errorf("set again: %s", c)
}
}
if err := m.restore(); err != nil {
t.Fatal(err)
}
if !slices.Equal(f.dns["Wi-Fi"], []string{"1.1.1.1", "2606:4700:4700::1111"}) || f.dns["Ethernet"] != nil || f.dns["Bluetooth PAN"] != nil {
t.Fatalf("set back to %v", f.dns)
}
if _, err := os.Stat(m.backup); !os.IsNotExist(err) {
t.Fatalf("the backup is still there: %v", err)
}
}
// A crash leaves the TUN's DNS server and the backup: the service sets
// them back when it starts again, with a new macDNS.
func TestTheDNSServersAreSetBackAfterACrash(t *testing.T) {
f, m := newFakeDNSSetup(t)
if err := m.apply(); err != nil {
t.Fatal(err)
}
again := macDNS{run: f.run, backup: m.backup, server: m.server}
if err := again.restore(); err != nil {
t.Fatal(err)
}
if !slices.Equal(f.dns["Wi-Fi"], []string{"1.1.1.1", "2606:4700:4700::1111"}) || f.dns["Ethernet"] != nil {
t.Fatalf("set back to %v", f.dns)
}
// Without a backup there is nothing to do.
f.calls = nil
if err := again.restore(); err != nil || len(f.calls) != 0 {
t.Fatalf("restore without a backup: %v, calls %v", err, f.calls)
}
}
// A setting the user changes while the TUN runs is set back to what they
// chose, not to what it was before; one they change after stays.
func TestADNSSettingChangedMeanwhileIsKept(t *testing.T) {
f, m := newFakeDNSSetup(t)
if err := m.apply(); err != nil {
t.Fatal(err)
}
f.dns["Wi-Fi"] = []string{"9.9.9.9"}
if err := m.apply(); err != nil {
t.Fatal(err)
}
if !slices.Equal(f.dns["Wi-Fi"], []string{"198.18.0.2"}) {
t.Fatalf("Wi-Fi has %v while the TUN runs", f.dns["Wi-Fi"])
}
f.dns["Ethernet"] = []string{"8.8.8.8"}
if err := m.restore(); err != nil {
t.Fatal(err)
}
if !slices.Equal(f.dns["Wi-Fi"], []string{"9.9.9.9"}) || !slices.Equal(f.dns["Ethernet"], []string{"8.8.8.8"}) {
t.Fatalf("set back to %v", f.dns)
}
}
// networksetup reports some failures only in its output.
func TestAFailedDNSSettingIsAnError(t *testing.T) {
f, m := newFakeDNSSetup(t)
f.fail = "Ethernet"
err := m.apply()
if err == nil || !strings.Contains(err.Error(), "Ethernet") {
t.Fatalf("got %v", err)
}
// What was changed is still set back.
f.fail = ""
if err := m.restore(); err != nil {
t.Fatal(err)
}
if !slices.Equal(f.dns["Wi-Fi"], []string{"1.1.1.1", "2606:4700:4700::1111"}) {
t.Fatalf("Wi-Fi has %v", f.dns["Wi-Fi"])
}
if _, err := m.get("Nowhere"); err == nil {
t.Fatal("reading an unknown service succeeded")
}
}