mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 12:42:27 +08:00
Merge remote-tracking branch 'origin/Alpha' into Alpha
This commit is contained in:
commit
0cb5270452
44
common/utils/range.go
Normal file
44
common/utils/range.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Range[T constraints.Ordered] struct {
|
||||||
|
start T
|
||||||
|
end T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRange[T constraints.Ordered](start, end T) *Range[T] {
|
||||||
|
if start > end {
|
||||||
|
return &Range[T]{
|
||||||
|
start: end,
|
||||||
|
end: start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Range[T]{
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range[T]) Contains(t T) bool {
|
||||||
|
return t >= r.start && t <= r.end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range[T]) LeftContains(t T) bool {
|
||||||
|
return t >= r.start && t < r.end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range[T]) RightContains(t T) bool {
|
||||||
|
return t > r.start && t <= r.end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range[T]) Start() T {
|
||||||
|
return r.start
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range[T]) End() T {
|
||||||
|
return r.end
|
||||||
|
}
|
@ -2,11 +2,15 @@ package sniffer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
|
|
||||||
CN "github.com/Dreamacro/clash/common/net"
|
CN "github.com/Dreamacro/clash/common/net"
|
||||||
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
@ -26,6 +30,7 @@ type SnifferDispatcher struct {
|
|||||||
|
|
||||||
foreDomain *trie.DomainTrie[bool]
|
foreDomain *trie.DomainTrie[bool]
|
||||||
skipSNI *trie.DomainTrie[bool]
|
skipSNI *trie.DomainTrie[bool]
|
||||||
|
portRanges *[]utils.Range[uint16]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
||||||
@ -35,6 +40,18 @@ func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if metadata.Host == "" || sd.foreDomain.Search(metadata.Host) != nil {
|
if metadata.Host == "" || sd.foreDomain.Search(metadata.Host) != nil {
|
||||||
|
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln("[Sniffer] Dst port is error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, portRange := range *sd.portRanges {
|
||||||
|
if !portRange.Contains(uint16(port)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if host, err := sd.sniffDomain(bufConn, metadata); err != nil {
|
if host, err := sd.sniffDomain(bufConn, metadata); err != nil {
|
||||||
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
||||||
return
|
return
|
||||||
@ -69,8 +86,16 @@ func (sd *SnifferDispatcher) Enable() bool {
|
|||||||
func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Metadata) (string, error) {
|
func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Metadata) (string, error) {
|
||||||
for _, sniffer := range sd.sniffers {
|
for _, sniffer := range sd.sniffers {
|
||||||
if sniffer.SupportNetwork() == C.TCP {
|
if sniffer.SupportNetwork() == C.TCP {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
_, err := conn.Peek(1)
|
_, err := conn.Peek(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_, ok := err.(*net.OpError)
|
||||||
|
if ok {
|
||||||
|
log.Errorln("[Sniffer] [%s] Maybe read timeout, Consider adding skip", metadata.DstIP.String())
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Errorln("[Sniffer] %v", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,11 +127,13 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
|
|||||||
return &dispatcher, nil
|
return &dispatcher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSnifferDispatcher(needSniffer []C.SnifferType, forceDomain *trie.DomainTrie[bool], skipSNI *trie.DomainTrie[bool]) (*SnifferDispatcher, error) {
|
func NewSnifferDispatcher(needSniffer []C.SnifferType, forceDomain *trie.DomainTrie[bool],
|
||||||
|
skipSNI *trie.DomainTrie[bool], ports *[]utils.Range[uint16]) (*SnifferDispatcher, error) {
|
||||||
dispatcher := SnifferDispatcher{
|
dispatcher := SnifferDispatcher{
|
||||||
enable: true,
|
enable: true,
|
||||||
foreDomain: forceDomain,
|
foreDomain: forceDomain,
|
||||||
skipSNI: skipSNI,
|
skipSNI: skipSNI,
|
||||||
|
portRanges: ports,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, snifferName := range needSniffer {
|
for _, snifferName := range needSniffer {
|
||||||
|
@ -4,16 +4,19 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
R "github.com/Dreamacro/clash/rule"
|
|
||||||
RP "github.com/Dreamacro/clash/rule/provider"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
|
R "github.com/Dreamacro/clash/rule"
|
||||||
|
RP "github.com/Dreamacro/clash/rule/provider"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter"
|
"github.com/Dreamacro/clash/adapter"
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||||
@ -127,6 +130,7 @@ type Sniffer struct {
|
|||||||
Reverses *trie.DomainTrie[bool]
|
Reverses *trie.DomainTrie[bool]
|
||||||
ForceDomain *trie.DomainTrie[bool]
|
ForceDomain *trie.DomainTrie[bool]
|
||||||
SkipSNI *trie.DomainTrie[bool]
|
SkipSNI *trie.DomainTrie[bool]
|
||||||
|
Ports *[]utils.Range[uint16]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental config
|
// Experimental config
|
||||||
@ -224,6 +228,7 @@ type SnifferRaw struct {
|
|||||||
Reverse []string `yaml:"reverses" json:"reverses"`
|
Reverse []string `yaml:"reverses" json:"reverses"`
|
||||||
ForceDomain []string `yaml:"force-domain" json:"force-domain"`
|
ForceDomain []string `yaml:"force-domain" json:"force-domain"`
|
||||||
SkipSNI []string `yaml:"skip-sni" json:"skip-sni"`
|
SkipSNI []string `yaml:"skip-sni" json:"skip-sni"`
|
||||||
|
Ports []string `yaml:"port-whitelist" json:"port-whitelist"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse config
|
// Parse config
|
||||||
@ -298,6 +303,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
Reverse: []string{},
|
Reverse: []string{},
|
||||||
ForceDomain: []string{},
|
ForceDomain: []string{},
|
||||||
SkipSNI: []string{},
|
SkipSNI: []string{},
|
||||||
|
Ports: []string{},
|
||||||
},
|
},
|
||||||
Profile: Profile{
|
Profile: Profile{
|
||||||
StoreSelected: true,
|
StoreSelected: true,
|
||||||
@ -890,7 +896,7 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
|
|||||||
if _, after, ok := strings.Cut(d, "://"); ok {
|
if _, after, ok := strings.Cut(d, "://"); ok {
|
||||||
d = after
|
d = after
|
||||||
}
|
}
|
||||||
|
d = strings.Replace(d, "any", "0.0.0.0", 1)
|
||||||
addrPort, err := netip.ParseAddrPort(d)
|
addrPort, err := netip.ParseAddrPort(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parse dns-hijack url error: %w", err)
|
return nil, fmt.Errorf("parse dns-hijack url error: %w", err)
|
||||||
@ -914,6 +920,33 @@ func parseSniffer(snifferRaw SnifferRaw) (*Sniffer, error) {
|
|||||||
Force: snifferRaw.Force,
|
Force: snifferRaw.Force,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ports := []utils.Range[uint16]{}
|
||||||
|
if len(snifferRaw.Ports) == 0 {
|
||||||
|
ports = append(ports, *utils.NewRange[uint16](0, 65535))
|
||||||
|
} else {
|
||||||
|
for _, portRange := range snifferRaw.Ports {
|
||||||
|
portRaws := strings.Split(portRange, "-")
|
||||||
|
if len(portRaws) > 1 {
|
||||||
|
p, err := strconv.ParseUint(portRaws[0], 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s format error", portRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := uint16(p)
|
||||||
|
|
||||||
|
p, err = strconv.ParseUint(portRaws[0], 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s format error", portRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
end := uint16(p)
|
||||||
|
ports = append(ports, *utils.NewRange(start, end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sniffer.Ports = &ports
|
||||||
|
|
||||||
loadSniffer := make(map[C.SnifferType]struct{})
|
loadSniffer := make(map[C.SnifferType]struct{})
|
||||||
|
|
||||||
for _, snifferName := range snifferRaw.Sniffing {
|
for _, snifferName := range snifferRaw.Sniffing {
|
||||||
|
@ -88,11 +88,7 @@ type Metadata struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) RemoteAddress() string {
|
func (m *Metadata) RemoteAddress() string {
|
||||||
if m.DstIP.IsValid() {
|
return net.JoinHostPort(m.String(), m.DstPort)
|
||||||
return net.JoinHostPort(m.DstIP.String(), m.DstPort)
|
|
||||||
} else {
|
|
||||||
return net.JoinHostPort(m.String(), m.DstPort)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) SourceAddress() string {
|
func (m *Metadata) SourceAddress() string {
|
||||||
|
3
go.mod
3
go.mod
@ -21,7 +21,8 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
go.uber.org/automaxprocs v1.5.1
|
go.uber.org/automaxprocs v1.5.1
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2
|
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd
|
||||||
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||||
|
6
go.sum
6
go.sum
@ -224,6 +224,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
|
||||||
|
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -255,8 +257,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -222,7 +222,7 @@ func updateTun(tun *config.Tun, dns *config.DNS) {
|
|||||||
|
|
||||||
func updateSniffer(sniffer *config.Sniffer) {
|
func updateSniffer(sniffer *config.Sniffer) {
|
||||||
if sniffer.Enable {
|
if sniffer.Enable {
|
||||||
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipSNI)
|
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipSNI, sniffer.Ports)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("initial sniffer failed, err:%v", err)
|
log.Warnln("initial sniffer failed, err:%v", err)
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,17 @@ import (
|
|||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Handler = (*GVHandler)(nil)
|
var _ adapter.Handler = (*gvHandler)(nil)
|
||||||
|
|
||||||
type GVHandler struct {
|
type gvHandler struct {
|
||||||
DNSAdds []netip.AddrPort
|
gateway netip.Addr
|
||||||
|
dnsHijack []netip.AddrPort
|
||||||
|
|
||||||
TCPIn chan<- C.ConnContext
|
tcpIn chan<- C.ConnContext
|
||||||
UDPIn chan<- *inbound.PacketAdapter
|
udpIn chan<- *inbound.PacketAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
|
func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
||||||
id := tunConn.ID()
|
id := tunConn.ID()
|
||||||
|
|
||||||
rAddr := &net.TCPAddr{
|
rAddr := &net.TCPAddr{
|
||||||
@ -34,11 +35,11 @@ func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
|
|||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
||||||
|
|
||||||
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
log.Debugln("[TUN] hijack dns tcp: %s", addrPort.String())
|
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
||||||
|
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -77,10 +78,10 @@ func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gh.TCPIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
|
gh.tcpIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
||||||
id := tunConn.ID()
|
id := tunConn.ID()
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
rAddr := &net.UDPAddr{
|
||||||
@ -89,7 +90,13 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
||||||
|
|
||||||
|
if rAddrPort.Addr() == gh.gateway {
|
||||||
|
_ = tunConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
target := socks5.ParseAddrToSocksAddr(rAddr)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -104,7 +111,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
|
|
||||||
payload := buf[:n]
|
payload := buf[:n]
|
||||||
|
|
||||||
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
@ -130,7 +137,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case gh.UDPIn <- inbound.NewPacket(target, gvPacket, C.TUN):
|
case gh.udpIn <- inbound.NewPacket(target, gvPacket, C.TUN):
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
@ -34,7 +37,7 @@ func (s *gvStack) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a new *gvStack with given options.
|
// New allocates a new *gvStack with given options.
|
||||||
func New(device device.Device, handler adapter.Handler, opts ...option.Option) (ipstack.Stack, error) {
|
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, opts ...option.Option) (ipstack.Stack, error) {
|
||||||
s := &gvStack{
|
s := &gvStack{
|
||||||
Stack: stack.New(stack.Options{
|
Stack: stack.New(stack.Options{
|
||||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||||
@ -52,6 +55,13 @@ func New(device device.Device, handler adapter.Handler, opts ...option.Option) (
|
|||||||
device: device,
|
device: device,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler := &gvHandler{
|
||||||
|
gateway: tunAddress.Masked().Addr().Next(),
|
||||||
|
dnsHijack: dnsHijack,
|
||||||
|
tcpIn: tcpIn,
|
||||||
|
udpIn: udpIn,
|
||||||
|
}
|
||||||
|
|
||||||
// Generate unique NIC id.
|
// Generate unique NIC id.
|
||||||
nicID := tcpip.NICID(s.Stack.UniqueID())
|
nicID := tcpip.NICID(s.Stack.UniqueID())
|
||||||
|
|
||||||
|
@ -27,10 +27,8 @@ func StartListener(device io.ReadWriteCloser, gateway, portal, broadcast netip.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *StackListener) Close() error {
|
func (t *StackListener) Close() error {
|
||||||
_ = t.tcp.Close()
|
|
||||||
_ = t.udp.Close()
|
_ = t.udp.Close()
|
||||||
|
return t.tcp.Close()
|
||||||
return t.device.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *StackListener) TCP() *nat.TCP {
|
func (t *StackListener) TCP() *nat.TCP {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
@ -28,6 +29,8 @@ type sysStack struct {
|
|||||||
device device.Device
|
device device.Device
|
||||||
|
|
||||||
closed bool
|
closed bool
|
||||||
|
once sync.Once
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysStack) Close() error {
|
func (s *sysStack) Close() error {
|
||||||
@ -38,10 +41,12 @@ func (s *sysStack) Close() error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
s.closed = true
|
s.closed = true
|
||||||
if s.stack != nil {
|
|
||||||
return s.stack.Close()
|
err := s.stack.Close()
|
||||||
}
|
|
||||||
return nil
|
s.wg.Wait()
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||||
@ -67,16 +72,10 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
_ = tcp.Close()
|
_ = tcp.Close()
|
||||||
}(stack.TCP())
|
}(stack.TCP())
|
||||||
|
|
||||||
defer log.Debugln("TCP: closed")
|
|
||||||
|
|
||||||
for !ipStack.closed {
|
for !ipStack.closed {
|
||||||
if err = stack.TCP().SetDeadline(time.Time{}); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := stack.TCP().Accept()
|
conn, err := stack.TCP().Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("Accept connection: %v", err)
|
log.Debugln("[STACK] accept connection error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +145,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
|
|
||||||
tcpIn <- context.NewConnContext(conn, metadata)
|
tcpIn <- context.NewConnContext(conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipStack.wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
udp := func() {
|
udp := func() {
|
||||||
@ -153,14 +154,13 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
_ = udp.Close()
|
_ = udp.Close()
|
||||||
}(stack.UDP())
|
}(stack.UDP())
|
||||||
|
|
||||||
defer log.Debugln("UDP: closed")
|
|
||||||
|
|
||||||
for !ipStack.closed {
|
for !ipStack.closed {
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
|
|
||||||
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
|
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
_ = pool.Put(buf)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
raw := buf[:n]
|
raw := buf[:n]
|
||||||
@ -169,7 +169,7 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
|
|
||||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
||||||
|
|
||||||
if rAddrPort.Addr().IsLoopback() {
|
if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway {
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@ -209,17 +209,23 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipStack.wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
go tcp()
|
ipStack.once.Do(func() {
|
||||||
|
ipStack.wg.Add(1)
|
||||||
|
go tcp()
|
||||||
|
|
||||||
numUDPWorkers := 4
|
numUDPWorkers := 4
|
||||||
if num := runtime.GOMAXPROCS(0); num > numUDPWorkers {
|
if num := runtime.GOMAXPROCS(0); num > numUDPWorkers {
|
||||||
numUDPWorkers = num
|
numUDPWorkers = num
|
||||||
}
|
}
|
||||||
for i := 0; i < numUDPWorkers; i++ {
|
for i := 0; i < numUDPWorkers; i++ {
|
||||||
go udp()
|
ipStack.wg.Add(1)
|
||||||
}
|
go udp()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return ipStack, nil
|
return ipStack, nil
|
||||||
}
|
}
|
||||||
|
@ -67,13 +67,7 @@ func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, u
|
|||||||
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
|
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tunStack, err = gvisor.New(tunDevice,
|
tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn, option.WithDefault())
|
||||||
&gvisor.GVHandler{
|
|
||||||
DNSAdds: tunConf.DNSHijack,
|
|
||||||
TCPIn: tcpIn, UDPIn: udpIn,
|
|
||||||
},
|
|
||||||
option.WithDefault(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunDevice.Close()
|
_ = tunDevice.Close()
|
||||||
|
@ -5,20 +5,16 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type portReal struct {
|
|
||||||
portStart int
|
|
||||||
portEnd int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Port struct {
|
type Port struct {
|
||||||
*Base
|
*Base
|
||||||
adapter string
|
adapter string
|
||||||
port string
|
port string
|
||||||
isSource bool
|
isSource bool
|
||||||
portList []portReal
|
portList []utils.Range[uint16]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Port) RuleType() C.RuleType {
|
func (p *Port) RuleType() C.RuleType {
|
||||||
@ -45,17 +41,13 @@ func (p *Port) Payload() string {
|
|||||||
|
|
||||||
func (p *Port) matchPortReal(portRef string) bool {
|
func (p *Port) matchPortReal(portRef string) bool {
|
||||||
port, _ := strconv.Atoi(portRef)
|
port, _ := strconv.Atoi(portRef)
|
||||||
var rs bool
|
|
||||||
for _, pr := range p.portList {
|
for _, pr := range p.portList {
|
||||||
if pr.portEnd == -1 {
|
if pr.Contains(uint16(port)) {
|
||||||
rs = port == pr.portStart
|
|
||||||
} else {
|
|
||||||
rs = port >= pr.portStart && port <= pr.portEnd
|
|
||||||
}
|
|
||||||
if rs {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +57,7 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
|||||||
return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error())
|
return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var portList []portReal
|
var portRange []utils.Range[uint16]
|
||||||
for _, p := range ports {
|
for _, p := range ports {
|
||||||
if p == "" {
|
if p == "" {
|
||||||
continue
|
continue
|
||||||
@ -84,23 +76,18 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
|||||||
|
|
||||||
switch subPortsLen {
|
switch subPortsLen {
|
||||||
case 1:
|
case 1:
|
||||||
portList = append(portList, portReal{int(portStart), -1})
|
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portStart)))
|
||||||
case 2:
|
case 2:
|
||||||
portEnd, err := strconv.ParseUint(strings.Trim(subPorts[1], "[ ]"), 10, 16)
|
portEnd, err := strconv.ParseUint(strings.Trim(subPorts[1], "[ ]"), 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errPayload
|
return nil, errPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldReverse := portStart > portEnd
|
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portEnd)))
|
||||||
if shouldReverse {
|
|
||||||
portList = append(portList, portReal{int(portEnd), int(portStart)})
|
|
||||||
} else {
|
|
||||||
portList = append(portList, portReal{int(portStart), int(portEnd)})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(portList) == 0 {
|
if len(portRange) == 0 {
|
||||||
return nil, errPayload
|
return nil, errPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +96,7 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
|||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
port: port,
|
port: port,
|
||||||
isSource: isSource,
|
isSource: isSource,
|
||||||
portList: portList,
|
portList: portRange,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ var (
|
|||||||
mode = Rule
|
mode = Rule
|
||||||
|
|
||||||
// default timeout for UDP session
|
// default timeout for UDP session
|
||||||
udpTimeout = 60 * time.Second
|
udpTimeout = 60 * time.Second
|
||||||
|
procesCache string
|
||||||
snifferDispatcher *sniffer.SnifferDispatcher
|
failTotal int
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -171,11 +171,17 @@ func preHandleMetadata(metadata *C.Metadata) error {
|
|||||||
if err == nil && P.ShouldFindProcess(metadata) {
|
if err == nil && P.ShouldFindProcess(metadata) {
|
||||||
path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort)
|
path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[Process] find process %s: %v", metadata.String(), err)
|
if failTotal < 20 {
|
||||||
|
log.Debugln("[Process] find process %s: %v", metadata.String(), err)
|
||||||
|
failTotal++
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Debugln("[Process] %s from process %s", metadata.String(), path)
|
|
||||||
metadata.Process = filepath.Base(path)
|
metadata.Process = filepath.Base(path)
|
||||||
metadata.ProcessPath = path
|
metadata.ProcessPath = path
|
||||||
|
if procesCache == metadata.Process {
|
||||||
|
log.Debugln("[Process] %s from process %s", metadata.String(), path)
|
||||||
|
}
|
||||||
|
procesCache = metadata.Process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user