package engine import ( "errors" "fmt" "net" "sync" "time" "github.com/xjasonlyu/tun2socks/v2/component/dialer" "github.com/xjasonlyu/tun2socks/v2/core" "github.com/xjasonlyu/tun2socks/v2/core/device" "github.com/xjasonlyu/tun2socks/v2/core/option" "github.com/xjasonlyu/tun2socks/v2/engine/mirror" "github.com/xjasonlyu/tun2socks/v2/log" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/restapi" "github.com/xjasonlyu/tun2socks/v2/tunnel" "github.com/docker/go-units" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/stack" ) var ( _engineMu sync.Mutex // _defaultKey holds the default key for the engine. _defaultKey *Key // _defaultProxy holds the default proxy for the engine. _defaultProxy proxy.Proxy // _defaultDevice holds the default device for the engine. _defaultDevice device.Device // _defaultStack holds the default stack for the engine. _defaultStack *stack.Stack ) // Start starts the default engine up. func Start() { if err := start(); err != nil { log.Fatalf("[ENGINE] failed to start: %v", err) } } // Stop shuts the default engine down. func Stop() { if err := stop(); err != nil { log.Fatalf("[ENGINE] failed to stop: %v", err) } } // Insert loads *Key to the default engine. func Insert(k *Key) { _engineMu.Lock() _defaultKey = k _engineMu.Unlock() } func start() error { _engineMu.Lock() if _defaultKey == nil { return errors.New("empty key") } for _, f := range []func(*Key) error{ general, restAPI, netstack, } { if err := f(_defaultKey); err != nil { return err } } _engineMu.Unlock() return nil } func stop() (err error) { _engineMu.Lock() if _defaultDevice != nil { err = _defaultDevice.Close() } if _defaultStack != nil { _defaultStack.Close() _defaultStack.Wait() } _engineMu.Unlock() return err } func general(k *Key) error { level, err := log.ParseLevel(k.LogLevel) if err != nil { return err } log.SetLevel(level) if k.Interface != "" { iface, err := net.InterfaceByName(k.Interface) if err != nil { return err } dialer.DefaultInterfaceName.Store(iface.Name) dialer.DefaultInterfaceIndex.Store(int32(iface.Index)) log.Infof("[DIALER] bind to interface: %s", k.Interface) } if k.Mark != 0 { dialer.DefaultRoutingMark.Store(int32(k.Mark)) log.Infof("[DIALER] set fwmark: %#x", k.Mark) } if k.UDPTimeout > 0 { if k.UDPTimeout < time.Second { return errors.New("invalid udp timeout value") } tunnel.SetUDPTimeout(k.UDPTimeout) } return nil } func restAPI(k *Key) error { if k.RestAPI != "" { u, err := parseRestAPI(k.RestAPI) if err != nil { return err } host, token := u.Host, u.User.String() restapi.SetStatsFunc(func() tcpip.Stats { _engineMu.Lock() defer _engineMu.Unlock() // default stack is not initialized. if _defaultStack == nil { return tcpip.Stats{} } return _defaultStack.Stats() }) go func() { if err := restapi.Start(host, token); err != nil { log.Warnf("[RESTAPI] failed to start: %v", err) } }() log.Infof("[RESTAPI] serve at: %s", u) } return nil } func netstack(k *Key) (err error) { if k.Proxy == "" { return errors.New("empty proxy") } if k.Device == "" { return errors.New("empty device") } if _defaultProxy, err = parseProxy(k.Proxy); err != nil { return } proxy.SetDialer(_defaultProxy) if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil { return } var opts []option.Option if k.TCPModerateReceiveBuffer { opts = append(opts, option.WithTCPModerateReceiveBuffer(true)) } if k.TCPSendBufferSize != "" { size, err := units.RAMInBytes(k.TCPSendBufferSize) if err != nil { return err } opts = append(opts, option.WithTCPSendBufferSize(int(size))) } if k.TCPReceiveBufferSize != "" { size, err := units.RAMInBytes(k.TCPReceiveBufferSize) if err != nil { return err } opts = append(opts, option.WithTCPReceiveBufferSize(int(size))) } if _defaultStack, err = core.CreateStack(&core.Config{ LinkEndpoint: _defaultDevice, TransportHandler: &mirror.Tunnel{}, PrintFunc: func(format string, v ...any) { log.Warnf("[STACK] %s", fmt.Sprintf(format, v...)) }, Options: opts, }); err != nil { return } log.Infof( "[STACK] %s://%s <-> %s://%s", _defaultDevice.Type(), _defaultDevice.Name(), _defaultProxy.Proto(), _defaultProxy.Addr(), ) return nil }