mirror of
https://github.com/go-gost/gost-plus.git
synced 2024-08-11 17:43:16 +00:00
258 lines
4.9 KiB
Go
258 lines
4.9 KiB
Go
package tunnel
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/go-gost/core/chain"
|
|
"github.com/go-gost/core/handler"
|
|
"github.com/go-gost/core/listener"
|
|
"github.com/go-gost/core/logger"
|
|
"github.com/go-gost/core/observer/stats"
|
|
"github.com/go-gost/core/service"
|
|
cfg "github.com/go-gost/gost.plus/config"
|
|
xchain "github.com/go-gost/x/chain"
|
|
"github.com/go-gost/x/config"
|
|
chain_parser "github.com/go-gost/x/config/parsing/chain"
|
|
"github.com/go-gost/x/handler/forward/remote"
|
|
"github.com/go-gost/x/hop"
|
|
"github.com/go-gost/x/listener/rudp"
|
|
mdx "github.com/go-gost/x/metadata"
|
|
xservice "github.com/go-gost/x/service"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type udpTunnel struct {
|
|
endpoint string
|
|
opts Options
|
|
config *config.Config
|
|
forward service.Service
|
|
favorite atomic.Bool
|
|
stats cfg.ServiceStats
|
|
|
|
cclose chan struct{}
|
|
|
|
err error
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func NewUDPTunnel(opts ...Option) Tunnel {
|
|
var options Options
|
|
for _, opt := range opts {
|
|
opt(&options)
|
|
}
|
|
|
|
if options.ID == "" {
|
|
options.ID = uuid.NewString()
|
|
}
|
|
|
|
v := md5.Sum([]byte(options.ID))
|
|
endpoint := hex.EncodeToString(v[:8])
|
|
|
|
if options.Endpoint == "" {
|
|
options.Endpoint = "localhost:8080"
|
|
}
|
|
|
|
if options.Name == "" {
|
|
options.Name = endpoint
|
|
}
|
|
if options.CreatedAt.IsZero() {
|
|
options.CreatedAt = time.Now()
|
|
}
|
|
|
|
s := &udpTunnel{
|
|
endpoint: endpoint,
|
|
opts: options,
|
|
cclose: make(chan struct{}),
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *udpTunnel) ID() string {
|
|
return s.opts.ID
|
|
}
|
|
|
|
func (s *udpTunnel) Type() string {
|
|
return UDPTunnel
|
|
}
|
|
|
|
func (s *udpTunnel) Name() string {
|
|
return s.opts.Name
|
|
}
|
|
|
|
func (s *udpTunnel) Endpoint() string {
|
|
return s.opts.Endpoint
|
|
}
|
|
|
|
func (s *udpTunnel) Entrypoint() string {
|
|
return fmt.Sprintf("%s.%s", s.endpoint, EndpointAddr)
|
|
}
|
|
|
|
func (s *udpTunnel) Options() Options {
|
|
return s.opts
|
|
}
|
|
|
|
func (s *udpTunnel) Favorite(b bool) {
|
|
s.favorite.Store(b)
|
|
}
|
|
|
|
func (s *udpTunnel) IsFavorite() bool {
|
|
return s.favorite.Load()
|
|
}
|
|
|
|
func (s *udpTunnel) init() error {
|
|
rudp := &config.ServiceConfig{
|
|
Name: s.opts.Name,
|
|
Addr: s.opts.Hostname,
|
|
Handler: &config.HandlerConfig{
|
|
Type: "rudp",
|
|
},
|
|
Listener: &config.ListenerConfig{
|
|
Type: "rudp",
|
|
Chain: s.opts.Name,
|
|
},
|
|
Forwarder: &config.ForwarderConfig{
|
|
Nodes: []*config.ForwardNodeConfig{
|
|
{
|
|
Name: s.opts.Name,
|
|
Addr: s.opts.Endpoint,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s.config = &config.Config{
|
|
Services: []*config.ServiceConfig{rudp},
|
|
Chains: []*config.ChainConfig{ChainConfig(s.opts.ID, s.opts.Name)},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *udpTunnel) Run() (err error) {
|
|
if s.IsClosed() {
|
|
return ErrTunnelClosed
|
|
}
|
|
|
|
defer func() {
|
|
s.setErr(err)
|
|
}()
|
|
|
|
if err = s.init(); err != nil {
|
|
return
|
|
}
|
|
|
|
log := logger.Default().WithFields(map[string]any{
|
|
"kind": "service",
|
|
"service": s.opts.Name,
|
|
})
|
|
|
|
{
|
|
var ch chain.Chainer
|
|
ch, err = chain_parser.ParseChain(s.config.Chains[0], log)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
|
|
listenerLogger := log.WithFields(map[string]any{"kind": "listener", "listener": "rudp"})
|
|
stats := &stats.Stats{}
|
|
cfg := s.config.Services[0]
|
|
ln := rudp.NewListener(
|
|
listener.AddrOption(cfg.Addr),
|
|
listener.RouterOption(xchain.NewRouter(chain.ChainRouterOption(ch), chain.LoggerRouterOption(listenerLogger))),
|
|
listener.LoggerOption(listenerLogger),
|
|
listener.StatsOption(stats),
|
|
)
|
|
if err = ln.Init(mdx.NewMetadata(cfg.Listener.Metadata)); err != nil {
|
|
return
|
|
}
|
|
|
|
handlerLogger := log.WithFields(map[string]any{"kind": "handler", "handler": "rudp"})
|
|
h := remote.NewHandler(
|
|
handler.RouterOption(xchain.NewRouter(chain.LoggerRouterOption(handlerLogger))),
|
|
handler.LoggerOption(handlerLogger),
|
|
)
|
|
if err = h.Init(mdx.NewMetadata(cfg.Handler.Metadata)); err != nil {
|
|
return
|
|
}
|
|
|
|
node := cfg.Forwarder.Nodes[0]
|
|
if forwarder, ok := h.(handler.Forwarder); ok {
|
|
forwarder.Forward(hop.NewHop(
|
|
hop.NodeOption(chain.NewNode(node.Name, node.Addr)),
|
|
hop.LoggerOption(log.WithFields(map[string]any{"kind": "hop"})),
|
|
))
|
|
}
|
|
s.forward = xservice.NewService(s.opts.Name, ln, h,
|
|
xservice.LoggerOption(log),
|
|
xservice.StatsOption(stats),
|
|
)
|
|
}
|
|
|
|
go func() {
|
|
s.setErr(s.forward.Serve())
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *udpTunnel) Status() *xservice.Status {
|
|
if ss, _ := s.forward.(ServiceStatus); ss != nil {
|
|
return ss.Status()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *udpTunnel) Stats() cfg.ServiceStats {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.stats
|
|
}
|
|
|
|
func (s *udpTunnel) SetStats(stats cfg.ServiceStats) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.stats = stats
|
|
}
|
|
|
|
func (s *udpTunnel) Close() error {
|
|
defer func() {
|
|
select {
|
|
case <-s.cclose:
|
|
default:
|
|
close(s.cclose)
|
|
}
|
|
}()
|
|
|
|
if s.forward != nil {
|
|
return s.forward.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *udpTunnel) IsClosed() bool {
|
|
select {
|
|
case <-s.cclose:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (s *udpTunnel) setErr(err error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.err = err
|
|
}
|
|
|
|
func (s *udpTunnel) Err() error {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.err
|
|
}
|