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"
)
const appDataMaxLength = 16401
type clientHelloFields struct {
random []byte
sessionId []byte

View File

@ -66,6 +66,7 @@ func MakeSession(connConfig remoteConnConfig, authInfo authInfo, dialer common.D
Obfuscator: obfuscator,
Valve: nil,
Unordered: authInfo.Unordered,
MaxFrameSize: appDataMaxLength,
}
sesh := mux.MakeSession(authInfo.SessionId, seshConfig)

View File

@ -1,13 +1,13 @@
package client
import (
"github.com/cbeuw/Cloak/internal/common"
"io"
"net"
"sync/atomic"
"time"
mux "github.com/cbeuw/Cloak/internal/multiplex"
"github.com/cbeuw/Cloak/internal/util"
log "github.com/sirupsen/logrus"
)
@ -136,8 +136,15 @@ func RouteTCP(localConfig localConnConfig, newSeshFunc func() *mux.Session) {
stream.Close()
return
}
go util.Pipe(localConn, stream, 0)
util.Pipe(stream, localConn, localConfig.Timeout)
go func() {
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

@ -35,6 +35,7 @@ type Obfuscator struct {
// Remove TLS header, decrypt and unmarshall frames
Deobfs Deobfser
SessionKey [32]byte
minOverhead int
}
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) {
obfuscator = &Obfuscator{
SessionKey: sessionKey,
}
var payloadCipher cipher.AEAD
switch encryptionMethod {
case E_METHOD_PLAIN:
payloadCipher = nil
obfuscator.minOverhead = 0
case E_METHOD_AES_GCM:
var c cipher.Block
c, err = aes.NewCipher(sessionKey[:])
@ -151,19 +156,18 @@ func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (obfuscator *Obf
if err != nil {
return
}
obfuscator.minOverhead = payloadCipher.Overhead()
case E_METHOD_CHACHA20_POLY1305:
payloadCipher, err = chacha20poly1305.New(sessionKey[:])
if err != nil {
return
}
obfuscator.minOverhead = payloadCipher.Overhead()
default:
return nil, errors.New("Unknown encryption method")
}
obfuscator = &Obfuscator{
MakeObfs(sessionKey, payloadCipher),
MakeDeobfs(sessionKey, payloadCipher),
sessionKey,
}
obfuscator.Obfs = MakeObfs(sessionKey, payloadCipher)
obfuscator.Deobfs = MakeDeobfs(sessionKey, payloadCipher)
return
}

View File

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

View File

@ -96,11 +96,19 @@ func (s *Stream) Write(in []byte) (n int, err error) {
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{
StreamID: s.id,
Seq: atomic.AddUint64(&s.nextSendSeq, 1) - 1,
Closing: C_NOOP,
Payload: in,
Payload: payload,
}
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
}
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 == errBrokenSwitchboard {
s.session.SetTerminalMsg(err.Error())
@ -116,7 +124,7 @@ func (s *Stream) Write(in []byte) (n int, err error) {
}
return
}
return len(in), nil
return len(payload), nil
}

View File

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

View File

@ -3,6 +3,7 @@ package server
import (
"bytes"
"encoding/base64"
"github.com/cbeuw/Cloak/internal/common"
"github.com/cbeuw/Cloak/internal/util"
"io"
"net"
@ -72,6 +73,13 @@ func DispatchConnection(conn net.Conn, sta *State) {
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
// 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
@ -82,10 +90,6 @@ func DispatchConnection(conn net.Conn, sta *State) {
return
}
log.Trace("finished handshake")
seshConfig := mux.SessionConfig{
Obfuscator: obfuscator,
Valve: nil,
}
sesh := mux.MakeSession(0, seshConfig)
sesh.AddConnection(preparedConn)
//TODO: Router could be nil in cnc mode
@ -113,11 +117,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
return
}
sesh, existing, err := user.GetSession(ci.SessionId, mux.SessionConfig{
Obfuscator: obfuscator,
Valve: nil,
Unordered: ci.Unordered,
})
sesh, existing, err := user.GetSession(ci.SessionId, seshConfig)
if err != nil {
user.CloseSession(ci.SessionId, "")
log.Error(err)
@ -173,9 +173,17 @@ func DispatchConnection(conn net.Conn, sta *State) {
}
log.Tracef("%v endpoint has been successfully connected", ci.ProxyMethod)
go util.Pipe(localConn, newStream, sta.Timeout)
go util.Pipe(newStream, localConn, 0)
//TODO: stream timeout
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/cipher"
"crypto/rand"
"io"
"net"
"time"
log "github.com/sirupsen/logrus"
@ -58,18 +56,12 @@ func CryptoRandRead(buf []byte) {
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) {
// The maximum size of TLS message will be 16380+14+16. 14 because of the stream header and 16
// because of the salt/mac
// 16408 is the max TLS message size on Firefox
buf := make([]byte, 16378)
if srcReadTimeout != 0 {
src.SetReadDeadline(time.Now().Add(srcReadTimeout))
}
for {
if srcReadTimeout != 0 {
src.SetReadDeadline(time.Now().Add(srcReadTimeout))
@ -88,3 +80,5 @@ func Pipe(dst net.Conn, src net.Conn, srcReadTimeout time.Duration) {
}
}
}
*/