improve ss

This commit is contained in:
ginuerzh
2021-11-01 21:57:28 +08:00
parent e2995ece96
commit ec8615991b
71 changed files with 554 additions and 316 deletions

View File

@ -0,0 +1,100 @@
package bufpool
import "sync"
var (
smallBufferSize = 1 * 1024 // 1KB buffer
mediumBufferSize = 8 * 1024 // 8KB buffer
largeBufferSize = 64 * 1024 // 64KB buffer
)
var (
pools = []struct {
size int
pool sync.Pool
}{
{
size: 128,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 128)
},
},
},
{
size: 512,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 512)
},
},
},
{
size: 1024,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
},
},
{
size: 4096,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 4096)
},
},
},
{
size: 8192,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 8192)
},
},
},
{
size: 16 * 1024,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 16*1024)
},
},
},
{
size: 32 * 1024,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 32*1024)
},
},
},
{
size: 64 * 1024,
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 64*1024)
},
},
},
}
)
// Get returns a buffer size range from (0, 64]KB,
// panic if size > 64KB.
func Get(size int) []byte {
for i := range pools {
if size <= pools[i].size {
return pools[i].pool.Get().([]byte)
}
}
panic("size too large (max=64KB)")
}
func Put(b []byte) {
for i := range pools {
if len(b) == pools[i].size {
pools[i].pool.Put(b)
}
}
}

34
pkg/internal/utils/kcp.go Normal file
View File

@ -0,0 +1,34 @@
package utils
import (
"net"
"github.com/golang/snappy"
)
type kcpCompStreamConn struct {
net.Conn
w *snappy.Writer
r *snappy.Reader
}
func KCPCompStreamConn(conn net.Conn) net.Conn {
return &kcpCompStreamConn{
Conn: conn,
w: snappy.NewBufferedWriter(conn),
r: snappy.NewReader(conn),
}
}
func (c *kcpCompStreamConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
func (c *kcpCompStreamConn) Write(b []byte) (n int, err error) {
n, err = c.w.Write(b)
if err != nil {
return
}
err = c.w.Flush()
return n, err
}

104
pkg/internal/utils/quic.go Normal file
View File

@ -0,0 +1,104 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
"net"
"github.com/lucas-clemente/quic-go"
)
type quicConn struct {
quic.Session
quic.Stream
}
func QUICConn(session quic.Session, stream quic.Stream) net.Conn {
return &quicConn{
Session: session,
Stream: stream,
}
}
type quicCipherConn struct {
net.PacketConn
key []byte
}
func QUICCipherConn(conn net.PacketConn, key []byte) net.PacketConn {
return &quicCipherConn{
PacketConn: conn,
key: key,
}
}
func (conn *quicCipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) {
n, addr, err = conn.PacketConn.ReadFrom(data)
if err != nil {
return
}
b, err := conn.decrypt(data[:n])
if err != nil {
return
}
copy(data, b)
return len(b), addr, nil
}
func (conn *quicCipherConn) WriteTo(data []byte, addr net.Addr) (n int, err error) {
b, err := conn.encrypt(data)
if err != nil {
return
}
_, err = conn.PacketConn.WriteTo(b, addr)
if err != nil {
return
}
return len(b), nil
}
func (conn *quicCipherConn) encrypt(data []byte) ([]byte, error) {
c, err := aes.NewCipher(conn.key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, data, nil), nil
}
func (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) {
c, err := aes.NewCipher(conn.key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return nil, errors.New("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}

60
pkg/internal/utils/ss.go Normal file
View File

@ -0,0 +1,60 @@
package utils
import (
"bytes"
"net"
"github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
type shadowCipher struct {
cipher *ss.Cipher
}
func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
return ss.NewConn(conn, c.cipher.Copy())
}
func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {
return ss.NewSecurePacketConn(conn, c.cipher.Copy())
}
func ShadowCipher(method, password string, key string) (core.Cipher, error) {
if method == "" && password == "" {
return nil, nil
}
c, _ := ss.NewCipher(method, password)
if c != nil {
return &shadowCipher{cipher: c}, nil
}
return core.PickCipher(method, []byte(key), password)
}
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
// we wrap around it to make io.Copy happy.
type shadowConn struct {
net.Conn
wbuf *bytes.Buffer
}
func ShadowConn(conn net.Conn, header []byte) net.Conn {
return &shadowConn{
Conn: conn,
wbuf: bytes.NewBuffer(header),
}
}
func (c *shadowConn) Write(b []byte) (n int, err error) {
n = len(b) // force byte length consistent
if c.wbuf.Len() > 0 {
c.wbuf.Write(b) // append the data to the cached header
_, err = c.Conn.Write(c.wbuf.Bytes())
c.wbuf.Reset()
return
}
_, err = c.Conn.Write(b)
return
}

32
pkg/internal/utils/tcp.go Normal file
View File

@ -0,0 +1,32 @@
package utils
import (
"net"
"time"
)
const (
defaultKeepAlivePeriod = 180 * time.Second
)
// TCPKeepAliveListener is a TCP listener with keep alive enabled.
type TCPKeepAliveListener struct {
KeepAlivePeriod time.Duration
*net.TCPListener
}
func (l *TCPKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := l.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
period := l.KeepAlivePeriod
if period <= 0 {
period = defaultKeepAlivePeriod
}
tc.SetKeepAlivePeriod(period)
return tc, nil
}

40
pkg/internal/utils/tls.go Normal file
View File

@ -0,0 +1,40 @@
package utils
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
)
// LoadTLSConfig loads the certificate from cert & key files and optional client CA file.
func LoadTLSConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
if pool, _ := loadCA(caFile); pool != nil {
cfg.ClientCAs = pool
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
return cfg, nil
}
func loadCA(caFile string) (cp *x509.CertPool, err error) {
if caFile == "" {
return
}
cp = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
if !cp.AppendCertsFromPEM(data) {
return nil, errors.New("AppendCertsFromPEM failed")
}
return
}

41
pkg/internal/utils/ws.go Normal file
View File

@ -0,0 +1,41 @@
package utils
import (
"net"
"time"
"github.com/gorilla/websocket"
)
type websocketConn struct {
*websocket.Conn
rb []byte
}
func WebsocketServerConn(conn *websocket.Conn) net.Conn {
return &websocketConn{
Conn: conn,
}
}
func (c *websocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.ReadMessage()
}
n = copy(b, c.rb)
c.rb = c.rb[n:]
return
}
func (c *websocketConn) Write(b []byte) (n int, err error) {
err = c.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
return
}
func (c *websocketConn) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}