Set frame size limit through multiplexer

This commit is contained in:
Andy Wang 2020-04-09 16:37:46 +01:00
parent 780d607436
commit e41394c83c
10 changed files with 173 additions and 38 deletions

View File

@ -11,6 +11,8 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
const appDataMaxLength = 16401
type clientHelloFields struct { type clientHelloFields struct {
random []byte random []byte
sessionId []byte sessionId []byte

View File

@ -63,9 +63,10 @@ func MakeSession(connConfig remoteConnConfig, authInfo authInfo, dialer common.D
} }
seshConfig := mux.SessionConfig{ seshConfig := mux.SessionConfig{
Obfuscator: obfuscator, Obfuscator: obfuscator,
Valve: nil, Valve: nil,
Unordered: authInfo.Unordered, Unordered: authInfo.Unordered,
MaxFrameSize: appDataMaxLength,
} }
sesh := mux.MakeSession(authInfo.SessionId, seshConfig) sesh := mux.MakeSession(authInfo.SessionId, seshConfig)

View File

@ -1,13 +1,13 @@
package client package client
import ( import (
"github.com/cbeuw/Cloak/internal/common"
"io" "io"
"net" "net"
"sync/atomic" "sync/atomic"
"time" "time"
mux "github.com/cbeuw/Cloak/internal/multiplex" mux "github.com/cbeuw/Cloak/internal/multiplex"
"github.com/cbeuw/Cloak/internal/util"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -136,8 +136,15 @@ func RouteTCP(localConfig localConnConfig, newSeshFunc func() *mux.Session) {
stream.Close() stream.Close()
return return
} }
go util.Pipe(localConn, stream, 0) go func() {
util.Pipe(stream, localConn, localConfig.Timeout) if _, err := common.Copy(localConn, stream, 0); err != nil {
log.Debugf("copying stream to proxy client: %v", err)
}
}()
//util.Pipe(stream, localConn, localConfig.Timeout)
if _, err = common.Copy(stream, localConn, localConfig.Timeout); err != nil {
log.Debugf("copying proxy client to stream: %v", err)
}
}() }()
} }

104
internal/common/copy.go Normal file
View File

