mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2026-10-20 08:18:35 +00:00
CodeQL / Analyze (go) (push) Canceled after 0s
Publish Docker Image / Docker (push) Canceled after 0s
Linter / Linter (push) Canceled after 0s
Test / Unit Test (push) Canceled after 0s
Test / Build Test (push) Canceled after 0s
Test / E2E Test (HTTP) (push) Canceled after 0s
Test / E2E Test (Relay) (push) Canceled after 0s
Test / E2E Test (SOCKSv4) (push) Canceled after 0s
Test / E2E Test (SOCKSv5) (push) Canceled after 0s
Test / E2E Test (SSH) (push) Canceled after 0s
Test / E2E Test (Direct) (push) Canceled after 0s
Test / E2E Test (Shadowsocks) (push) Canceled after 0s
* Feat(engine): make Start/Stop return error instead of log.Fatalf (#550) * Refactor(engine): add StartOrFatal/StopOrFatal wrappers --------- Co-authored-by: Jason Lyu <xjasonlyu@gmail.com>
257 lines
5.6 KiB
Go
257 lines
5.6 KiB
Go
package engine
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"os/exec"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/docker/go-units"
|
|
"github.com/google/shlex"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
|
|
"github.com/xjasonlyu/tun2socks/v2/core"
|
|
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
|
|
"github.com/xjasonlyu/tun2socks/v2/core/device"
|
|
"github.com/xjasonlyu/tun2socks/v2/core/option"
|
|
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
|
"github.com/xjasonlyu/tun2socks/v2/log"
|
|
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
|
"github.com/xjasonlyu/tun2socks/v2/restapi"
|
|
"github.com/xjasonlyu/tun2socks/v2/tunnel"
|
|
)
|
|
|
|
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
|
|
|
|
// _icmpHandler holds the custom ICMP handler for the engine.
|
|
_icmpHandler adapter.NetworkHandler
|
|
)
|
|
|
|
// Insert loads *Key to the default engine.
|
|
func Insert(k *Key) {
|
|
_engineMu.Lock()
|
|
_defaultKey = k
|
|
_engineMu.Unlock()
|
|
}
|
|
|
|
// SetICMPHandler sets the custom ICMP handler for the default engine.
|
|
func SetICMPHandler(h adapter.NetworkHandler) {
|
|
_engineMu.Lock()
|
|
_icmpHandler = h
|
|
_engineMu.Unlock()
|
|
}
|
|
|
|
// Start starts the default engine up.
|
|
func Start() error {
|
|
_engineMu.Lock()
|
|
defer _engineMu.Unlock()
|
|
|
|
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
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StartOrFatal starts the default engine up, and exits the
|
|
// process if it fails.
|
|
func StartOrFatal() {
|
|
if err := Start(); err != nil {
|
|
log.Fatalf("[ENGINE] failed to start: %v", err)
|
|
}
|
|
}
|
|
|
|
// Stop shuts the default engine down.
|
|
func Stop() error {
|
|
_engineMu.Lock()
|
|
if _defaultDevice != nil {
|
|
_defaultDevice.Close()
|
|
}
|
|
if _defaultStack != nil {
|
|
_defaultStack.Close()
|
|
_defaultStack.Wait()
|
|
}
|
|
_engineMu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
// StopOrFatal shuts the default engine down, and exits the
|
|
// process if it fails.
|
|
func StopOrFatal() {
|
|
if err := Stop(); err != nil {
|
|
log.Fatalf("[ENGINE] failed to stop: %v", err)
|
|
}
|
|
}
|
|
|
|
func execCommand(cmd string) error {
|
|
parts, err := shlex.Split(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(parts) == 0 {
|
|
return errors.New("empty command")
|
|
}
|
|
_, err = exec.Command(parts[0], parts[1:]...).Output()
|
|
return err
|
|
}
|
|
|
|
func general(k *Key) error {
|
|
level, err := log.ParseLevel(k.LogLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.SetLogger(log.Must(log.NewLeveled(level)))
|
|
|
|
// Reset default dialer before registering options.
|
|
dialer.Reset()
|
|
|
|
if k.Interface != "" {
|
|
iface, err := net.InterfaceByName(k.Interface)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dialer.RegisterSockOpt(dialer.WithBindToInterface(iface))
|
|
log.Infof("[DIALER] bind to interface: %s", k.Interface)
|
|
}
|
|
|
|
if k.Mark != 0 {
|
|
dialer.RegisterSockOpt(dialer.WithRoutingMark(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.T().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.Errorf("[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 k.TUNPreUp != "" {
|
|
log.Infof("[TUN] pre-execute command: `%s`", k.TUNPreUp)
|
|
if preUpErr := execCommand(k.TUNPreUp); preUpErr != nil {
|
|
log.Errorf("[TUN] failed to pre-execute: %s: %v", k.TUNPreUp, preUpErr)
|
|
}
|
|
}
|
|
|
|
defer func() {
|
|
if k.TUNPostUp == "" || err != nil {
|
|
return
|
|
}
|
|
log.Infof("[TUN] post-execute command: `%s`", k.TUNPostUp)
|
|
if postUpErr := execCommand(k.TUNPostUp); postUpErr != nil {
|
|
log.Errorf("[TUN] failed to post-execute: %s: %v", k.TUNPostUp, postUpErr)
|
|
}
|
|
}()
|
|
|
|
multicastGroups, err := parseMulticastGroups(k.MulticastGroups)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _defaultProxy, err = parseProxy(k.Proxy); err != nil {
|
|
return err
|
|
}
|
|
tunnel.T().SetProxy(_defaultProxy)
|
|
|
|
if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil {
|
|
return err
|
|
}
|
|
|
|
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: tunnel.T(),
|
|
ICMPHandler: _icmpHandler,
|
|
MulticastGroups: multicastGroups,
|
|
Options: opts,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Infof("[STACK] %s <-> %s", k.Device, k.Proxy)
|
|
return nil
|
|
}
|