diff --git a/cmd/gost/config.go b/cmd/gost/config.go index 8c9f6c5..729e75b 100644 --- a/cmd/gost/config.go +++ b/cmd/gost/config.go @@ -36,11 +36,14 @@ func buildService(cfg *config.Config) (services []*service.Service) { } for _, svc := range cfg.Services { - listenerLogger := log.WithFields(map[string]interface{}{ - "kind": "listener", - "type": svc.Listener.Type, + serviceLogger := log.WithFields(map[string]interface{}{ "service": svc.Name, }) + + listenerLogger := serviceLogger.WithFields(map[string]interface{}{ + "kind": "listener", + "type": svc.Listener.Type, + }) ln := registry.GetListener(svc.Listener.Type)( listener.AddrOption(svc.Addr), listener.LoggerOption(listenerLogger), @@ -49,10 +52,9 @@ func buildService(cfg *config.Config) (services []*service.Service) { listenerLogger.Fatal("init: ", err) } - handlerLogger := log.WithFields(map[string]interface{}{ - "kind": "handler", - "type": svc.Handler.Type, - "service": svc.Name, + handlerLogger := serviceLogger.WithFields(map[string]interface{}{ + "kind": "handler", + "type": svc.Handler.Type, }) h := registry.GetHandler(svc.Handler.Type)( @@ -66,8 +68,11 @@ func buildService(cfg *config.Config) (services []*service.Service) { s := (&service.Service{}). WithListener(ln). - WithHandler(h) + WithHandler(h). + WithLogger(serviceLogger) services = append(services, s) + + serviceLogger.Info("listening on: ", s.Addr()) } return diff --git a/cmd/gost/gost.yml b/cmd/gost/gost.yml index 830eb3c..079a91d 100644 --- a/cmd/gost/gost.yml +++ b/cmd/gost/gost.yml @@ -43,6 +43,18 @@ services: keepAlive: 15s chain: chain01 # bypass: bypass01 +- name: ssu + url: "ss://chacha20:gost@:8000" + addr: ":8338" + handler: + type: ssu + metadata: + # method: AES-256-GCM + # password: gost + readTimeout: 5s + retry: 3 + listener: + type: udp - name: socks5+tcp url: "socks5://gost:gost@:1080" addr: ":1080" diff --git a/go.mod b/go.mod index 12ba956..ba7e0c6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/coreos/go-iptables v0.5.0 // indirect github.com/ginuerzh/tls-dissector v0.0.2-0.20201202075250-98fa925912da github.com/go-gost/gosocks4 v0.0.1 - github.com/go-gost/gosocks5 v0.3.1-0.20211108032632-bbfd2de9a32d + github.com/go-gost/gosocks5 v0.3.1-0.20211108125245-019dfd6b3aea github.com/gobwas/glob v0.2.3 github.com/golang/snappy v0.0.3 github.com/google/gopacket v1.1.19 // indirect diff --git a/go.sum b/go.sum index 43dda52..03953c5 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/go-gost/gosocks5 v0.3.1-0.20211107153135-23b5baedc2aa h1:4yBKO6CPj5Lo github.com/go-gost/gosocks5 v0.3.1-0.20211107153135-23b5baedc2aa/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4= github.com/go-gost/gosocks5 v0.3.1-0.20211108032632-bbfd2de9a32d h1:mjoFToMUWNN06IwOyXOk9bEsev3T5RUoC9n4Xt7ZDkg= github.com/go-gost/gosocks5 v0.3.1-0.20211108032632-bbfd2de9a32d/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4= +github.com/go-gost/gosocks5 v0.3.1-0.20211108125245-019dfd6b3aea h1:mrm6bMpdxBvInvBuDbUaAQWV60r/PaByLIG9fQJEEIc= +github.com/go-gost/gosocks5 v0.3.1-0.20211108125245-019dfd6b3aea/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= diff --git a/pkg/handler/socks/v5/udp.go b/pkg/handler/socks/v5/udp.go index 18933d5..c67cf39 100644 --- a/pkg/handler/socks/v5/udp.go +++ b/pkg/handler/socks/v5/udp.go @@ -323,7 +323,6 @@ func (h *socks5Handler) relayUDP(c, peer net.PacketConn) (err error) { if socksAddr == nil { socksAddr = &gosocks5.Addr{} } - socksAddr.Type = 0 addrLen := socksAddr.Length() socksAddr.Encode(b[dataPos-addrLen : dataPos]) diff --git a/pkg/handler/socks/v5/udp_tun.go b/pkg/handler/socks/v5/udp_tun.go index 01da76e..449ce96 100644 --- a/pkg/handler/socks/v5/udp_tun.go +++ b/pkg/handler/socks/v5/udp_tun.go @@ -165,7 +165,6 @@ func (h *socks5Handler) tunnelServerUDP(tunnel net.Conn, c net.PacketConn) (err if addr == nil { addr = &gosocks5.Addr{} } - addr.Type = 0 header := gosocks5.UDPHeader{ Rsv: uint16(n), Addr: addr, diff --git a/pkg/handler/ss/handler.go b/pkg/handler/ss/handler.go index a92e4d0..21bedc3 100644 --- a/pkg/handler/ss/handler.go +++ b/pkg/handler/ss/handler.go @@ -49,7 +49,6 @@ func (h *ssHandler) 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(), diff --git a/pkg/handler/ssu/handler.go b/pkg/handler/ssu/handler.go index 0826965..93e8146 100644 --- a/pkg/handler/ssu/handler.go +++ b/pkg/handler/ssu/handler.go @@ -1,18 +1,20 @@ package ssu import ( + "bytes" "context" "net" "time" + "github.com/go-gost/gosocks5" "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/ss" "github.com/go-gost/gost/pkg/logger" md "github.com/go-gost/gost/pkg/metadata" "github.com/go-gost/gost/pkg/registry" - "github.com/shadowsocks/go-shadowsocks2/core" - ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) func init() { @@ -47,12 +49,10 @@ 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{}{ @@ -60,10 +60,257 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn) { }).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr()) }() + // 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 + } + + 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()) + + 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(pc, cc) + h.logger. + WithFields(map[string]interface{}{"duration": time.Since(t)}). + Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr()) + return + } + + if h.md.cipher != nil { + conn = ss.ShadowConn(h.md.cipher.StreamConn(conn), nil) + } + + t := time.Now() + h.logger.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr()) + h.tunnelUDP(conn, 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) + var clientAddr net.Addr + + go func() { + b := bufpool.Get(bufSize) + defer bufpool.Put(b) + + for { + err := func() error { + n, addr, err := pc1.ReadFrom(b) + if err != nil { + return err + } + if clientAddr == nil { + clientAddr = addr + } + + rb := bytes.NewBuffer(b[:n]) + saddr := gosocks5.Addr{} + if _, err := saddr.ReadFrom(rb); err != nil { + return err + } + taddr, err := net.ResolveUDPAddr("udp", saddr.String()) + if err != nil { + return err + } + + if h.bypass != nil && h.bypass.Contains(taddr.String()) { + h.logger.Warn("bypass: ", taddr) + return nil + } + + if _, err = pc2.WriteTo(rb.Bytes(), taddr); err != nil { + return err + } + + if h.logger.IsLevelEnabled(logger.DebugLevel) { + h.logger.Debugf("%s >>> %s: %v, data: %d", + addr, taddr, saddr.String(), rb.Len()) + } + return nil + }() + + if err != nil { + errc <- err + return + } + } + }() + + go func() { + b := bufpool.Get(bufSize) + defer bufpool.Put(b) + + const dataPos = 259 + + for { + err := func() error { + n, raddr, err := pc2.ReadFrom(b[dataPos:]) + if err != nil { + return err + } + if clientAddr == nil { + return nil + } + + if h.bypass != nil && h.bypass.Contains(raddr.String()) { + h.logger.Warn("bypass: ", raddr) + return nil + } + + socksAddr, _ := gosocks5.NewAddr(raddr.String()) + if socksAddr == nil { + socksAddr = &gosocks5.Addr{} + } + addrLen := socksAddr.Length() + socksAddr.Encode(b[dataPos-addrLen : dataPos]) + + if _, err = pc1.WriteTo(b[dataPos-addrLen:dataPos+n], clientAddr); err != nil { + return err + } + + if h.logger.IsLevelEnabled(logger.DebugLevel) { + h.logger.Debugf("%s <<< %s: %v data: %d", + clientAddr, raddr, b[dataPos-addrLen:dataPos], n) + } + return nil + }() + + if err != nil { + errc <- err + return + } + } + }() + + return <-errc +} + +func (h *ssuHandler) tunnelUDP(tunnel net.Conn, c net.PacketConn) (err error) { + bufSize := h.md.bufferSize + errc := make(chan error, 2) + + go func() { + b := bufpool.Get(bufSize) + defer bufpool.Put(b) + + const dataPos = 262 + + for { + addr := gosocks5.Addr{} + header := gosocks5.UDPHeader{ + Addr: &addr, + } + + data := b[dataPos:] + dgram := gosocks5.UDPDatagram{ + Header: &header, + Data: data, + } + _, err := dgram.ReadFrom(tunnel) + if err != nil { + errc <- err + return + } + // NOTE: the dgram.Data may be reallocated if the provided buffer is too short, + // we drop it for simplicity. As this occurs, you should enlarge the buffer size. + if len(dgram.Data) > len(data) { + h.logger.Warnf("buffer too short, dropped") + continue + } + + raddr, err := net.ResolveUDPAddr("udp", addr.String()) + if err != nil { + continue // drop silently + } + if h.bypass != nil && h.bypass.Contains(raddr.String()) { + h.logger.Warn("bypass: ", raddr.String()) + continue // bypass + } + + if _, err := c.WriteTo(dgram.Data, raddr); err != nil { + errc <- err + return + } + + if h.logger.IsLevelEnabled(logger.DebugLevel) { + h.logger.Debugf("%s >>> %s: %v data: %d", + tunnel.RemoteAddr(), raddr, header.String(), len(dgram.Data)) + } + } + }() + + go func() { + b := bufpool.Get(bufSize) + defer bufpool.Put(b) + + for { + n, raddr, err := c.ReadFrom(b) + if err != nil { + errc <- err + return + } + + if h.bypass != nil && h.bypass.Contains(raddr.String()) { + h.logger.Warn("bypass: ", raddr.String()) + continue // bypass + } + + addr, _ := gosocks5.NewAddr(raddr.String()) + if addr == nil { + addr = &gosocks5.Addr{} + } + header := gosocks5.UDPHeader{ + Rsv: uint16(n), + Addr: addr, + } + dgram := gosocks5.UDPDatagram{ + Header: &header, + Data: b[:n], + } + + if _, err := dgram.WriteTo(tunnel); err != nil { + errc <- err + return + } + if h.logger.IsLevelEnabled(logger.DebugLevel) { + h.logger.Debugf("%s <<< %s: %v data: %d", + tunnel.RemoteAddr(), raddr, header.String(), len(dgram.Data)) + } + } + }() + + return <-errc } func (h *ssuHandler) parseMetadata(md md.Metadata) (err error) { - h.md.cipher, err = h.initCipher( + h.md.cipher, err = ss.ShadowCipher( md.GetString(method), md.GetString(password), md.GetString(key), @@ -75,30 +322,17 @@ func (h *ssuHandler) parseMetadata(md md.Metadata) (err error) { 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 } - -func (h *ssuHandler) initCipher(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) -} - -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()) -} diff --git a/pkg/handler/ssu/metadata.go b/pkg/handler/ssu/metadata.go index 89412af..c8f1df2 100644 --- a/pkg/handler/ssu/metadata.go +++ b/pkg/handler/ssu/metadata.go @@ -12,10 +12,12 @@ const ( key = "key" readTimeout = "readTimeout" retryCount = "retry" + bufferSize = "bufferSize" ) type metadata struct { cipher core.Cipher readTimeout time.Duration retryCount int + bufferSize int } diff --git a/pkg/listener/tcp/listener.go b/pkg/listener/tcp/listener.go index 006ea0c..bf548fd 100644 --- a/pkg/listener/tcp/listener.go +++ b/pkg/listener/tcp/listener.go @@ -55,7 +55,6 @@ func (l *tcpListener) Init(md md.Metadata) (err error) { } l.Listener = ln - l.logger.Info("listening on:", l.Listener.Addr()) return } diff --git a/pkg/listener/udp/listener.go b/pkg/listener/udp/listener.go index e41466c..f5995a1 100644 --- a/pkg/listener/udp/listener.go +++ b/pkg/listener/udp/listener.go @@ -126,6 +126,25 @@ func (l *udpListener) listenLoop() { } func (l *udpListener) parseMetadata(md md.Metadata) (err error) { + l.md.ttl = md.GetDuration(ttl) + if l.md.ttl <= 0 { + l.md.ttl = defaultTTL + } + l.md.readBufferSize = md.GetInt(readBufferSize) + if l.md.readBufferSize <= 0 { + l.md.readBufferSize = defaultReadBufferSize + } + + l.md.readQueueSize = md.GetInt(readQueueSize) + if l.md.readQueueSize <= 0 { + l.md.readQueueSize = defaultReadQueueSize + } + + l.md.connQueueSize = md.GetInt(connQueueSize) + if l.md.connQueueSize <= 0 { + l.md.connQueueSize = defaultConnQueueSize + } + return } diff --git a/pkg/listener/udp/metadata.go b/pkg/listener/udp/metadata.go index ec01921..76bc478 100644 --- a/pkg/listener/udp/metadata.go +++ b/pkg/listener/udp/metadata.go @@ -9,6 +9,13 @@ const ( defaultConnQueueSize = 128 ) +const ( + ttl = "ttl" + readBufferSize = "readBufferSize" + readQueueSize = "readQueueSize" + connQueueSize = "connQueueSize" +) + type metadata struct { ttl time.Duration diff --git a/pkg/service/service.go b/pkg/service/service.go index 427df87..2e0ef00 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -7,11 +7,13 @@ import ( "github.com/go-gost/gost/pkg/handler" "github.com/go-gost/gost/pkg/listener" + "github.com/go-gost/gost/pkg/logger" ) type Service struct { listener listener.Listener handler handler.Handler + logger logger.Logger } func (s *Service) WithListener(ln listener.Listener) *Service { @@ -24,6 +26,11 @@ func (s *Service) WithHandler(h handler.Handler) *Service { return s } +func (s *Service) WithLogger(logger logger.Logger) *Service { + s.logger = logger + return s +} + func (s *Service) Addr() net.Addr { return s.listener.Addr() } @@ -50,7 +57,7 @@ func (s *Service) serve() error { if max := 1 * time.Second; tempDelay > max { tempDelay = max } - // log.Logf("server: Accept error: %v; retrying in %v", e, tempDelay) + s.logger.Warnf("accept: %v, retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue }