From fd9c4cbfa51519f78d2d96bb5831f74848620875 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 17:43:50 +0800 Subject: [PATCH] chore: listeners support tun --- config/config.go | 2 +- docs/config.yaml | 37 +++++++++ listener/config/tun.go | 12 +++ listener/inbound/base.go | 2 +- listener/inbound/shadowsocks.go | 20 ++--- listener/inbound/tuic.go | 32 ++++---- listener/inbound/tun.go | 139 ++++++++++++++++++++++++++++++++ listener/inbound/tunnel.go | 5 +- listener/inbound/vmess.go | 26 +++--- listener/listener.go | 1 + listener/parse.go | 10 +++ listener/sing_tun/server.go | 24 ++++-- 12 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 listener/inbound/tun.go diff --git a/config/config.go b/config/config.go index 0dc100f4..b5bc6a1d 100644 --- a/config/config.go +++ b/config/config.go @@ -243,7 +243,7 @@ type RawTun struct { RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - //Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` + //Inet4Address []LC.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` diff --git a/docs/config.yaml b/docs/config.yaml index 1df28909..161046ad 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -740,3 +740,40 @@ listeners: network: [ tcp, udp ] target: target.com + - name: tun-in-1 + type: tun + # rule: sub-rule + stack: system # gvisor / lwip + dns-hijack: + - 0.0.0.0:53 # 需要劫持的 DNS + # auto-detect-interface: false # 自动识别出口网卡 + # auto-route: false # 配置路由表 + # mtu: 9000 # 最大传输单元 + # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 + inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - 0.0.0.0/1 + - 128.0.0.0/1 + inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - "::/1" + - "8000::/1" + # endpoint_independent_nat: false # 启用独立于端点的 NAT + # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route + # - 0 + # include_uid_range: # 限制被路由的的用户范围 + # - 1000-99999 + # exclude_uid: # 排除路由的的用户 + #- 1000 + # exclude_uid_range: # 排除路由的的用户范围 + # - 1000-99999 + + # Android 用户和应用规则仅在 Android 下被支持 + # 并且需要 auto_route + + # include_android_user: # 限制被路由的 Android 用户 + # - 0 + # - 10 + # include_package: # 限制被路由的 Android 应用包名 + # - com.android.chrome + # exclude_package: # 排除被路由的 Android 应用包名 + # - com.android.captiveportallogin + diff --git a/listener/config/tun.go b/listener/config/tun.go index 828ed488..e7b2c930 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -58,6 +58,18 @@ func (p ListenPrefix) Build() netip.Prefix { return netip.Prefix(p) } +func StringSliceToListenPrefixSlice(ss []string) ([]ListenPrefix, error) { + lps := make([]ListenPrefix, 0, len(ss)) + for _, s := range ss { + prefix, err := netip.ParsePrefix(s) + if err != nil { + return nil, err + } + lps = append(lps, ListenPrefix(prefix)) + } + return lps, nil +} + type Tun struct { Enable bool Device string diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 46cd190d..39366e1b 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -74,7 +74,7 @@ var _ C.InboundListener = (*Base)(nil) type BaseOption struct { NameStr string `inbound:"name"` Listen string `inbound:"listen,omitempty"` - Port int `inbound:"port"` + Port int `inbound:"port,omitempty"` SpecialRules string `inbound:"rule,omitempty"` } diff --git a/listener/inbound/shadowsocks.go b/listener/inbound/shadowsocks.go index c21f2f39..e6baa80c 100644 --- a/listener/inbound/shadowsocks.go +++ b/listener/inbound/shadowsocks.go @@ -21,6 +21,7 @@ type ShadowSocks struct { *Base config *ShadowSocksOption l C.MultiAddrListener + ss LC.ShadowsocksServer } func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { @@ -31,8 +32,13 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { return &ShadowSocks{ Base: base, config: options, + ss: LC.ShadowsocksServer{ + Enable: true, + Listen: base.RawAddress(), + Password: options.Password, + Cipher: options.Cipher, + }, }, nil - } // Config implements constant.InboundListener @@ -53,17 +59,7 @@ func (s *ShadowSocks) Address() string { // Listen implements constant.InboundListener func (s *ShadowSocks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - s.l, err = sing_shadowsocks.New( - LC.ShadowsocksServer{ - Enable: true, - Listen: s.RawAddress(), - Password: s.config.Password, - Cipher: s.config.Cipher, - }, - tcpIn, - udpIn, - s.Additions()..., - ) + s.l, err = sing_shadowsocks.New(s.ss, tcpIn, udpIn, s.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tuic.go b/listener/inbound/tuic.go index efe5d8b2..f3b8a5f2 100644 --- a/listener/inbound/tuic.go +++ b/listener/inbound/tuic.go @@ -27,6 +27,7 @@ type Tuic struct { *Base config *TuicOption l *tuic.Listener + ts LC.TuicServer } func NewTuic(options *TuicOption) (*Tuic, error) { @@ -37,8 +38,19 @@ func NewTuic(options *TuicOption) (*Tuic, error) { return &Tuic{ Base: base, config: options, + ts: LC.TuicServer{ + Enable: true, + Listen: base.RawAddress(), + Token: options.Token, + Certificate: options.Certificate, + PrivateKey: options.PrivateKey, + CongestionController: options.CongestionController, + MaxIdleTime: options.MaxIdleTime, + AuthenticationTimeout: options.AuthenticationTimeout, + ALPN: options.ALPN, + MaxUdpRelayPacketSize: options.MaxUdpRelayPacketSize, + }, }, nil - } // Config implements constant.InboundListener @@ -59,23 +71,7 @@ func (t *Tuic) Address() string { // Listen implements constant.InboundListener func (t *Tuic) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - t.l, err = tuic.New( - LC.TuicServer{ - Enable: true, - Listen: t.RawAddress(), - Token: t.config.Token, - Certificate: t.config.Certificate, - PrivateKey: t.config.PrivateKey, - CongestionController: t.config.CongestionController, - MaxIdleTime: t.config.MaxIdleTime, - AuthenticationTimeout: t.config.AuthenticationTimeout, - ALPN: t.config.ALPN, - MaxUdpRelayPacketSize: t.config.MaxUdpRelayPacketSize, - }, - tcpIn, - udpIn, - t.Additions()..., - ) + t.l, err = tuic.New(t.ts, tcpIn, udpIn, t.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go new file mode 100644 index 00000000..3365ed89 --- /dev/null +++ b/listener/inbound/tun.go @@ -0,0 +1,139 @@ +package inbound + +import ( + "errors" + "net/netip" + "strings" + + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing_tun" + "github.com/Dreamacro/clash/log" +) + +type TunOption struct { + BaseOption + Device string `inbound:"device,omitempty"` + Stack string `inbound:"stack,omitempty"` + DNSHijack []string `inbound:"dns-hijack,omitempty"` + AutoRoute bool `inbound:"auto-route,omitempty"` + AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"` + + MTU uint32 `inbound:"mtu,omitempty"` + Inet4Address []string `inbound:"inet4_address,omitempty"` + Inet6Address []string `inbound:"inet6_address,omitempty"` + StrictRoute bool `inbound:"strict_route,omitempty"` + Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"` + Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"` + IncludeUID []uint32 `inbound:"include_uid,omitempty"` + IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` + ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` + ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` + IncludePackage []string `inbound:"include_package,omitempty"` + ExcludePackage []string `inbound:"exclude_package,omitempty"` + EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `inbound:"udp_timeout,omitempty"` +} + +func (o TunOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Tun struct { + *Base + config *TunOption + l *sing_tun.Listener + tun LC.Tun +} + +func NewTun(options *TunOption) (*Tun, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + stack, exist := C.StackTypeMapping[strings.ToLower(options.Stack)] + if !exist { + return nil, errors.New("invalid tun stack") + } + dnsHijack := make([]netip.AddrPort, 0, len(options.DNSHijack)) + for _, str := range options.DNSHijack { + var a netip.AddrPort + err = a.UnmarshalText([]byte(str)) + if err != nil { + return nil, err + } + dnsHijack = append(dnsHijack, a) + } + inet4Address, err := LC.StringSliceToListenPrefixSlice(options.Inet4Address) + if err != nil { + return nil, err + } + inet6Address, err := LC.StringSliceToListenPrefixSlice(options.Inet6Address) + if err != nil { + return nil, err + } + inet4RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet4RouteAddress) + if err != nil { + return nil, err + } + inet6RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet6RouteAddress) + if err != nil { + return nil, err + } + return &Tun{ + Base: base, + config: options, + tun: LC.Tun{ + Enable: true, + Device: options.Device, + Stack: stack, + DNSHijack: dnsHijack, + AutoRoute: options.AutoRoute, + AutoDetectInterface: options.AutoDetectInterface, + MTU: options.MTU, + Inet4Address: inet4Address, + Inet6Address: inet6Address, + StrictRoute: options.StrictRoute, + Inet4RouteAddress: inet4RouteAddress, + Inet6RouteAddress: inet6RouteAddress, + IncludeUID: options.IncludeUID, + IncludeUIDRange: options.IncludeUIDRange, + ExcludeUID: options.ExcludeUID, + ExcludeUIDRange: options.ExcludeUIDRange, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, + EndpointIndependentNat: options.EndpointIndependentNat, + UDPTimeout: options.UDPTimeout, + }, + }, nil +} + +// Config implements constant.InboundListener +func (t *Tun) Config() C.InboundConfig { + return t.config +} + +// Address implements constant.InboundListener +func (t *Tun) Address() string { + return t.l.Address() +} + +// Listen implements constant.InboundListener +func (t *Tun) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + t.l, err = sing_tun.New(t.tun, tcpIn, udpIn, t.Additions()...) + if err != nil { + return err + } + log.Infoln("Tun[%s] proxy listening at: %s", t.Name(), t.Address()) + return nil +} + +// Close implements constant.InboundListener +func (t *Tun) Close() error { + return t.l.Close() +} + +var _ C.InboundListener = (*Tun)(nil) diff --git a/listener/inbound/tunnel.go b/listener/inbound/tunnel.go index 683f58c9..c8478e36 100644 --- a/listener/inbound/tunnel.go +++ b/listener/inbound/tunnel.go @@ -55,7 +55,7 @@ func (t *Tunnel) Close() error { if err == nil { err = udpErr } else { - return fmt.Errorf("close tcp err: %t, close udp err: %t", err.Error(), udpErr.Error()) + return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error()) } } } @@ -82,7 +82,8 @@ func (t *Tunnel) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return err } default: - return fmt.Errorf("unknow network type: %s", network) + log.Warnln("unknown network type: %s, passed", network) + continue } log.Infoln("Tunnel[%s](%s/%s)proxy listening at: %s", t.Name(), network, t.config.Target, t.Address()) } diff --git a/listener/inbound/vmess.go b/listener/inbound/vmess.go index 745da45e..130e17c5 100644 --- a/listener/inbound/vmess.go +++ b/listener/inbound/vmess.go @@ -26,6 +26,7 @@ type Vmess struct { *Base config *VmessOption l C.MultiAddrListener + vs LC.VmessServer } func NewVmess(options *VmessOption) (*Vmess, error) { @@ -33,11 +34,23 @@ func NewVmess(options *VmessOption) (*Vmess, error) { if err != nil { return nil, err } + users := make([]LC.VmessUser, len(options.Users)) + for i, v := range options.Users { + users[i] = LC.VmessUser{ + Username: v.Username, + UUID: v.UUID, + AlterID: v.AlterID, + } + } return &Vmess{ Base: base, config: options, + vs: LC.VmessServer{ + Enable: true, + Listen: base.RawAddress(), + Users: users, + }, }, nil - } // Config implements constant.InboundListener @@ -66,16 +79,7 @@ func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) AlterID: v.AlterID, } } - v.l, err = sing_vmess.New( - LC.VmessServer{ - Enable: true, - Listen: v.RawAddress(), - Users: users, - }, - tcpIn, - udpIn, - v.Additions()..., - ) + v.l, err = sing_vmess.New(v.vs, tcpIn, udpIn, v.Additions()...) if err != nil { return err } diff --git a/listener/listener.go b/listener/listener.go index 0530e20e..ab9963b6 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -533,6 +533,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack } tunLister, err = sing_tun.New(tunConf, tcpIn, udpIn) + log.Infoln("[TUN] Tun adapter listening at: %s", tunLister.Address()) } func ReCreateRedirToTun(ifaceNames []string) { diff --git a/listener/parse.go b/listener/parse.go index deb4e10d..9459b9e1 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -62,6 +62,16 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewTunnel(tunnelOption) + case "tun": + tunOption := &IN.TunOption{ + Stack: C.TunGvisor.String(), + DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query + } + err = decoder.Decode(mapping, tunOption) + if err != nil { + return nil, err + } + listener, err = IN.NewTun(tunOption) case "shadowsocks": shadowsocksOption := &IN.ShadowSocksOption{} err = decoder.Decode(mapping, shadowsocksOption) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 0dc4d476..d74324da 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -2,12 +2,14 @@ package sing_tun import ( "context" + "fmt" "net" "net/netip" "runtime" "strconv" "strings" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/iface" C "github.com/Dreamacro/clash/constant" @@ -29,6 +31,7 @@ type Listener struct { options LC.Tun handler *ListenerHandler tunName string + addrStr string tunIf tun.Tun tunStack tun.Stack @@ -64,7 +67,13 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (l *Listener, err error) { +func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) { + if len(additions) == 0 { + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-TUN"), + inbound.WithSpecialRules(""), + } + } tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) @@ -113,9 +122,10 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte handler := &ListenerHandler{ ListenerHandler: sing.ListenerHandler{ - TcpIn: tcpIn, - UdpIn: udpIn, - Type: C.TUN, + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.TUN, + Additions: additions, }, DnsAdds: dnsAdds, } @@ -211,7 +221,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte //l.openAndroidHotspot(tunOptions) - log.Infoln("[TUN] Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", + l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) return } @@ -286,3 +296,7 @@ func (l *Listener) Close() error { func (l *Listener) Config() LC.Tun { return l.options } + +func (l *Listener) Address() string { + return l.addrStr +}