merge ss and ssu

This commit is contained in:
ginuerzh
2021-11-12 16:44:31 +08:00
parent cae199dbd9
commit 3fe5084629
26 changed files with 545 additions and 393 deletions

View File

@ -0,0 +1,113 @@
package local
import (
"context"
"net"
"time"
"github.com/go-gost/gost/pkg/bypass"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
)
func init() {
registry.RegisterHandler("forward", NewHandler)
}
type localForwardHandler struct {
group *chain.NodeGroup
chain *chain.Chain
bypass bypass.Bypass
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
}
return &localForwardHandler{
chain: options.Chain,
bypass: options.Bypass,
logger: options.Logger,
}
}
func (h *localForwardHandler) Init(md md.Metadata) (err error) {
return h.parseMetadata(md)
}
// Forward implements handler.Forwarder.
func (h *localForwardHandler) Forward(group *chain.NodeGroup) {
h.group = group
}
func (h *localForwardHandler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
start := time.Now()
h.logger = h.logger.WithFields(map[string]interface{}{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
h.logger.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
h.logger.WithFields(map[string]interface{}{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
target := h.group.Next()
if target == nil {
h.logger.Error("no target available")
return
}
h.logger = h.logger.WithFields(map[string]interface{}{
"dst": target.Addr(),
})
h.logger.Infof("%s >> %s", conn.RemoteAddr(), target.Addr())
r := (&handler.Router{}).
WithChain(h.chain).
WithRetry(h.md.retryCount).
WithLogger(h.logger)
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
cc, err := r.Dial(ctx, network, target.Addr())
if err != nil {
h.logger.Error(err)
// TODO: the router itself may be failed due to the failed node in the router,
// the dead marker may be a wrong operation.
target.Marker().Mark()
return
}
defer cc.Close()
target.Marker().Reset()
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr())
handler.Transport(conn, cc)
h.logger.
WithFields(map[string]interface{}{
"duration": time.Since(t),
}).
Infof("%s >-< %s", conn.RemoteAddr(), target.Addr())
}
func (h *localForwardHandler) parseMetadata(md md.Metadata) (err error) {
h.md.readTimeout = md.GetDuration(readTimeout)
h.md.retryCount = md.GetInt(retryCount)
return
}

View File

@ -0,0 +1,15 @@
package local
import (
"time"
)
const (
readTimeout = "readTimeout"
retryCount = "retry"
)
type metadata struct {
readTimeout time.Duration
retryCount int
}

View File

@ -4,6 +4,7 @@ import (
"context"
"net"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/metadata"
)
@ -11,3 +12,7 @@ type Handler interface {
Init(metadata.Metadata) error
Handle(context.Context, net.Conn)
}
type Forwarder interface {
Forward(*chain.NodeGroup)
}

View File

@ -36,6 +36,7 @@ func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Co
if count <= 0 {
count = 1
}
r.logger.Debugf("dial: %s/%s", address, network)
for i := 0; i < count; i++ {
route := r.chain.GetRouteFor(network, address)

View File

@ -90,13 +90,29 @@ func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn) {
case gosocks5.CmdConnect:
h.handleConnect(ctx, conn, req.Addr.String())
case gosocks5.CmdBind:
h.handleBind(ctx, conn, req)
if h.md.enableBind {
h.handleBind(ctx, conn, req)
} else {
h.logger.Error("BIND is diabled")
}
case socks.CmdMuxBind:
h.handleMuxBind(ctx, conn, req)
if h.md.enableBind {
h.handleMuxBind(ctx, conn, req)
} else {
h.logger.Error("BIND is diabled")
}
case gosocks5.CmdUdp:
h.handleUDP(ctx, conn, req)
if h.md.enableUDP {
h.handleUDP(ctx, conn, req)
} else {
h.logger.Error("UDP relay is diabled")
}
case socks.CmdUDPTun:
h.handleUDPTun(ctx, conn, req)
if h.md.enableUDP {
h.handleUDPTun(ctx, conn, req)
} else {
h.logger.Error("UDP relay is diabled")
}
default:
h.logger.Errorf("unknown cmd: %d", req.Cmd)
resp := gosocks5.NewReply(gosocks5.CmdUnsupported, nil)

View File

@ -10,18 +10,6 @@ import (
md "github.com/go-gost/gost/pkg/metadata"
)
const (
certFile = "certFile"
keyFile = "keyFile"
caFile = "caFile"
authsKey = "auths"
readTimeout = "readTimeout"
timeout = "timeout"
retryCount = "retry"
noTLS = "notls"
udpBufferSize = "udpBufferSize"
)
type metadata struct {
tlsConfig *tls.Config
authenticator auth.Authenticator
@ -29,10 +17,26 @@ type metadata struct {
readTimeout time.Duration
retryCount int
noTLS bool
enableBind bool
enableUDP bool
udpBufferSize int
}
func (h *socks5Handler) parseMetadata(md md.Metadata) error {
const (
certFile = "certFile"
keyFile = "keyFile"
caFile = "caFile"
authsKey = "auths"
readTimeout = "readTimeout"
timeout = "timeout"
retryCount = "retry"
noTLS = "notls"
enableBind = "bind"
enableUDP = "udp"
udpBufferSize = "udpBufferSize"
)
var err error
h.md.tlsConfig, err = util_tls.LoadTLSConfig(
md.GetString(certFile),
@ -62,6 +66,8 @@ func (h *socks5Handler) parseMetadata(md md.Metadata) error {
h.md.timeout = md.GetDuration(timeout)
h.md.retryCount = md.GetInt(retryCount)
h.md.noTLS = md.GetBool(noTLS)
h.md.enableBind = md.GetBool(enableBind)
h.md.enableUDP = md.GetBool(enableUDP)
h.md.udpBufferSize = md.GetInt(udpBufferSize)
if h.md.udpBufferSize > 0 {

View File

@ -1,6 +1,7 @@
package ss
import (
"bufio"
"context"
"io"
"io/ioutil"
@ -61,24 +62,55 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn) {
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
sc := conn
// standard UDP relay.
if pc, ok := conn.(net.PacketConn); ok {
if h.md.enableUDP {
h.handleUDP(ctx, conn.RemoteAddr(), pc)
return
} else {
h.logger.Error("UDP relay is diabled")
}
return
}
if h.md.cipher != nil {
sc = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil)
conn = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil)
}
if h.md.readTimeout > 0 {
sc.SetReadDeadline(time.Now().Add(h.md.readTimeout))
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
}
addr := &gosocks5.Addr{}
_, err := addr.ReadFrom(sc)
br := bufio.NewReader(conn)
data, err := br.Peek(3)
if err != nil {
h.logger.Error(err)
h.discard(conn)
return
}
conn.SetReadDeadline(time.Time{})
sc.SetReadDeadline(time.Time{})
conn = handler.NewBufferReaderConn(conn, br)
if data[2] == 0xff {
if h.md.enableUDP {
// UDP-over-TCP relay
h.handleUDPTun(ctx, conn)
} else {
h.logger.Error("UDP relay is diabled")
}
return
}
// standard TCP.
addr := &gosocks5.Addr{}
if _, err = addr.ReadFrom(conn); err != nil {
h.logger.Error(err)
h.discard(conn)
return
}
conn.SetReadDeadline(time.Time{})
h.logger = h.logger.WithFields(map[string]interface{}{
"dst": addr.String(),
@ -103,7 +135,7 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn) {
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), addr)
handler.Transport(sc, cc)
handler.Transport(conn, cc)
h.logger.
WithFields(map[string]interface{}{
"duration": time.Since(t),
@ -114,18 +146,3 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn) {
func (h *ssHandler) discard(conn net.Conn) {
io.Copy(ioutil.Discard, conn)
}
func (h *ssHandler) parseMetadata(md md.Metadata) (err error) {
h.md.cipher, err = ss.ShadowCipher(
md.GetString(method),
md.GetString(password),
md.GetString(key),
)
if err != nil {
return
}
h.md.readTimeout = md.GetDuration(readTimeout)
h.md.retryCount = md.GetInt(retryCount)
return
}

View File

@ -3,19 +3,53 @@ package ss
import (
"time"
"github.com/go-gost/gost/pkg/internal/utils/ss"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/shadowsocks/go-shadowsocks2/core"
)
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
retryCount = "retry"
)
type metadata struct {
cipher core.Cipher
readTimeout time.Duration
retryCount int
bufferSize int
enableUDP bool
}
func (h *ssHandler) parseMetadata(md md.Metadata) (err error) {
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
retryCount = "retry"
enableUDP = "udp"
bufferSize = "bufferSize"
)
h.md.cipher, err = ss.ShadowCipher(
md.GetString(method),
md.GetString(password),
md.GetString(key),
)
if err != nil {
return
}
h.md.readTimeout = md.GetDuration(readTimeout)
h.md.retryCount = md.GetInt(retryCount)
h.md.enableUDP = md.GetBool(enableUDP)
h.md.bufferSize = md.GetInt(bufferSize)
if h.md.bufferSize > 0 {
if h.md.bufferSize < 512 {
h.md.bufferSize = 512 // min buffer size
}
if h.md.bufferSize > 65*1024 {
h.md.bufferSize = 65 * 1024 // max buffer size
}
} else {
h.md.bufferSize = 4096 // default buffer size
}
return
}

View File

@ -1,64 +1,21 @@
package ssu
package ss
import (
"context"
"net"
"time"
"github.com/go-gost/gost/pkg/bypass"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/internal/bufpool"
"github.com/go-gost/gost/pkg/internal/utils/socks"
"github.com/go-gost/gost/pkg/internal/utils/ss"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
)
func init() {
registry.RegisterHandler("ssu", NewHandler)
}
type ssuHandler struct {
chain *chain.Chain
bypass bypass.Bypass
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
func (h *ssHandler) handleUDP(ctx context.Context, raddr net.Addr, conn net.PacketConn) {
if h.md.cipher != nil {
conn = h.md.cipher.PacketConn(conn)
}
return &ssuHandler{
chain: options.Chain,
bypass: options.Bypass,
logger: options.Logger,
}
}
func (h *ssuHandler) Init(md md.Metadata) (err error) {
return h.parseMetadata(md)
}
func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
start := time.Now()
h.logger = h.logger.WithFields(map[string]interface{}{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
h.logger.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
h.logger.WithFields(map[string]interface{}{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
// obtain a udp connection
r := (&handler.Router{}).
WithChain(h.chain).
@ -81,28 +38,40 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn) {
"bind": cc.LocalAddr().String(),
})
h.logger.Infof("bind on %s OK", cc.LocalAddr().String())
t := time.Now()
h.logger.Infof("%s <-> %s", raddr, cc.LocalAddr())
h.relayPacket(
ss.UDPServerConn(conn, raddr, h.md.bufferSize),
cc,
)
h.logger.
WithFields(map[string]interface{}{"duration": time.Since(t)}).
Infof("%s >-< %s", raddr, cc.LocalAddr())
}
pc, ok := conn.(net.PacketConn)
if ok {
if h.md.cipher != nil {
pc = h.md.cipher.PacketConn(pc)
}
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr())
h.relayPacket(
ss.UDPServerConn(pc, conn.RemoteAddr(), h.md.bufferSize),
cc,
)
h.logger.
WithFields(map[string]interface{}{"duration": time.Since(t)}).
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
func (h *ssHandler) handleUDPTun(ctx context.Context, conn net.Conn) {
// obtain a udp connection
r := (&handler.Router{}).
WithChain(h.chain).
WithRetry(h.md.retryCount).
WithLogger(h.logger)
c, err := r.Dial(ctx, "udp", "")
if err != nil {
h.logger.Error(err)
return
}
if h.md.cipher != nil {
conn = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil)
cc, ok := c.(net.PacketConn)
if !ok {
h.logger.Errorf("%s: not a packet connection")
return
}
defer cc.Close()
h.logger = h.logger.WithFields(map[string]interface{}{
"bind": cc.LocalAddr().String(),
})
h.logger.Infof("bind on %s OK", cc.LocalAddr().String())
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr())
@ -112,7 +81,7 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn) {
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
}
func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn) (err error) {
func (h *ssHandler) relayPacket(pc1, pc2 net.PacketConn) (err error) {
bufSize := h.md.bufferSize
errc := make(chan error, 2)
@ -183,7 +152,7 @@ func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn) (err error) {
return <-errc
}
func (h *ssuHandler) tunnelUDP(tunnel, c net.PacketConn) (err error) {
func (h *ssHandler) tunnelUDP(tunnel, c net.PacketConn) (err error) {
bufSize := h.md.bufferSize
errc := make(chan error, 2)
@ -255,31 +224,3 @@ func (h *ssuHandler) tunnelUDP(tunnel, c net.PacketConn) (err error) {
return <-errc
}
func (h *ssuHandler) parseMetadata(md md.Metadata) (err error) {
h.md.cipher, err = ss.ShadowCipher(
md.GetString(method),
md.GetString(password),
md.GetString(key),
)
if err != nil {
return
}
h.md.readTimeout = md.GetDuration(readTimeout)
h.md.retryCount = md.GetInt(retryCount)
h.md.bufferSize = md.GetInt(bufferSize)
if h.md.bufferSize > 0 {
if h.md.bufferSize < 512 {
h.md.bufferSize = 512 // min buffer size
}
if h.md.bufferSize > 65*1024 {
h.md.bufferSize = 65 * 1024 // max buffer size
}
} else {
h.md.bufferSize = 4096 // default buffer size
}
return
}

View File

@ -1,23 +0,0 @@
package ssu
import (
"time"
"github.com/shadowsocks/go-shadowsocks2/core"
)
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
retryCount = "retry"
bufferSize = "bufferSize"
)
type metadata struct {
cipher core.Cipher
readTimeout time.Duration
retryCount int
bufferSize int
}

View File

@ -1,7 +1,9 @@
package handler
import (
"bufio"
"io"
"net"
"github.com/go-gost/gost/pkg/internal/bufpool"
)
@ -30,3 +32,19 @@ func copyBuffer(dst io.Writer, src io.Reader) error {
_, err := io.CopyBuffer(dst, src, buf)
return err
}
type bufferReaderConn struct {
net.Conn
br *bufio.Reader
}
func NewBufferReaderConn(conn net.Conn, br *bufio.Reader) net.Conn {
return &bufferReaderConn{
Conn: conn,
br: br,
}
}
func (c *bufferReaderConn) Read(b []byte) (int, error) {
return c.br.Read(b)
}