@ -0,0 +1,104 @@
/*
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Forked from https://golang.org/src/io/io.go
*/
package common
import (
"io"
"net"
"time"
)
// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func Copy(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) (written int64, err error) {
/*
// If the reader has a WriteTo method, use it to do the copy.
// Avoids an allocation and a copy.
if wt, ok := src.(WriterTo); ok {
return wt.WriteTo(dst)
}
// Similarly, if the writer has a ReadFrom method, use it to do the copy.
if rt, ok := dst.(ReaderFrom); ok {
return rt.ReadFrom(src)
}
*/
//if buf == nil {
size := 32 * 1024
/*
if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
if l.N < 1 {
size = 1
} else {
size = int(l.N)
}
}
*/
buf := make([]byte, size)
//}
for {
if srcReadTimeout != 0 {
src.SetReadDeadline(time.Now().Add(srcReadTimeout))
/*
err =
if err != nil {
break
}
*/
}
nr, er := src.Read(buf)
if nr > 0 {
var offset int
for offset < nr {
nw, ew := dst.Write(buf[offset:nr])
if nw > 0 {
written += int64(nw)
}
if ew != nil {
err = ew
break
}
offset += nw
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}

View File

@ -33,8 +33,9 @@ type Obfuscator struct {
// Used in Stream.Write. Add multiplexing headers, encrypt and add TLS header // Used in Stream.Write. Add multiplexing headers, encrypt and add TLS header
Obfs Obfser Obfs Obfser
// Remove TLS header, decrypt and unmarshall frames // Remove TLS header, decrypt and unmarshall frames
Deobfs Deobfser Deobfs Deobfser
SessionKey [32]byte SessionKey [32]byte
minOverhead int
} }
func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser { func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
@ -137,10 +138,14 @@ func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Deobfser {
} }
func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (obfuscator *Obfuscator, err error) { func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (obfuscator *Obfuscator, err error) {
obfuscator = &Obfuscator{
SessionKey: sessionKey,
}
var payloadCipher cipher.AEAD var payloadCipher cipher.AEAD
switch encryptionMethod { switch encryptionMethod {
case E_METHOD_PLAIN: case E_METHOD_PLAIN:
payloadCipher = nil payloadCipher = nil
obfuscator.minOverhead = 0
case E_METHOD_AES_GCM: case E_METHOD_AES_GCM:
var c cipher.Block var c cipher.Block
c, err = aes.NewCipher(sessionKey[:]) c, err = aes.NewCipher(sessionKey[:])
@ -151,19 +156,18 @@ func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (obfuscator *Obf
if err != nil { if err != nil {
return return
} }
obfuscator.minOverhead = payloadCipher.Overhead()
case E_METHOD_CHACHA20_POLY1305: case E_METHOD_CHACHA20_POLY1305:
payloadCipher, err = chacha20poly1305.New(sessionKey[:]) payloadCipher, err = chacha20poly1305.New(sessionKey[:])
if err != nil { if err != nil {
return return
} }
obfuscator.minOverhead = payloadCipher.Overhead()
default: default:
return nil, errors.New("Unknown encryption method") return nil, errors.New("Unknown encryption method")
} }
obfuscator = &Obfuscator{ obfuscator.Obfs = MakeObfs(sessionKey, payloadCipher)
MakeObfs(sessionKey, payloadCipher), obfuscator.Deobfs = MakeDeobfs(sessionKey, payloadCipher)
MakeDeobfs(sessionKey, payloadCipher),
sessionKey,
}
return return
} }

View File

@ -13,7 +13,8 @@ import (
) )
const ( const (
acceptBacklog = 1024 acceptBacklog = 1024
// TODO: will this be a signature?
defaultSendRecvBufSize = 20480 defaultSendRecvBufSize = 20480
) )
@ -29,6 +30,7 @@ type SessionConfig struct {
Unordered bool Unordered bool
MaxFrameSize int // maximum size of the frame, including the header
SendBufferSize int SendBufferSize int
ReceiveBufferSize int ReceiveBufferSize int
} }
@ -77,6 +79,9 @@ func MakeSession(id uint32, config SessionConfig) *Session {
if config.ReceiveBufferSize <= 0 { if config.ReceiveBufferSize <= 0 {
sesh.ReceiveBufferSize = defaultSendRecvBufSize sesh.ReceiveBufferSize = defaultSendRecvBufSize
} }
if config.MaxFrameSize <= 0 {
sesh.MaxFrameSize = defaultSendRecvBufSize - 1024
}
sbConfig := switchboardConfig{ sbConfig := switchboardConfig{
valve: sesh.Valve, valve: sesh.Valve,

View File

@ -96,11 +96,19 @@ func (s *Stream) Write(in []byte) (n int, err error) {
return 0, ErrBrokenStream return 0, ErrBrokenStream
} }
var payload []byte
maxDataLen := s.session.MaxFrameSize - HEADER_LEN - s.session.minOverhead
if len(in) <= maxDataLen {
payload = in
} else {
payload = in[:maxDataLen]
}
f := &Frame{ f := &Frame{
StreamID: s.id, StreamID: s.id,
Seq: atomic.AddUint64(&s.nextSendSeq, 1) - 1, Seq: atomic.AddUint64(&s.nextSendSeq, 1) - 1,
Closing: C_NOOP, Closing: C_NOOP,
Payload: in, Payload: payload,
} }
i, err := s.session.Obfs(f, s.obfsBuf) i, err := s.session.Obfs(f, s.obfsBuf)
@ -108,7 +116,7 @@ func (s *Stream) Write(in []byte) (n int, err error) {
return i, err return i, err
} }
n, err = s.session.sb.send(s.obfsBuf[:i], &s.assignedConnId) n, err = s.session.sb.send(s.obfsBuf[:i], &s.assignedConnId)
log.Tracef("%v sent to remote through stream %v with err %v", len(in), s.id, err) log.Tracef("%v sent to remote through stream %v with err %v", len(payload), s.id, err)
if err != nil { if err != nil {
if err == errBrokenSwitchboard { if err == errBrokenSwitchboard {
s.session.SetTerminalMsg(err.Error()) s.session.SetTerminalMsg(err.Error())
@ -116,7 +124,7 @@ func (s *Stream) Write(in []byte) (n int, err error) {
} }
return return
} }
return len(in), nil return len(payload), nil
} }

View File

