github.com/yaling888/clash@v1.53.0/constant/tun.go (about)

     1  package constant
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/netip"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  var StackTypeMapping = map[string]TUNStack{
    13  	strings.ToUpper(TunGvisor.String()): TunGvisor,
    14  	strings.ToUpper(TunSystem.String()): TunSystem,
    15  }
    16  
    17  const (
    18  	TunGvisor TUNStack = iota
    19  	TunSystem
    20  )
    21  
    22  const (
    23  	TunDisabled TUNState = iota
    24  	TunEnabled
    25  	TunPaused
    26  )
    27  
    28  type TUNState int
    29  
    30  type TUNChangeCallback interface {
    31  	Pause()
    32  	Resume()
    33  }
    34  
    35  // Tun config
    36  type Tun struct {
    37  	Enable              bool          `yaml:"enable" json:"enable"`
    38  	Device              string        `yaml:"device" json:"device"`
    39  	Stack               TUNStack      `yaml:"stack" json:"stack"`
    40  	DNSHijack           []DNSUrl      `yaml:"dns-hijack" json:"dns-hijack"`
    41  	AutoRoute           bool          `yaml:"auto-route" json:"auto-route"`
    42  	AutoDetectInterface bool          `yaml:"auto-detect-interface" json:"auto-detect-interface"`
    43  	TunAddressPrefix    *netip.Prefix `yaml:"-" json:"-"`
    44  	RedirectToTun       []string      `yaml:"-" json:"-"`
    45  	StopRouteListener   bool          `yaml:"-" json:"-"`
    46  }
    47  
    48  var lastTunConf *Tun
    49  
    50  func GetLastTunConf() *Tun {
    51  	return lastTunConf
    52  }
    53  
    54  func SetLastTunConf(conf *Tun) {
    55  	lastTunConf = conf
    56  }
    57  
    58  // GetTunConf returns the last tun config
    59  func GetTunConf() Tun {
    60  	if lastTunConf == nil {
    61  		addrPort := DNSAddrPort{
    62  			AddrPort: netip.MustParseAddrPort("0.0.0.0:53"),
    63  		}
    64  		return Tun{
    65  			Enable: false,
    66  			Stack:  TunGvisor,
    67  			DNSHijack: []DNSUrl{ // default hijack all dns query
    68  				{
    69  					Network:  "udp",
    70  					AddrPort: addrPort,
    71  				},
    72  				{
    73  					Network:  "tcp",
    74  					AddrPort: addrPort,
    75  				},
    76  			},
    77  			AutoRoute:           true,
    78  			AutoDetectInterface: false,
    79  		}
    80  	}
    81  	return *lastTunConf
    82  }
    83  
    84  type TUNStack int
    85  
    86  // UnmarshalYAML unserialize TUNStack with yaml
    87  func (e *TUNStack) UnmarshalYAML(unmarshal func(any) error) error {
    88  	var tp string
    89  	if err := unmarshal(&tp); err != nil {
    90  		return err
    91  	}
    92  	mode, exist := StackTypeMapping[strings.ToUpper(tp)]
    93  	if !exist {
    94  		return errors.New("invalid tun stack")
    95  	}
    96  	*e = mode
    97  	return nil
    98  }
    99  
   100  // MarshalYAML serialize TUNStack with yaml
   101  func (e TUNStack) MarshalYAML() (any, error) {
   102  	return e.String(), nil
   103  }
   104  
   105  // UnmarshalJSON unserialize TUNStack with json
   106  func (e *TUNStack) UnmarshalJSON(data []byte) error {
   107  	var tp string
   108  	_ = json.Unmarshal(data, &tp)
   109  	mode, exist := StackTypeMapping[strings.ToUpper(tp)]
   110  	if !exist {
   111  		return errors.New("invalid tun stack")
   112  	}
   113  	*e = mode
   114  	return nil
   115  }
   116  
   117  // MarshalJSON serialize TUNStack with json
   118  func (e TUNStack) MarshalJSON() ([]byte, error) {
   119  	return json.Marshal(e.String())
   120  }
   121  
   122  func (e TUNStack) String() string {
   123  	switch e {
   124  	case TunGvisor:
   125  		return "gVisor"
   126  	case TunSystem:
   127  		return "System"
   128  	default:
   129  		return "unknown"
   130  	}
   131  }
   132  
   133  type DNSAddrPort struct {
   134  	netip.AddrPort
   135  }
   136  
   137  func (p *DNSAddrPort) UnmarshalText(text []byte) error {
   138  	if len(text) == 0 {
   139  		*p = DNSAddrPort{}
   140  		return nil
   141  	}
   142  
   143  	addrPort := string(text)
   144  	if strings.HasPrefix(addrPort, "any") {
   145  		_, port, _ := strings.Cut(addrPort, "any")
   146  		addrPort = "0.0.0.0" + port
   147  	}
   148  
   149  	ap, err := netip.ParseAddrPort(addrPort)
   150  	*p = DNSAddrPort{AddrPort: ap}
   151  	return err
   152  }
   153  
   154  func (p DNSAddrPort) String() string {
   155  	addrPort := p.AddrPort.String()
   156  	if p.AddrPort.Addr().IsUnspecified() {
   157  		addrPort = "any:" + strconv.Itoa(int(p.AddrPort.Port()))
   158  	}
   159  	return addrPort
   160  }
   161  
   162  type DNSUrl struct {
   163  	Network  string
   164  	AddrPort DNSAddrPort
   165  }
   166  
   167  func (d *DNSUrl) UnmarshalYAML(unmarshal func(any) error) error {
   168  	var text string
   169  	if err := unmarshal(&text); err != nil {
   170  		return err
   171  	}
   172  
   173  	text = strings.ToLower(text)
   174  	network := "udp"
   175  	if before, after, found := strings.Cut(text, "://"); found {
   176  		network = before
   177  		text = after
   178  	}
   179  
   180  	if network != "udp" && network != "tcp" {
   181  		return errors.New("invalid dns url schema")
   182  	}
   183  
   184  	ap := &DNSAddrPort{}
   185  	if err := ap.UnmarshalText([]byte(text)); err != nil {
   186  		return err
   187  	}
   188  
   189  	*d = DNSUrl{Network: network, AddrPort: *ap}
   190  
   191  	return nil
   192  }
   193  
   194  func (d DNSUrl) MarshalYAML() (any, error) {
   195  	return d.String(), nil
   196  }
   197  
   198  func (d *DNSUrl) UnmarshalJSON(data []byte) error {
   199  	var text string
   200  	if err := json.Unmarshal(data, &text); err != nil {
   201  		return err
   202  	}
   203  
   204  	text = strings.ToLower(text)
   205  	network := "udp"
   206  	if before, after, found := strings.Cut(text, "://"); found {
   207  		network = before
   208  		text = after
   209  	}
   210  
   211  	if network != "udp" && network != "tcp" {
   212  		return errors.New("invalid dns url schema")
   213  	}
   214  
   215  	ap := &DNSAddrPort{}
   216  	if err := ap.UnmarshalText([]byte(text)); err != nil {
   217  		return err
   218  	}
   219  
   220  	*d = DNSUrl{Network: network, AddrPort: *ap}
   221  
   222  	return nil
   223  }
   224  
   225  func (d DNSUrl) MarshalJSON() ([]byte, error) {
   226  	return json.Marshal(d.String())
   227  }
   228  
   229  func (d DNSUrl) String() string {
   230  	return fmt.Sprintf("%s://%s", d.Network, d.AddrPort)
   231  }