separate ssu from ss

This commit is contained in:
ginuerzh
2021-11-19 15:48:03 +08:00
parent 8185d1124f
commit c5df25e84d
11 changed files with 361 additions and 556 deletions

View File

@ -1,7 +1,6 @@
package ss
import (
"bufio"
"context"
"io"
"io/ioutil"
@ -62,12 +61,6 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn) {
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
// standard UDP relay.
if pc, ok := conn.(net.PacketConn); ok {
h.handleUDP(ctx, pc, conn.RemoteAddr())
return
}
if h.md.cipher != nil {
conn = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil)
}
@ -76,25 +69,8 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn) {
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
}
br := bufio.NewReader(conn)
data, err := br.Peek(3)
conn.SetReadDeadline(time.Time{})
if err != nil {
h.logger.Error(err)
h.discard(conn)
return
}
conn = handler.NewBufferReaderConn(conn, br)
if data[2] == 0xff {
// UDP-over-TCP relay
h.handleUDPTun(ctx, conn)
return
}
// standard TCP.
addr := &gosocks5.Addr{}
if _, err = addr.ReadFrom(conn); err != nil {
if _, err := addr.ReadFrom(conn); err != nil {
h.logger.Error(err)
h.discard(conn)
return

View File

@ -12,8 +12,6 @@ type metadata struct {
cipher core.Cipher
readTimeout time.Duration
retryCount int
bufferSize int
enableUDP bool
}
func (h *ssHandler) parseMetadata(md md.Metadata) (err error) {
@ -23,8 +21,6 @@ func (h *ssHandler) parseMetadata(md md.Metadata) (err error) {
key = "key"
readTimeout = "readTimeout"
retryCount = "retry"
enableUDP = "udp"
bufferSize = "bufferSize"
)
h.md.cipher, err = ss.ShadowCipher(
@ -38,18 +34,6 @@ func (h *ssHandler) parseMetadata(md md.Metadata) (err error) {
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,236 +0,0 @@
package ss
import (
"context"
"net"
"time"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/common/bufpool"
"github.com/go-gost/gost/pkg/common/util/socks"
"github.com/go-gost/gost/pkg/common/util/ss"
)
func (h *ssHandler) handleUDP(ctx context.Context, conn net.PacketConn, raddr net.Addr) {
if !h.md.enableUDP {
h.logger.Error("UDP relay is diabled")
return
}
if h.md.cipher != nil {
conn = h.md.cipher.PacketConn(conn)
}
// obtain a udp connection
r := (&chain.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
}
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", 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())
}
func (h *ssHandler) handleUDPTun(ctx context.Context, conn net.Conn) {
if !h.md.enableUDP {
h.logger.Error("UDP relay is diabled")
return
}
// obtain a udp connection
r := (&chain.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
}
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())
h.tunnelUDP(socks.UDPTunServerConn(conn), cc)
h.logger.
WithFields(map[string]interface{}{"duration": time.Since(t)}).
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
}
func (h *ssHandler) relayPacket(pc1, pc2 net.PacketConn) (err error) {
bufSize := h.md.bufferSize
errc := make(chan error, 2)
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, addr, err := pc1.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(addr.String()) {
h.logger.Warn("bypass: ", addr)
return nil
}
if _, err = pc2.WriteTo(b[:n], addr); err != nil {
return err
}
h.logger.Debugf("%s >>> %s data: %d",
pc2.LocalAddr(), addr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, raddr, err := pc2.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(raddr.String()) {
h.logger.Warn("bypass: ", raddr)
return nil
}
if _, err = pc1.WriteTo(b[:n], raddr); err != nil {
return err
}
h.logger.Debugf("%s <<< %s data: %d",
pc2.LocalAddr(), raddr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
return <-errc
}
func (h *ssHandler) tunnelUDP(tunnel, c net.PacketConn) (err error) {
bufSize := h.md.bufferSize
errc := make(chan error, 2)
go func() {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
for {
err := func() error {
n, addr, err := tunnel.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(addr.String()) {
h.logger.Warn("bypass: ", addr.String())
return nil // bypass
}
if _, err := c.WriteTo(b[:n], addr); err != nil {
return err
}
h.logger.Debugf("%s >>> %s data: %d",
c.LocalAddr(), addr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
for {
err := func() error {
n, raddr, err := c.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(raddr.String()) {
h.logger.Warn("bypass: ", raddr.String())
return nil // bypass
}
if _, err := tunnel.WriteTo(b[:n], raddr); err != nil {
return err
}
h.logger.Debugf("%s <<< %s data: %d",
c.LocalAddr(), raddr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
return <-errc
}

View File

@ -0,0 +1,173 @@
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/common/bufpool"
"github.com/go-gost/gost/pkg/common/util/socks"
"github.com/go-gost/gost/pkg/common/util/ss"
"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("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)
}
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())
}()
pc, ok := conn.(net.PacketConn)
if ok {
if h.md.cipher != nil {
pc = h.md.cipher.PacketConn(pc)
}
// standard UDP relay.
pc = ss.UDPServerConn(pc, conn.RemoteAddr(), h.md.bufferSize)
} else {
if h.md.cipher != nil {
conn = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil)
}
// UDP over TCP
pc = socks.UDPTunServerConn(conn)
}
// obtain a udp connection
r := (&chain.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
}
cc, ok := c.(net.PacketConn)
if !ok {
h.logger.Errorf("%s: not a packet connection")
return
}
defer cc.Close()
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr())
h.relayPacket(pc, cc)
h.logger.
WithFields(map[string]interface{}{"duration": time.Since(t)}).
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
}
func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn) (err error) {
bufSize := h.md.bufferSize
errc := make(chan error, 2)
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, addr, err := pc1.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(addr.String()) {
h.logger.Warn("bypass: ", addr)
return nil
}
if _, err = pc2.WriteTo(b[:n], addr); err != nil {
return err
}
h.logger.Debugf("%s >>> %s data: %d",
pc2.LocalAddr(), addr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, raddr, err := pc2.ReadFrom(b)
if err != nil {
return err
}
if h.bypass != nil && h.bypass.Contains(raddr.String()) {
h.logger.Warn("bypass: ", raddr)
return nil
}
if _, err = pc1.WriteTo(b[:n], raddr); err != nil {
return err
}
h.logger.Debugf("%s <<< %s data: %d",
pc2.LocalAddr(), raddr, n)
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
return <-errc
}

View File

@ -0,0 +1,52 @@
package ss
import (
"time"
"github.com/go-gost/gost/pkg/common/util/ss"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/shadowsocks/go-shadowsocks2/core"
)
type metadata struct {
cipher core.Cipher
readTimeout time.Duration
retryCount int
bufferSize int
}
func (h *ssuHandler) parseMetadata(md md.Metadata) (err error) {
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
retryCount = "retry"
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.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
}