@ -11,6 +11,8 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
const appDataMaxLength = 16401
type TLS struct{} type TLS struct{}
var ErrBadClientHello = errors.New("non (or malformed) ClientHello") var ErrBadClientHello = errors.New("non (or malformed) ClientHello")

View File

@ -3,6 +3,7 @@ package server
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"github.com/cbeuw/Cloak/internal/common"
"github.com/cbeuw/Cloak/internal/util" "github.com/cbeuw/Cloak/internal/util"
"io" "io"
"net" "net"
@ -72,6 +73,13 @@ func DispatchConnection(conn net.Conn, sta *State) {
return return
} }
seshConfig := mux.SessionConfig{
Obfuscator: obfuscator,
Valve: nil,
Unordered: ci.Unordered,
MaxFrameSize: appDataMaxLength,
}
// adminUID can use the server as normal with unlimited QoS credits. The adminUID is not // adminUID can use the server as normal with unlimited QoS credits. The adminUID is not
// added to the userinfo database. The distinction between going into the admin mode // added to the userinfo database. The distinction between going into the admin mode
// and normal proxy mode is that sessionID needs == 0 for admin mode // and normal proxy mode is that sessionID needs == 0 for admin mode
@ -82,10 +90,6 @@ func DispatchConnection(conn net.Conn, sta *State) {
return return
} }
log.Trace("finished handshake") log.Trace("finished handshake")
seshConfig := mux.SessionConfig{
Obfuscator: obfuscator,
Valve: nil,
}
sesh := mux.MakeSession(0, seshConfig) sesh := mux.MakeSession(0, seshConfig)
sesh.AddConnection(preparedConn) sesh.AddConnection(preparedConn)
//TODO: Router could be nil in cnc mode //TODO: Router could be nil in cnc mode
@ -113,11 +117,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
return return
} }
sesh, existing, err := user.GetSession(ci.SessionId, mux.SessionConfig{ sesh, existing, err := user.GetSession(ci.SessionId, seshConfig)
Obfuscator: obfuscator,
Valve: nil,
Unordered: ci.Unordered,
})
if err != nil { if err != nil {
user.CloseSession(ci.SessionId, "") user.CloseSession(ci.SessionId, "")
log.Error(err) log.Error(err)
@ -173,9 +173,17 @@ func DispatchConnection(conn net.Conn, sta *State) {
} }
log.Tracef("%v endpoint has been successfully connected", ci.ProxyMethod) log.Tracef("%v endpoint has been successfully connected", ci.ProxyMethod)
go util.Pipe(localConn, newStream, sta.Timeout) //TODO: stream timeout
go util.Pipe(newStream, localConn, 0) go func() {
if _, err := common.Copy(localConn, newStream, sta.Timeout); err != nil {
log.Debugf("copying stream to proxy client: %v", err)
}
}()
go func() {
if _, err := common.Copy(newStream, localConn, 0); err != nil {
log.Debugf("copying proxy client to stream: %v", err)
}
}()
} }
} }

View File

@ -4,8 +4,6 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"io"
"net"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -58,18 +56,12 @@ func CryptoRandRead(buf []byte) {
log.Fatal("Cannot get cryptographic random bytes after 10 retries") log.Fatal("Cannot get cryptographic random bytes after 10 retries")
} }
// ReadTLS reads TLS data according to its record layer /*
//func ReadTLS(conn net.Conn, buffer []byte) (n int, err error) {
//}
func Pipe(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) { func Pipe(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) {
// The maximum size of TLS message will be 16380+14+16. 14 because of the stream header and 16 // The maximum size of TLS message will be 16380+14+16. 14 because of the stream header and 16
// because of the salt/mac // because of the salt/mac
// 16408 is the max TLS message size on Firefox // 16408 is the max TLS message size on Firefox
buf := make([]byte, 16378) buf := make([]byte, 16378)
if srcReadTimeout != 0 {
src.SetReadDeadline(time.Now().Add(srcReadTimeout))
}
for { for {
if srcReadTimeout != 0 { if srcReadTimeout != 0 {
src.SetReadDeadline(time.Now().Add(srcReadTimeout)) src.SetReadDeadline(time.Now().Add(srcReadTimeout))
@ -88,3 +80,5 @@ func Pipe(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) {
} }
} }
} }
*/