Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent replays of server data #78

Merged
merged 15 commits into from
Aug 27, 2020
Prev Previous commit
Next Next commit
Rename ERR_REPLAY and add a test
  • Loading branch information
Ben Schwartz committed Aug 21, 2020
commit 463afb06f106a1201062e1995f53431206c057fe
2 changes: 1 addition & 1 deletion shadowsocks/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (s *tcpService) handleConnection(listenerPort int, clientConn onet.DuplexCo
if isServerSalt {
status = "ERR_REPLAY_SERVER"
} else {
status = "ERR_REPLAY"
status = "ERR_REPLAY_CLIENT"
}
s.absorbProbe(listenerPort, clientConn, clientLocation, status, &proxyMetrics)
logger.Debugf(status+": %v in %s sent %d bytes", clientConn.RemoteAddr(), clientLocation, proxyMetrics.ClientProxy)
Expand Down
66 changes: 64 additions & 2 deletions shadowsocks/tcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,77 @@ func TestReplayDefense(t *testing.T) {
t.Errorf("Unexpected probe data: %v", data)
}
status := testMetrics.probeStatus[0]
if status != "ERR_REPLAY" {
if status != "ERR_REPLAY_CLIENT" {
t.Errorf("Unexpected TCP probe status: %s", status)
}
} else {
t.Error("Replay should have triggered probe detection")
}
if len(testMetrics.closeStatus) == 2 {
status := testMetrics.closeStatus[1]
if status != "ERR_REPLAY" {
if status != "ERR_REPLAY_CLIENT" {
t.Errorf("Unexpected TCP close status: %s", status)
}
} else {
t.Error("Replay should have reported an error status")
}
}

func TestReverseReplayDefense(t *testing.T) {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
if err != nil {
t.Fatalf("ListenTCP failed: %v", err)
}
cipherList, err := MakeTestCiphers(MakeTestSecrets(1))
if err != nil {
t.Fatal(err)
}
replayCache := NewReplayCache(5)
testMetrics := &probeTestMetrics{}
const testTimeout = 200 * time.Millisecond
s := NewTCPService(cipherList, &replayCache, testMetrics, testTimeout)
_, snapshot := cipherList.SnapshotForClientIP(nil)
cipherEntry := snapshot[0].Value.(*CipherEntry)
cipher := cipherEntry.Cipher
reader, writer := io.Pipe()
ssWriter := NewShadowsocksWriter(writer, cipher)
// Use a server-marked salt in the client's preamble.
ssWriter.SetSaltGenerator(cipherEntry.SaltGenerator)
go ssWriter.Write([]byte{0})
preamble := make([]byte, 32+2+16)
if _, err := io.ReadFull(reader, preamble); err != nil {
t.Fatal(err)
}

go s.Serve(listener)

conn, err := net.Dial(listener.Addr().Network(), listener.Addr().String())
if err != nil {
t.Fatal(err)
}
n, err := conn.Write(preamble)
if n < len(preamble) {
t.Error(err)
}
conn.Close()
s.GracefulStop()

// The preamble should have been marked as a server replay.
if len(testMetrics.probeData) == 1 {
data := testMetrics.probeData[0]
if data.ClientProxy != int64(len(preamble)) {
t.Errorf("Unexpected probe data: %v", data)
}
status := testMetrics.probeStatus[0]
if status != "ERR_REPLAY_SERVER" {
t.Errorf("Unexpected TCP probe status: %s", status)
}
} else {
t.Error("Replay should have triggered probe detection")
}
if len(testMetrics.closeStatus) == 1 {
status := testMetrics.closeStatus[0]
if status != "ERR_REPLAY_SERVER" {
t.Errorf("Unexpected TCP close status: %s", status)
}
} else {
Expand Down