This commit is contained in:
qaq
2025-06-17 12:17:33 +08:00
parent 4a6b2c8e0f
commit 9fa968d07c
648 changed files with 64968 additions and 1621 deletions
+142
View File
@@ -0,0 +1,142 @@
package chain
import (
"context"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
"github.com/go-gost/core/selector"
)
var (
_ chain.Chainer = (*chainGroup)(nil)
)
type ChainOptions struct {
Metadata metadata.Metadata
Logger logger.Logger
}
type ChainOption func(*ChainOptions)
func MetadataChainOption(md metadata.Metadata) ChainOption {
return func(opts *ChainOptions) {
opts.Metadata = md
}
}
func LoggerChainOption(logger logger.Logger) ChainOption {
return func(opts *ChainOptions) {
opts.Logger = logger
}
}
type chainNamer interface {
Name() string
}
type Chain struct {
name string
hops []hop.Hop
marker selector.Marker
metadata metadata.Metadata
logger logger.Logger
}
func NewChain(name string, opts ...ChainOption) *Chain {
var options ChainOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
return &Chain{
name: name,
metadata: options.Metadata,
marker: selector.NewFailMarker(),
logger: options.Logger,
}
}
func (c *Chain) AddHop(hop hop.Hop) {
c.hops = append(c.hops, hop)
}
// Metadata implements metadata.Metadatable interface.
func (c *Chain) Metadata() metadata.Metadata {
return c.metadata
}
// Marker implements selector.Markable interface.
func (c *Chain) Marker() selector.Marker {
return c.marker
}
func (c *Chain) Name() string {
return c.name
}
func (c *Chain) Route(ctx context.Context, network, address string, opts ...chain.RouteOption) chain.Route {
if c == nil || len(c.hops) == 0 {
return nil
}
var options chain.RouteOptions
for _, opt := range opts {
opt(&options)
}
rt := NewRoute(ChainRouteOption(c))
for _, h := range c.hops {
node := h.Select(ctx,
hop.NetworkSelectOption(network),
hop.AddrSelectOption(address),
hop.HostSelectOption(options.Host),
)
if node == nil {
return rt
}
if node.Options().Transport.Multiplex() {
tr := node.Options().Transport.Copy()
tr.Options().Route = rt
node = node.Copy()
node.Options().Transport = tr
rt = NewRoute(ChainRouteOption(c))
}
rt.addNode(node)
}
return rt
}
type chainGroup struct {
chains []chain.Chainer
selector selector.Selector[chain.Chainer]
}
func NewChainGroup(chains ...chain.Chainer) *chainGroup {
return &chainGroup{chains: chains}
}
func (p *chainGroup) WithSelector(s selector.Selector[chain.Chainer]) *chainGroup {
p.selector = s
return p
}
func (p *chainGroup) Route(ctx context.Context, network, address string, opts ...chain.RouteOption) chain.Route {
if chain := p.next(ctx); chain != nil {
return chain.Route(ctx, network, address, opts...)
}
return nil
}
func (p *chainGroup) next(ctx context.Context) chain.Chainer {
if p == nil || len(p.chains) == 0 {
return nil
}
return p.selector.Select(ctx, p.chains...)
}
+312
View File
@@ -0,0 +1,312 @@
package chain
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metrics"
"github.com/go-gost/core/selector"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/dialer"
"github.com/go-gost/x/internal/net/udp"
xmetrics "github.com/go-gost/x/metrics"
)
var (
ErrEmptyRoute = errors.New("empty route")
)
var (
DefaultRoute chain.Route = &defaultRoute{}
)
// defaultRoute is a Route without nodes.
type defaultRoute struct{}
func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
var options chain.DialOptions
for _, opt := range opts {
opt(&options)
}
netd := dialer.Dialer{
Interface: options.Interface,
Netns: options.Netns,
Logger: options.Logger,
}
if options.SockOpts != nil {
netd.Mark = options.SockOpts.Mark
}
return netd.Dial(ctx, network, address)
}
func (*defaultRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) {
var options chain.BindOptions
for _, opt := range opts {
opt(&options)
}
switch network {
case "tcp", "tcp4", "tcp6":
addr, err := net.ResolveTCPAddr(network, address)
if err != nil {
return nil, err
}
return net.ListenTCP(network, addr)
case "udp", "udp4", "udp6":
addr, err := net.ResolveUDPAddr(network, address)
if err != nil {
return nil, err
}
conn, err := net.ListenUDP(network, addr)
if err != nil {
return nil, err
}
logger := logger.Default().WithFields(map[string]any{
"network": network,
"address": address,
})
ln := udp.NewListener(conn, &udp.ListenConfig{
Backlog: options.Backlog,
ReadQueueSize: options.UDPDataQueueSize,
ReadBufferSize: options.UDPDataBufferSize,
TTL: options.UDPConnTTL,
Keepalive: true,
Logger: logger,
})
return ln, err
default:
err := fmt.Errorf("network %s unsupported", network)
return nil, err
}
}
func (r *defaultRoute) Nodes() []*chain.Node {
return nil
}
type RouteOptions struct {
Chain chain.Chainer
}
type RouteOption func(*RouteOptions)
func ChainRouteOption(c chain.Chainer) RouteOption {
return func(o *RouteOptions) {
o.Chain = c
}
}
type chainRoute struct {
nodes []*chain.Node
options RouteOptions
}
func NewRoute(opts ...RouteOption) *chainRoute {
var options RouteOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
return &chainRoute{
options: options,
}
}
func (r *chainRoute) addNode(nodes ...*chain.Node) {
r.nodes = append(r.nodes, nodes...)
}
func (r *chainRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
if len(r.Nodes()) == 0 {
return DefaultRoute.Dial(ctx, network, address, opts...)
}
var options chain.DialOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
conn, err := r.connect(ctx, options.Logger)
if err != nil {
return nil, err
}
cc, err := r.getNode(len(r.Nodes())-1).Options().Transport.Connect(ctx, conn, network, address)
if err != nil {
if conn != nil {
conn.Close()
}
return nil, err
}
return cc, nil
}
func (r *chainRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) {
if len(r.Nodes()) == 0 {
return DefaultRoute.Bind(ctx, network, address, opts...)
}
var options chain.BindOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
conn, err := r.connect(ctx, options.Logger)
if err != nil {
return nil, err
}
ln, err := r.getNode(len(r.Nodes())-1).Options().Transport.Bind(ctx,
conn, network, address,
connector.BacklogBindOption(options.Backlog),
connector.MuxBindOption(options.Mux),
connector.UDPConnTTLBindOption(options.UDPConnTTL),
connector.UDPDataBufferSizeBindOption(options.UDPDataBufferSize),
connector.UDPDataQueueSizeBindOption(options.UDPDataQueueSize),
)
if err != nil {
conn.Close()
return nil, err
}
return ln, nil
}
func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn net.Conn, err error) {
network := "ip"
node := r.nodes[0]
defer func() {
if r.options.Chain != nil {
var marker selector.Marker
if m, ok := r.options.Chain.(selector.Markable); ok && m != nil {
marker = m.Marker()
}
var name string
if cn, _ := r.options.Chain.(chainNamer); cn != nil {
name = cn.Name()
}
// chain error
if err != nil {
if marker != nil {
marker.Mark()
}
if v := xmetrics.GetCounter(xmetrics.MetricChainErrorsCounter,
metrics.Labels{"chain": name, "node": node.Name}); v != nil {
v.Inc()
}
return
}
if marker != nil {
marker.Reset()
}
}
}()
addr, err := xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
marker := node.Marker()
if err != nil {
if marker != nil {
marker.Mark()
}
return
}
start := time.Now()
cc, err := node.Options().Transport.Dial(ctx, addr)
if err != nil {
if marker != nil {
marker.Mark()
}
return
}
cn, err := node.Options().Transport.Handshake(ctx, cc)
if err != nil {
cc.Close()
if marker != nil {
marker.Mark()
}
return
}
if marker != nil {
marker.Reset()
}
if r.options.Chain != nil {
var name string
if cn, _ := r.options.Chain.(chainNamer); cn != nil {
name = cn.Name()
}
if v := xmetrics.GetObserver(xmetrics.MetricNodeConnectDurationObserver,
metrics.Labels{"chain": name, "node": node.Name}); v != nil {
v.Observe(time.Since(start).Seconds())
}
}
preNode := node
for _, node := range r.nodes[1:] {
marker := node.Marker()
addr, err = xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
if err != nil {
cn.Close()
if marker != nil {
marker.Mark()
}
return
}
cc, err = preNode.Options().Transport.Connect(ctx, cn, "tcp", addr)
if err != nil {
cn.Close()
if marker != nil {
marker.Mark()
}
return
}
cc, err = node.Options().Transport.Handshake(ctx, cc)
if err != nil {
cn.Close()
if marker != nil {
marker.Mark()
}
return
}
if marker != nil {
marker.Reset()
}
cn = cc
preNode = node
}
conn = cn
return
}
func (r *chainRoute) getNode(index int) *chain.Node {
if r == nil || len(r.Nodes()) == 0 || index < 0 || index >= len(r.Nodes()) {
return nil
}
return r.nodes[index]
}
func (r *chainRoute) Nodes() []*chain.Node {
if r != nil {
return r.nodes
}
return nil
}
+223
View File
@@ -0,0 +1,223 @@
package chain
import (
"bytes"
"context"
"fmt"
"net"
"time"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
ctxvalue "github.com/go-gost/x/ctx"
xnet "github.com/go-gost/x/internal/net"
)
type Router struct {
options chain.RouterOptions
}
func NewRouter(opts ...chain.RouterOption) *Router {
r := &Router{}
for _, opt := range opts {
if opt != nil {
opt(&r.options)
}
}
if r.options.Timeout == 0 {
r.options.Timeout = 15 * time.Second
}
if r.options.Logger == nil {
r.options.Logger = logger.Default().WithFields(map[string]any{"kind": "router"})
}
return r
}
func (r *Router) Options() *chain.RouterOptions {
if r == nil {
return nil
}
return &r.options
}
func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
host := address
if h, _, _ := net.SplitHostPort(address); h != "" {
host = h
}
r.record(ctx, recorder.RecorderServiceRouterDialAddress, []byte(host))
log := r.options.Logger.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
})
conn, err = r.dial(ctx, network, address, log)
if err != nil {
r.record(ctx, recorder.RecorderServiceRouterDialAddressError, []byte(host))
return
}
if network == "udp" || network == "udp4" || network == "udp6" {
if _, ok := conn.(net.PacketConn); !ok {
return &packetConn{conn}, nil
}
}
return
}
func (r *Router) record(ctx context.Context, name string, data []byte) error {
if len(data) == 0 {
return nil
}
for _, rec := range r.options.Recorders {
if rec.Record == name {
return rec.Recorder.Record(ctx, data)
}
}
return nil
}
func (r *Router) dial(ctx context.Context, network, address string, log logger.Logger) (conn net.Conn, err error) {
count := r.options.Retries + 1
if count <= 0 {
count = 1
}
log.Debugf("dial %s/%s", address, network)
for i := 0; i < count; i++ {
ctx := ctx
if r.options.Timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
defer cancel()
}
buf := ctxvalue.BufferFromContext(ctx)
if buf != nil {
buf.Reset()
}
var ipAddr string
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log)
if err != nil {
log.Error(err)
break
}
if buf != nil {
buf.Reset()
}
var route chain.Route
if r.options.Chain != nil {
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
}
if buf == nil {
buf = &bytes.Buffer{}
}
for _, node := range routePath(route) {
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(buf, "%s", ipAddr)
log.Debugf("route(retry=%d) %s", i, buf.String())
if route == nil {
route = DefaultRoute
}
conn, err = route.Dial(ctx, network, ipAddr,
chain.InterfaceDialOption(r.options.IfceName),
chain.NetnsDialOption(r.options.Netns),
chain.SockOptsDialOption(r.options.SockOpts),
chain.LoggerDialOption(log),
)
if err == nil {
break
}
log.Errorf("route(retry=%d) %s", i, err)
}
return
}
func (r *Router) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (ln net.Listener, err error) {
count := r.options.Retries + 1
if count <= 0 {
count = 1
}
log := r.options.Logger.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
})
log.Debugf("bind on %s/%s", address, network)
for i := 0; i < count; i++ {
ctx := ctx
if r.options.Timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
defer cancel()
}
var route chain.Route
if r.options.Chain != nil {
route = r.options.Chain.Route(ctx, network, address)
if route == nil || len(route.Nodes()) == 0 {
err = ErrEmptyRoute
return
}
}
if log.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range routePath(route) {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", address)
log.Debugf("route(retry=%d) %s", i, buf.String())
}
if route == nil {
route = DefaultRoute
}
ln, err = route.Bind(ctx, network, address, opts...)
if err == nil {
break
}
log.Errorf("route(retry=%d) %s", i, err)
}
return
}
func routePath(route chain.Route) (path []*chain.Node) {
if route == nil {
return
}
for _, node := range route.Nodes() {
if tr := node.Options().Transport; tr != nil {
path = append(path, routePath(tr.Options().Route)...)
}
path = append(path, node)
}
return
}
type packetConn struct {
net.Conn
}
func (c *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
n, err = c.Read(b)
addr = c.Conn.RemoteAddr()
return
}
func (c *packetConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return c.Write(b)
}
+106
View File
@@ -0,0 +1,106 @@
package chain
import (
"context"
"net"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/dialer"
net_dialer "github.com/go-gost/x/internal/net/dialer"
)
type Transport struct {
dialer dialer.Dialer
connector connector.Connector
options chain.TransportOptions
}
func NewTransport(d dialer.Dialer, c connector.Connector, opts ...chain.TransportOption) *Transport {
tr := &Transport{
dialer: d,
connector: c,
}
for _, opt := range opts {
if opt != nil {
opt(&tr.options)
}
}
return tr
}
func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) {
netd := &net_dialer.Dialer{
Interface: tr.options.IfceName,
Netns: tr.options.Netns,
}
if tr.options.SockOpts != nil {
netd.Mark = tr.options.SockOpts.Mark
}
if tr.options.Route != nil && len(tr.options.Route.Nodes()) > 0 {
netd.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return tr.options.Route.Dial(ctx, network, addr)
}
}
opts := []dialer.DialOption{
dialer.HostDialOption(tr.options.Addr),
dialer.NetDialerDialOption(netd),
}
return tr.dialer.Dial(ctx, addr, opts...)
}
func (tr *Transport) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
var err error
if hs, ok := tr.dialer.(dialer.Handshaker); ok {
conn, err = hs.Handshake(ctx, conn,
dialer.AddrHandshakeOption(tr.options.Addr))
if err != nil {
return nil, err
}
}
if hs, ok := tr.connector.(connector.Handshaker); ok {
return hs.Handshake(ctx, conn)
}
return conn, nil
}
func (tr *Transport) Connect(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
netd := &net_dialer.Dialer{
Interface: tr.options.IfceName,
Netns: tr.options.Netns,
}
if tr.options.SockOpts != nil {
netd.Mark = tr.options.SockOpts.Mark
}
return tr.connector.Connect(ctx, conn, network, address,
connector.DialerConnectOption(netd),
)
}
func (tr *Transport) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
if binder, ok := tr.connector.(connector.Binder); ok {
return binder.Bind(ctx, conn, network, address, opts...)
}
return nil, connector.ErrBindUnsupported
}
func (tr *Transport) Multiplex() bool {
if mux, ok := tr.dialer.(dialer.Multiplexer); ok {
return mux.Multiplex()
}
return false
}
func (tr *Transport) Options() *chain.TransportOptions {
if tr != nil {
return &tr.options
}
return nil
}
func (tr *Transport) Copy() chain.Transporter {
tr2 := &Transport{}
*tr2 = *tr
return tr
}