merge ss and ssu
This commit is contained in:
@ -12,14 +12,14 @@ type Node struct {
|
||||
addr string
|
||||
transport *Transport
|
||||
bypass bypass.Bypass
|
||||
marker *failMarker
|
||||
marker *FailMarker
|
||||
}
|
||||
|
||||
func NewNode(name, addr string) *Node {
|
||||
return &Node{
|
||||
name: name,
|
||||
addr: addr,
|
||||
marker: &failMarker{},
|
||||
marker: &FailMarker{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,10 @@ func (node *Node) Addr() string {
|
||||
return node.addr
|
||||
}
|
||||
|
||||
func (node *Node) Marker() *FailMarker {
|
||||
return node.marker
|
||||
}
|
||||
|
||||
func (node *Node) WithTransport(tr *Transport) *Node {
|
||||
node.transport = tr
|
||||
return node
|
||||
@ -80,13 +84,13 @@ func (g *NodeGroup) Next() *Node {
|
||||
return selector.Select(g.nodes...)
|
||||
}
|
||||
|
||||
type failMarker struct {
|
||||
type FailMarker struct {
|
||||
failTime int64
|
||||
failCount uint32
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
func (m *failMarker) FailTime() int64 {
|
||||
func (m *FailMarker) FailTime() int64 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
@ -97,7 +101,7 @@ func (m *failMarker) FailTime() int64 {
|
||||
return m.failTime
|
||||
}
|
||||
|
||||
func (m *failMarker) FailCount() uint32 {
|
||||
func (m *FailMarker) FailCount() uint32 {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
@ -108,7 +112,7 @@ func (m *failMarker) FailCount() uint32 {
|
||||
return m.failCount
|
||||
}
|
||||
|
||||
func (m *failMarker) Mark() {
|
||||
func (m *FailMarker) Mark() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
@ -120,7 +124,7 @@ func (m *failMarker) Mark() {
|
||||
m.failCount++
|
||||
}
|
||||
|
||||
func (m *failMarker) Reset() {
|
||||
func (m *FailMarker) Reset() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
@ -26,33 +26,33 @@ func (r *Route) Connect(ctx context.Context) (conn net.Conn, err error) {
|
||||
node := r.nodes[0]
|
||||
cc, err := node.transport.Dial(ctx, r.nodes[0].Addr())
|
||||
if err != nil {
|
||||
node.marker.Mark()
|
||||
node.Marker().Mark()
|
||||
return
|
||||
}
|
||||
|
||||
cn, err := node.transport.Handshake(ctx, cc)
|
||||
if err != nil {
|
||||
cc.Close()
|
||||
node.marker.Mark()
|
||||
node.Marker().Mark()
|
||||
return
|
||||
}
|
||||
node.marker.Reset()
|
||||
node.Marker().Reset()
|
||||
|
||||
preNode := node
|
||||
for _, node := range r.nodes[1:] {
|
||||
cc, err = preNode.transport.Connect(ctx, cn, "tcp", node.Addr())
|
||||
if err != nil {
|
||||
cn.Close()
|
||||
node.marker.Mark()
|
||||
node.Marker().Mark()
|
||||
return
|
||||
}
|
||||
cc, err = node.transport.Handshake(ctx, cc)
|
||||
if err != nil {
|
||||
cn.Close()
|
||||
node.marker.Mark()
|
||||
node.Marker().Mark()
|
||||
return
|
||||
}
|
||||
node.marker.Reset()
|
||||
node.Marker().Reset()
|
||||
|
||||
cn = cc
|
||||
preNode = node
|
||||
@ -89,7 +89,7 @@ func (r *Route) dialDirect(ctx context.Context, network, address string) (net.Co
|
||||
default:
|
||||
}
|
||||
|
||||
d := &net.Dialer{}
|
||||
d := net.Dialer{}
|
||||
return d.DialContext(ctx, network, address)
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,6 @@ const (
|
||||
DefaultFailTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
defaultSelector Selector = NewSelector(nil)
|
||||
)
|
||||
|
||||
type Selector interface {
|
||||
Select(nodes ...*Node) *Node
|
||||
}
|
||||
@ -145,8 +141,8 @@ func (f *failFilter) Filter(nodes ...*Node) []*Node {
|
||||
}
|
||||
var nl []*Node
|
||||
for _, node := range nodes {
|
||||
if node.marker.FailCount() < uint32(maxFails) ||
|
||||
time.Since(time.Unix(node.marker.FailTime(), 0)) >= failTimeout {
|
||||
if node.Marker().FailCount() < uint32(maxFails) ||
|
||||
time.Since(time.Unix(node.Marker().FailTime(), 0)) >= failTimeout {
|
||||
nl = append(nl, node)
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type ProfilingConfig struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type LoadbalancingConfig struct {
|
||||
type SelectorConfig struct {
|
||||
Strategy string
|
||||
MaxFails int
|
||||
FailTimeout time.Duration
|
||||
@ -50,6 +50,11 @@ type HandlerConfig struct {
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type ForwarderConfig struct {
|
||||
Targets []string
|
||||
Selector *SelectorConfig
|
||||
}
|
||||
|
||||
type DialerConfig struct {
|
||||
Type string
|
||||
Metadata map[string]interface{}
|
||||
@ -61,25 +66,26 @@ type ConnectorConfig struct {
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
Name string
|
||||
URL string
|
||||
Addr string
|
||||
Listener *ListenerConfig
|
||||
Handler *HandlerConfig
|
||||
Chain string
|
||||
Bypass string
|
||||
Name string
|
||||
URL string
|
||||
Addr string
|
||||
Listener *ListenerConfig
|
||||
Handler *HandlerConfig
|
||||
Forwarder *ForwarderConfig
|
||||
Chain string
|
||||
Bypass string
|
||||
}
|
||||
|
||||
type ChainConfig struct {
|
||||
Name string
|
||||
LB *LoadbalancingConfig
|
||||
Hops []HopConfig
|
||||
Name string
|
||||
Selector *SelectorConfig
|
||||
Hops []HopConfig
|
||||
}
|
||||
|
||||
type HopConfig struct {
|
||||
Name string
|
||||
LB *LoadbalancingConfig
|
||||
Nodes []NodeConfig
|
||||
Name string
|
||||
Selector *SelectorConfig
|
||||
Nodes []NodeConfig
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
|
@ -49,6 +49,7 @@ func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
"network": network,
|
||||
"address": address,
|
||||
})
|
||||
c.logger.Infof("connect: %s/%s", address, network)
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
@ -71,8 +72,6 @@ func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
}
|
||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||
|
||||
c.logger.Infof("connect: ", address)
|
||||
|
||||
if user := c.md.User; user != nil {
|
||||
u := user.Username()
|
||||
p, _ := user.Password()
|
||||
|
@ -2,6 +2,7 @@ package v4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
@ -47,6 +48,7 @@ func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
"network": network,
|
||||
"address": address,
|
||||
})
|
||||
c.logger.Infof("connect: %s/%s", address, network)
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
@ -56,8 +58,6 @@ func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.logger.Info("connect: ", address)
|
||||
|
||||
var addr *gosocks4.Addr
|
||||
|
||||
if c.md.disable4a {
|
||||
@ -107,7 +107,9 @@ func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
c.logger.Debug(reply)
|
||||
|
||||
if reply.Code != gosocks4.Granted {
|
||||
return nil, fmt.Errorf("error: %d", reply.Code)
|
||||
err = errors.New("host unreachable")
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
|
@ -67,6 +67,7 @@ func (c *socks5Connector) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handshake implements connector.Handshaker.
|
||||
func (c *socks5Connector) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
||||
c.logger = c.logger.WithFields(map[string]interface{}{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
@ -92,17 +93,18 @@ func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
"network": network,
|
||||
"address": address,
|
||||
})
|
||||
c.logger.Infof("connect: %s/%s", address, network)
|
||||
|
||||
switch network {
|
||||
case "udp", "udp4", "udp6":
|
||||
return c.connectUDP(ctx, conn, network, address)
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
default:
|
||||
err := fmt.Errorf("network %s unsupported, should be tcp, tcp4 or tcp6", network)
|
||||
err := fmt.Errorf("network %s unsupported", network)
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.logger.Info("connect: ", address)
|
||||
|
||||
addr := gosocks5.Addr{}
|
||||
if err := addr.ParseFrom(address); err != nil {
|
||||
c.logger.Error(err)
|
||||
@ -129,12 +131,48 @@ func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
c.logger.Debug(reply)
|
||||
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return nil, errors.New("service unavailable")
|
||||
err = errors.New("host unreachable")
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *socks5Connector) connectUDP(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
|
||||
addr, err := net.ResolveUDPAddr(network, address)
|
||||
if err != nil {
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := gosocks5.NewRequest(socks.CmdUDPTun, nil)
|
||||
if err := req.Write(conn); err != nil {
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
c.logger.Debug(req)
|
||||
|
||||
reply, err := gosocks5.ReadReply(conn)
|
||||
if err != nil {
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
c.logger.Debug(reply)
|
||||
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return nil, errors.New("get socks5 UDP tunnel failure")
|
||||
}
|
||||
|
||||
baddr, err := net.ResolveUDPAddr("udp", reply.Addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.logger.Debugf("associate on %s OK", baddr)
|
||||
|
||||
return socks.UDPTunClientConn(conn, addr), nil
|
||||
}
|
||||
|
||||
func (c *socks5Connector) parseMetadata(md md.Metadata) (err error) {
|
||||
if v := md.GetString(auth); v != "" {
|
||||
ss := strings.SplitN(v, ":", 2)
|
||||
|
@ -2,6 +2,7 @@ package ss
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
"github.com/go-gost/gosocks5"
|
||||
"github.com/go-gost/gost/pkg/connector"
|
||||
"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"
|
||||
@ -46,15 +48,23 @@ func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, addre
|
||||
"network": network,
|
||||
"address": address,
|
||||
})
|
||||
c.logger.Infof("connect: %s/%s", address, network)
|
||||
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
case "udp", "udp4", "udp6":
|
||||
if c.md.enableUDP {
|
||||
return c.connectUDP(ctx, conn, network, address)
|
||||
} else {
|
||||
err := errors.New("UDP relay is disabled")
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("network %s unsupported, should be tcp, tcp4 or tcp6", network)
|
||||
err := fmt.Errorf("network %s unsupported", network)
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
c.logger.Infof("connect: ", address)
|
||||
|
||||
addr := gosocks5.Addr{}
|
||||
if err := addr.ParseFrom(address); err != nil {
|
||||
@ -94,18 +104,28 @@ func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, addre
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
func (c *ssConnector) parseMetadata(md md.Metadata) (err error) {
|
||||
c.md.cipher, err = ss.ShadowCipher(
|
||||
md.GetString(method),
|
||||
md.GetString(password),
|
||||
md.GetString(key),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
func (c *ssConnector) connectUDP(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
|
||||
if c.md.connectTimeout > 0 {
|
||||
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
c.md.connectTimeout = md.GetDuration(connectTimeout)
|
||||
c.md.noDelay = md.GetBool(noDelay)
|
||||
taddr, _ := net.ResolveUDPAddr(network, address)
|
||||
if taddr == nil {
|
||||
taddr = &net.UDPAddr{}
|
||||
}
|
||||
|
||||
return
|
||||
pc, ok := conn.(net.PacketConn)
|
||||
if ok {
|
||||
if c.md.cipher != nil {
|
||||
pc = c.md.cipher.PacketConn(pc)
|
||||
}
|
||||
|
||||
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.udpBufferSize), nil
|
||||
}
|
||||
|
||||
if c.md.cipher != nil {
|
||||
conn = ss.ShadowConn(c.md.cipher.StreamConn(conn), nil)
|
||||
}
|
||||
return socks.UDPTunClientConn(conn, taddr), nil
|
||||
}
|
||||
|
@ -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"
|
||||
connectTimeout = "timeout"
|
||||
noDelay = "noDelay"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
cipher core.Cipher
|
||||
connectTimeout time.Duration
|
||||
noDelay bool
|
||||
enableUDP bool
|
||||
udpBufferSize int
|
||||
}
|
||||
|
||||
func (c *ssConnector) parseMetadata(md md.Metadata) (err error) {
|
||||
const (
|
||||
method = "method"
|
||||
password = "password"
|
||||
key = "key"
|
||||
connectTimeout = "timeout"
|
||||
noDelay = "noDelay"
|
||||
enableUDP = "udp" // enable UDP relay
|
||||
udpBufferSize = "udpBufferSize" // udp buffer size
|
||||
)
|
||||
|
||||
c.md.cipher, err = ss.ShadowCipher(
|
||||
md.GetString(method),
|
||||
md.GetString(password),
|
||||
md.GetString(key),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.md.connectTimeout = md.GetDuration(connectTimeout)
|
||||
c.md.noDelay = md.GetBool(noDelay)
|
||||
c.md.enableUDP = md.GetBool(enableUDP)
|
||||
|
||||
if c.md.udpBufferSize > 0 {
|
||||
if c.md.udpBufferSize < 512 {
|
||||
c.md.udpBufferSize = 512
|
||||
}
|
||||
if c.md.udpBufferSize > 65*1024 {
|
||||
c.md.udpBufferSize = 65 * 1024
|
||||
}
|
||||
} else {
|
||||
c.md.udpBufferSize = 4096
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1,105 +0,0 @@
|
||||
package ssu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/pkg/connector"
|
||||
"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.RegiserConnector("ssu", NewConnector)
|
||||
}
|
||||
|
||||
type ssuConnector struct {
|
||||
md metadata
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
options := &connector.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
return &ssuConnector{
|
||||
logger: options.Logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ssuConnector) Init(md md.Metadata) (err error) {
|
||||
return c.parseMetadata(md)
|
||||
}
|
||||
|
||||
func (c *ssuConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
c.logger = c.logger.WithFields(map[string]interface{}{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
})
|
||||
|
||||
switch network {
|
||||
case "udp", "udp4", "udp6":
|
||||
default:
|
||||
err := fmt.Errorf("network %s unsupported, should be udp, udp4 or udp6", network)
|
||||
c.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.logger.Info("connect: ", address)
|
||||
|
||||
if c.md.connectTimeout > 0 {
|
||||
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
taddr, _ := net.ResolveUDPAddr(network, address)
|
||||
if taddr == nil {
|
||||
taddr = &net.UDPAddr{}
|
||||
}
|
||||
|
||||
pc, ok := conn.(net.PacketConn)
|
||||
if ok {
|
||||
if c.md.cipher != nil {
|
||||
pc = c.md.cipher.PacketConn(pc)
|
||||
}
|
||||
|
||||
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.bufferSize), nil
|
||||
}
|
||||
|
||||
return socks.UDPTunClientConn(conn, taddr), nil
|
||||
}
|
||||
|
||||
func (c *ssuConnector) parseMetadata(md md.Metadata) (err error) {
|
||||
c.md.cipher, err = ss.ShadowCipher(
|
||||
md.GetString(method),
|
||||
md.GetString(password),
|
||||
md.GetString(key),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.md.connectTimeout = md.GetDuration(connectTimeout)
|
||||
c.md.bufferSize = md.GetInt(bufferSize)
|
||||
if c.md.bufferSize > 0 {
|
||||
if c.md.bufferSize < 512 {
|
||||
c.md.bufferSize = 512
|
||||
}
|
||||
if c.md.bufferSize > 65*1024 {
|
||||
c.md.bufferSize = 65 * 1024
|
||||
}
|
||||
} else {
|
||||
c.md.bufferSize = 4096
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package ssu
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
const (
|
||||
method = "method"
|
||||
password = "password"
|
||||
key = "key"
|
||||
connectTimeout = "timeout"
|
||||
bufferSize = "bufferSize"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
cipher core.Cipher
|
||||
connectTimeout time.Duration
|
||||
bufferSize int
|
||||
}
|
113
pkg/handler/forward/local/handler.go
Normal file
113
pkg/handler/forward/local/handler.go
Normal 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
|
||||
}
|
15
pkg/handler/forward/local/metadata.go
Normal file
15
pkg/handler/forward/local/metadata.go
Normal file
@ -0,0 +1,15 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
readTimeout = "readTimeout"
|
||||
retryCount = "retry"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
retryCount int
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ func (c *UDPTunConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
Data: b,
|
||||
}
|
||||
dgram.Header.Rsv = uint16(len(dgram.Data))
|
||||
dgram.Header.Frag = 0xff // UDP tun relay flag, used by shadowsocks
|
||||
_, err = dgram.WriteTo(c.Conn)
|
||||
n = len(b)
|
||||
|
||||
|
Reference in New Issue
Block a user