improve ss
This commit is contained in:
100
pkg/internal/bufpool/pool.go
Normal file
100
pkg/internal/bufpool/pool.go
Normal 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
34
pkg/internal/utils/kcp.go
Normal 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
104
pkg/internal/utils/quic.go
Normal 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
60
pkg/internal/utils/ss.go
Normal 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
32
pkg/internal/utils/tcp.go
Normal 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
40
pkg/internal/utils/tls.go
Normal 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
41
pkg/internal/utils/ws.go
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user