Skip to content

Commit 8d4d23e

Browse files
authored
fix: use magicsock DERPHeaders in netcheck (#39)
1 parent 30f543e commit 8d4d23e

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

net/netcheck/netcheck.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ type Client struct {
203203
// If false, the default net.Resolver will be used, with no caching.
204204
UseDNSCache bool
205205

206+
// GetDERPHeaders optionally specifies headers to send with all HTTP(S) DERP
207+
// probes.
208+
GetDERPHeaders func() http.Header
209+
206210
// For tests
207211
testEnoughRegions int
208212
testCaptivePortalDelay time.Duration
@@ -1277,7 +1281,13 @@ func (c *Client) measureHTTPLatency(ctx context.Context, reg *tailcfg.DERPRegion
12771281

12781282
var ip netip.Addr
12791283

1284+
derpHeaders := http.Header{}
1285+
if c.GetDERPHeaders != nil {
1286+
derpHeaders = c.GetDERPHeaders()
1287+
}
1288+
12801289
dc := derphttp.NewNetcheckClient(c.logf)
1290+
dc.Header = derpHeaders
12811291
defer dc.Close()
12821292

12831293
var hasForceHTTPNode = false
@@ -1356,6 +1366,9 @@ func (c *Client) measureHTTPLatency(ctx context.Context, reg *tailcfg.DERPRegion
13561366
if err != nil {
13571367
return 0, ip, err
13581368
}
1369+
for k := range derpHeaders {
1370+
req.Header.Set(k, derpHeaders.Get(k))
1371+
}
13591372

13601373
resp, err := hc.Do(req)
13611374
if err != nil {

net/netcheck/netcheck_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,91 @@ func TestNodeAddrResolve(t *testing.T) {
10221022
}
10231023
}
10241024

1025+
func TestProbeHeaders(t *testing.T) {
1026+
logf, closeLogf := logger.LogfCloser(t.Logf)
1027+
defer closeLogf()
1028+
1029+
// Create a DERP server manually, without a STUN server and with a custom
1030+
// handler.
1031+
derpServer := derp.NewServer(key.NewNode(), logf)
1032+
derpHandler := derphttp.Handler(derpServer)
1033+
1034+
expectedHeaders := http.Header{}
1035+
expectedHeaders.Set("X-Cool-Test", "yes")
1036+
expectedHeaders.Set("X-Proxy-Auth-Key", "blah blah blah")
1037+
1038+
var called atomic.Bool
1039+
httpsrv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1040+
called.Store(true)
1041+
for k, v := range expectedHeaders {
1042+
if got := r.Header[k]; !reflect.DeepEqual(got, v) {
1043+
t.Errorf("unexpected header %q: got %q; want %q", k, got, v)
1044+
}
1045+
}
1046+
1047+
if r.URL.Path == "/derp/latency-check" {
1048+
w.WriteHeader(http.StatusOK)
1049+
return
1050+
}
1051+
if r.URL.Path == "/derp" {
1052+
derpHandler.ServeHTTP(w, r)
1053+
return
1054+
}
1055+
1056+
t.Errorf("unexpected request: %v", r.URL)
1057+
w.WriteHeader(http.StatusNotFound)
1058+
}))
1059+
httpsrv.Config.ErrorLog = logger.StdLogger(logf)
1060+
httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
1061+
httpsrv.StartTLS()
1062+
t.Cleanup(func() {
1063+
httpsrv.CloseClientConnections()
1064+
httpsrv.Close()
1065+
derpServer.Close()
1066+
})
1067+
1068+
derpMap := &tailcfg.DERPMap{
1069+
Regions: map[int]*tailcfg.DERPRegion{
1070+
1: {
1071+
RegionID: 1,
1072+
RegionCode: "derpy",
1073+
Nodes: []*tailcfg.DERPNode{
1074+
{
1075+
Name: "d1",
1076+
RegionID: 1,
1077+
HostName: "localhost",
1078+
// Don't specify an IP address to avoid ICMP pinging,
1079+
// which will bypass the artificial latency.
1080+
IPv4: "",
1081+
IPv6: "",
1082+
STUNPort: -1,
1083+
DERPPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port,
1084+
InsecureForTests: true,
1085+
},
1086+
},
1087+
},
1088+
},
1089+
}
1090+
1091+
c := &Client{
1092+
Logf: t.Logf,
1093+
UDPBindAddr: "127.0.0.1:0",
1094+
GetDERPHeaders: func() http.Header { return expectedHeaders.Clone() },
1095+
}
1096+
1097+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
1098+
defer cancel()
1099+
1100+
_, err := c.GetReport(ctx, derpMap)
1101+
if err != nil {
1102+
t.Fatal(err)
1103+
}
1104+
1105+
if !called.Load() {
1106+
t.Error("didn't call test handler")
1107+
}
1108+
}
1109+
10251110
func TestNeverPickSTUNOnlyRegionAsPreferredDERP(t *testing.T) {
10261111
// Create two DERP regions, one with a STUN server only and one with only a
10271112
// DERP node. Add artificial latency of 300ms to the DERP region, and test

wgengine/magicsock/magicsock.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,13 @@ func NewConn(opts Options) (*Conn, error) {
443443
SkipExternalNetwork: inTest(),
444444
PortMapper: c.portMapper,
445445
UseDNSCache: true,
446+
GetDERPHeaders: func() http.Header {
447+
h := c.derpHeader.Load()
448+
if h == nil {
449+
return nil
450+
}
451+
return h.Clone()
452+
},
446453
}
447454

448455
c.ignoreSTUNPackets()

0 commit comments

Comments
 (0)