Merge branch 'master' into notsure2

This commit is contained in:
notsure2 2020-12-28 09:55:06 +02:00
commit edb18d5b4c
7 changed files with 248 additions and 102 deletions

View File

@ -73,6 +73,9 @@ func main() {
log.Info("Starting standalone mode") log.Info("Starting standalone mode")
} }
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
})
lvl, err := log.ParseLevel(*verbosity) lvl, err := log.ParseLevel(*verbosity)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -72,6 +72,10 @@ func main() {
var pluginMode bool var pluginMode bool
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
})
if os.Getenv("SS_LOCAL_HOST") != "" && os.Getenv("SS_LOCAL_PORT") != "" { if os.Getenv("SS_LOCAL_HOST") != "" && os.Getenv("SS_LOCAL_PORT") != "" {
pluginMode = true pluginMode = true
config = os.Getenv("SS_PLUGIN_OPTIONS") config = os.Getenv("SS_PLUGIN_OPTIONS")

View File

@ -65,14 +65,15 @@ func makeSessionPair(numConn int) (*Session, *Session, []*connPair) {
func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) { func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) {
var wg sync.WaitGroup var wg sync.WaitGroup
testData := make([]byte, msgLen)
rand.Read(testData)
for _, conn := range conns { for _, conn := range conns {
wg.Add(1) wg.Add(1)
go func(conn net.Conn) { go func(conn net.Conn) {
defer wg.Done() defer wg.Done()
testData := make([]byte, msgLen)
rand.Read(testData)
// we cannot call t.Fatalf in concurrent contexts // we cannot call t.Fatalf in concurrent contexts
n, err := conn.Write(testData) n, err := conn.Write(testData)
if n != msgLen { if n != msgLen {

View File

@ -11,11 +11,6 @@ import (
"golang.org/x/crypto/salsa20" "golang.org/x/crypto/salsa20"
) )
var u32 = binary.BigEndian.Uint32
var u64 = binary.BigEndian.Uint64
var putU32 = binary.BigEndian.PutUint32
var putU64 = binary.BigEndian.PutUint64
const frameHeaderLength = 14 const frameHeaderLength = 14
const salsa20NonceSize = 8 const salsa20NonceSize = 8
@ -98,8 +93,8 @@ func (o *Obfuscator) obfuscate(f *Frame, buf []byte, payloadOffsetInBuf int) (in
} }
header := buf[:frameHeaderLength] header := buf[:frameHeaderLength]
putU32(header[0:4], f.StreamID) binary.BigEndian.PutUint32(header[0:4], f.StreamID)
putU64(header[4:12], f.Seq) binary.BigEndian.PutUint64(header[4:12], f.Seq)
header[12] = f.Closing header[12] = f.Closing
header[13] = byte(extraLen) header[13] = byte(extraLen)
@ -130,8 +125,8 @@ func (o *Obfuscator) deobfuscate(f *Frame, in []byte) error {
nonce := in[len(in)-salsa20NonceSize:] nonce := in[len(in)-salsa20NonceSize:]
salsa20.XORKeyStream(header, header, nonce, &o.SessionKey) salsa20.XORKeyStream(header, header, nonce, &o.SessionKey)
streamID := u32(header[0:4]) streamID := binary.BigEndian.Uint32(header[0:4])
seq := u64(header[4:12]) seq := binary.BigEndian.Uint64(header[4:12])
closing := header[12] closing := header[12]
extraLen := header[13] extraLen := header[13]

View File

@ -4,7 +4,10 @@ import (
"bytes" "bytes"
"github.com/cbeuw/connutil" "github.com/cbeuw/connutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io"
"io/ioutil"
"math/rand" "math/rand"
"net"
"strconv" "strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -16,73 +19,188 @@ var seshConfigs = map[string]SessionConfig{
"ordered": {}, "ordered": {},
"unordered": {Unordered: true}, "unordered": {Unordered: true},
} }
var encryptionMethods = map[string]byte{
"plain": EncryptionMethodPlain,
"aes-256-gcm": EncryptionMethodAES256GCM,
"aes-128-gcm": EncryptionMethodAES128GCM,
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
}
const testPayloadLen = 1024 const testPayloadLen = 1024
const obfsBufLen = testPayloadLen * 2 const obfsBufLen = testPayloadLen * 2
func TestRecvDataFromRemote(t *testing.T) { func TestRecvDataFromRemote(t *testing.T) {
testPayload := make([]byte, testPayloadLen)
rand.Read(testPayload)
f := &Frame{
1,
0,
0,
testPayload,
}
obfsBuf := make([]byte, obfsBufLen)
var sessionKey [32]byte var sessionKey [32]byte
rand.Read(sessionKey[:]) rand.Read(sessionKey[:])
MakeObfuscatorUnwrap := func(method byte, sessionKey [32]byte) Obfuscator {
ret, err := MakeObfuscator(method, sessionKey)
if err != nil {
t.Fatalf("failed to make an obfuscator: %v", err)
}
return ret
}
encryptionMethods := map[string]Obfuscator{
"plain": MakeObfuscatorUnwrap(EncryptionMethodPlain, sessionKey),
"aes-gcm": MakeObfuscatorUnwrap(EncryptionMethodAES256GCM, sessionKey),
"chacha20-poly1305": MakeObfuscatorUnwrap(EncryptionMethodChaha20Poly1305, sessionKey),
}
for seshType, seshConfig := range seshConfigs { for seshType, seshConfig := range seshConfigs {
seshConfig := seshConfig seshConfig := seshConfig
t.Run(seshType, func(t *testing.T) { t.Run(seshType, func(t *testing.T) {
for method, obfuscator := range encryptionMethods { var err error
obfuscator := obfuscator seshConfig.Obfuscator, err = MakeObfuscator(EncryptionMethodPlain, sessionKey)
t.Run(method, func(t *testing.T) { if err != nil {
seshConfig.Obfuscator = obfuscator t.Fatalf("failed to make obfuscator: %v", err)
}
t.Run("initial frame", func(t *testing.T) {
sesh := MakeSession(0, seshConfig) sesh := MakeSession(0, seshConfig)
n, err := sesh.obfuscate(f, obfsBuf, 0) obfsBuf := make([]byte, obfsBufLen)
if err != nil { f := Frame{
t.Error(err) 1,
return 0,
0,
make([]byte, testPayloadLen),
} }
rand.Read(f.Payload)
n, err := sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n]) err = sesh.recvDataFromRemote(obfsBuf[:n])
if err != nil { assert.NoError(t, err)
t.Error(err)
return
}
stream, err := sesh.Accept() stream, err := sesh.Accept()
if err != nil { assert.NoError(t, err)
t.Error(err)
return
}
resultPayload := make([]byte, testPayloadLen) resultPayload := make([]byte, testPayloadLen)
_, err = stream.Read(resultPayload) _, err = stream.Read(resultPayload)
if err != nil { assert.NoError(t, err)
t.Error(err)
return assert.EqualValues(t, f.Payload, resultPayload)
})
t.Run("two frames in order", func(t *testing.T) {
sesh := MakeSession(0, seshConfig)
obfsBuf := make([]byte, obfsBufLen)
f := Frame{
1,
0,
0,
make([]byte, testPayloadLen),
} }
if !bytes.Equal(testPayload, resultPayload) { rand.Read(f.Payload)
t.Errorf("Expecting %x, got %x", testPayload, resultPayload) n, err := sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
stream, err := sesh.Accept()
assert.NoError(t, err)
resultPayload := make([]byte, testPayloadLen)
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, f.Payload, resultPayload)
f.Seq += 1
rand.Read(f.Payload)
n, err = sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, f.Payload, resultPayload)
})
t.Run("two frames in order", func(t *testing.T) {
sesh := MakeSession(0, seshConfig)
obfsBuf := make([]byte, obfsBufLen)
f := Frame{
1,
0,
0,
make([]byte, testPayloadLen),
} }
rand.Read(f.Payload)
n, err := sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
stream, err := sesh.Accept()
assert.NoError(t, err)
resultPayload := make([]byte, testPayloadLen)
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, f.Payload, resultPayload)
f.Seq += 1
rand.Read(f.Payload)
n, err = sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, f.Payload, resultPayload)
})
if seshType == "ordered" {
t.Run("frames out of order", func(t *testing.T) {
sesh := MakeSession(0, seshConfig)
obfsBuf := make([]byte, obfsBufLen)
f := Frame{
1,
0,
0,
nil,
}
// First frame
seq0 := make([]byte, testPayloadLen)
rand.Read(seq0)
f.Seq = 0
f.Payload = seq0
n, err := sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
// Third frame
seq2 := make([]byte, testPayloadLen)
rand.Read(seq2)
f.Seq = 2
f.Payload = seq2
n, err = sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
// Second frame
seq1 := make([]byte, testPayloadLen)
rand.Read(seq1)
f.Seq = 1
f.Payload = seq1
n, err = sesh.obfuscate(&f, obfsBuf, 0)
assert.NoError(t, err)
err = sesh.recvDataFromRemote(obfsBuf[:n])
assert.NoError(t, err)
// Expect things to receive in order
stream, err := sesh.Accept()
assert.NoError(t, err)
resultPayload := make([]byte, testPayloadLen)
// First
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, seq0, resultPayload)
// Second
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, seq1, resultPayload)
// Third
_, err = io.ReadFull(stream, resultPayload)
assert.NoError(t, err)
assert.EqualValues(t, seq2, resultPayload)
}) })
} }
}) })
} }
} }
@ -94,10 +212,9 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
var sessionKey [32]byte var sessionKey [32]byte
rand.Read(sessionKey[:]) rand.Read(sessionKey[:])
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
seshConfig := seshConfigs["ordered"] seshConfig := seshConfigs["ordered"]
seshConfig.Obfuscator = obfuscator seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
sesh := MakeSession(0, seshConfig) sesh := MakeSession(0, seshConfig)
f1 := &Frame{ f1 := &Frame{
@ -233,10 +350,9 @@ func TestRecvDataFromRemote_Closing_OutOfOrder(t *testing.T) {
var sessionKey [32]byte var sessionKey [32]byte
rand.Read(sessionKey[:]) rand.Read(sessionKey[:])
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
seshConfig := seshConfigs["ordered"] seshConfig := seshConfigs["ordered"]
seshConfig.Obfuscator = obfuscator seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
sesh := MakeSession(0, seshConfig) sesh := MakeSession(0, seshConfig)
// receive stream 1 closing first // receive stream 1 closing first
@ -415,7 +531,7 @@ func TestSession_timeoutAfter(t *testing.T) {
} }
} }
func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) { func BenchmarkRecvDataFromRemote(b *testing.B) {
testPayload := make([]byte, testPayloadLen) testPayload := make([]byte, testPayloadLen)
rand.Read(testPayload) rand.Read(testPayload)
f := &Frame{ f := &Frame{
@ -428,22 +544,20 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
var sessionKey [32]byte var sessionKey [32]byte
rand.Read(sessionKey[:]) rand.Read(sessionKey[:])
table := map[string]byte{
"plain": EncryptionMethodPlain,
"aes-256-gcm": EncryptionMethodAES256GCM,
"aes-128-gcm": EncryptionMethodAES128GCM,
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
}
const maxIter = 100_000 // run with -benchtime 100000x to avoid index out of bounds panic const maxIter = 100_000 // run with -benchtime 100000x to avoid index out of bounds panic
for name, ep := range table { for name, ep := range encryptionMethods {
ep := ep ep := ep
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
seshConfig := seshConfigs["ordered"] for seshType, seshConfig := range seshConfigs {
obfuscator, _ := MakeObfuscator(ep, sessionKey) b.Run(seshType, func(b *testing.B) {
seshConfig.Obfuscator = obfuscator seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
sesh := MakeSession(0, seshConfig) sesh := MakeSession(0, seshConfig)
go func() {
stream, _ := sesh.Accept()
stream.(*Stream).WriteTo(ioutil.Discard)
}()
binaryFrames := [maxIter][]byte{} binaryFrames := [maxIter][]byte{}
for i := 0; i < maxIter; i++ { for i := 0; i < maxIter; i++ {
obfsBuf := make([]byte, obfsBufLen) obfsBuf := make([]byte, obfsBufLen)
@ -459,28 +573,21 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
} }
}) })
} }
})
}
} }
func BenchmarkMultiStreamWrite(b *testing.B) { func BenchmarkMultiStreamWrite(b *testing.B) {
var sessionKey [32]byte var sessionKey [32]byte
rand.Read(sessionKey[:]) rand.Read(sessionKey[:])
table := map[string]byte{
"plain": EncryptionMethodPlain,
"aes-256-gcm": EncryptionMethodAES256GCM,
"aes-128-gcm": EncryptionMethodAES128GCM,
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
}
testPayload := make([]byte, testPayloadLen) testPayload := make([]byte, testPayloadLen)
for name, ep := range table { for name, ep := range encryptionMethods {
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
for seshType, seshConfig := range seshConfigs { for seshType, seshConfig := range seshConfigs {
seshConfig := seshConfig
b.Run(seshType, func(b *testing.B) { b.Run(seshType, func(b *testing.B) {
obfuscator, _ := MakeObfuscator(ep, sessionKey) seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
seshConfig.Obfuscator = obfuscator
sesh := MakeSession(0, seshConfig) sesh := MakeSession(0, seshConfig)
sesh.AddConnection(connutil.Discard()) sesh.AddConnection(connutil.Discard())
b.ResetTimer() b.ResetTimer()
@ -496,3 +603,36 @@ func BenchmarkMultiStreamWrite(b *testing.B) {
}) })
} }
} }
func BenchmarkLatency(b *testing.B) {
var sessionKey [32]byte
rand.Read(sessionKey[:])
for name, ep := range encryptionMethods {
b.Run(name, func(b *testing.B) {
for seshType, seshConfig := range seshConfigs {
b.Run(seshType, func(b *testing.B) {
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
clientSesh := MakeSession(0, seshConfig)
serverSesh := MakeSession(0, seshConfig)
c, s := net.Pipe()
clientSesh.AddConnection(c)
serverSesh.AddConnection(s)
buf := make([]byte, 64)
clientStream, _ := clientSesh.OpenStream()
clientStream.Write(buf)
serverStream, _ := serverSesh.Accept()
io.ReadFull(serverStream, buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clientStream.Write(buf)
io.ReadFull(serverStream, buf)
}
})
}
})
}
}

View File

@ -82,6 +82,8 @@ func (sb *streamBuffer) Write(f *Frame) (toBeClosed bool, err error) {
} }
saved := *f saved := *f
saved.Payload = make([]byte, len(f.Payload))
copy(saved.Payload, f.Payload)
heap.Push(&sb.sh, &saved) heap.Push(&sb.sh, &saved)
// Keep popping from the heap until empty or to the point that the wanted seq was not received // Keep popping from the heap until empty or to the point that the wanted seq was not received
for len(sb.sh) > 0 && sb.sh[0].Seq == sb.nextRecvSeq { for len(sb.sh) > 0 && sb.sh[0].Seq == sb.nextRecvSeq {

View File

@ -222,14 +222,15 @@ func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, a
func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) { func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) {
var wg sync.WaitGroup var wg sync.WaitGroup
testData := make([]byte, msgLen)
rand.Read(testData)
for _, conn := range conns { for _, conn := range conns {
wg.Add(1) wg.Add(1)
go func(conn net.Conn) { go func(conn net.Conn) {
defer wg.Done() defer wg.Done()
testData := make([]byte, msgLen)
rand.Read(testData)
// we cannot call t.Fatalf in concurrent contexts // we cannot call t.Fatalf in concurrent contexts
n, err := conn.Write(testData) n, err := conn.Write(testData)
if n != msgLen { if n != msgLen {