|
|
|
|
@ -11,7 +11,6 @@ import ( |
|
|
|
|
"crypto/tls" |
|
|
|
|
"encoding/binary" |
|
|
|
|
"encoding/json" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"io/ioutil" |
|
|
|
|
"net" |
|
|
|
|
@ -535,93 +534,6 @@ func TestDeviceStartStop(t *testing.T) { |
|
|
|
|
dev.Close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A context used in TestConnClosing() which seeks to test that code which calls
|
|
|
|
|
// Err() to see if a connection is already being closed does not then proceed to
|
|
|
|
|
// try to acquire the mutex, as this would lead to deadlock. When Err() is called
|
|
|
|
|
// this context acquires the lock itself, in order to force a deadlock (and test
|
|
|
|
|
// failure on timeout).
|
|
|
|
|
type testConnClosingContext struct { |
|
|
|
|
parent context.Context |
|
|
|
|
mu *sync.Mutex |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *testConnClosingContext) Deadline() (deadline time.Time, ok bool) { |
|
|
|
|
d, o := c.parent.Deadline() |
|
|
|
|
return d, o |
|
|
|
|
} |
|
|
|
|
func (c *testConnClosingContext) Done() <-chan struct{} { |
|
|
|
|
return c.parent.Done() |
|
|
|
|
} |
|
|
|
|
func (c *testConnClosingContext) Err() error { |
|
|
|
|
// Deliberately deadlock if anything grabs the lock after checking Err()
|
|
|
|
|
c.mu.Lock() |
|
|
|
|
return errors.New("testConnClosingContext error") |
|
|
|
|
} |
|
|
|
|
func (c *testConnClosingContext) Value(key interface{}) interface{} { |
|
|
|
|
return c.parent.Value(key) |
|
|
|
|
} |
|
|
|
|
func (*testConnClosingContext) String() string { |
|
|
|
|
return "testConnClosingContext" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestConnClosing(t *testing.T) { |
|
|
|
|
privateKey, err := wgkey.NewPrivate() |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("generating private key: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
logf, closeLogf := logger.LogfCloser(t.Logf) |
|
|
|
|
defer closeLogf() |
|
|
|
|
|
|
|
|
|
epCh := make(chan []string, 100) |
|
|
|
|
conn, err := NewConn(Options{ |
|
|
|
|
Logf: logf, |
|
|
|
|
PacketListener: nettype.Std{}, |
|
|
|
|
EndpointsFunc: func(eps []string) { |
|
|
|
|
epCh <- eps |
|
|
|
|
}, |
|
|
|
|
SimulatedNetwork: false, |
|
|
|
|
DisableLegacyNetworking: true, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("constructing magicsock: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
derpMap, cleanup := runDERPAndStun(t, logf, nettype.Std{}, netaddr.IPv4(127, 0, 3, 1)) |
|
|
|
|
defer cleanup() |
|
|
|
|
|
|
|
|
|
// The point of this test case is to exercise handling in derpWriteChanOfAddr() which
|
|
|
|
|
// returns early if connCtx.Err() returns non-nil, to avoid a deadlock on conn.mu.
|
|
|
|
|
// We swap in a context which always returns an error, and deliberately grabs the lock
|
|
|
|
|
// to cause a deadlock if magicsock.go tries to acquire the lock after calling Err().
|
|
|
|
|
closingCtx := testConnClosingContext{parent: conn.connCtx, mu: &conn.mu} |
|
|
|
|
conn.connCtx = &closingCtx |
|
|
|
|
conn.Start() |
|
|
|
|
|
|
|
|
|
conn.SetDERPMap(derpMap) |
|
|
|
|
if err := conn.SetPrivateKey(privateKey); err != nil { |
|
|
|
|
t.Fatalf("setting private key in magicsock: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tun := tuntest.NewChannelTUN() |
|
|
|
|
tsTun := tstun.WrapTUN(logf, tun.TUN()) |
|
|
|
|
tsTun.SetFilter(filter.NewAllowAllForTest(logf)) |
|
|
|
|
|
|
|
|
|
dev := device.NewDevice(tsTun, &device.DeviceOptions{ |
|
|
|
|
Logger: wireguardGoLogger(logf), |
|
|
|
|
CreateEndpoint: conn.CreateEndpoint, |
|
|
|
|
CreateBind: conn.CreateBind, |
|
|
|
|
SkipBindUpdate: true, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
dev.Up() |
|
|
|
|
conn.WaitReady(t) |
|
|
|
|
|
|
|
|
|
// We don't assert any failures within the test itself. If derpWriteChanOfAddr tries to
|
|
|
|
|
// grab the lock it will deadlock, and conn.WaitReady(t) will call t.Fatal() after timeout.
|
|
|
|
|
// (verified by deliberately breaking derpWriteChanOfAddr)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Exercise a code path in sendDiscoMessage if the connection has been closed.
|
|
|
|
|
func TestConnClosed(t *testing.T) { |
|
|
|
|
mstun := &natlab.Machine{Name: "stun"} |
|
|
|
|
|