Compare commits

..

7 Commits

20 changed files with 220 additions and 133 deletions

View File

@ -152,7 +152,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
func (v *Vless) StreamPacketConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ip, err := resolver.ResolveFirstIP(metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
@ -245,7 +245,7 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
if v.transport != nil && len(opts) == 0 {
// vless use stream-oriented udp with a special address, so we needs a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ip, err := resolver.ResolveFirstIP(metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}

View File

@ -203,7 +203,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
func (v *Vmess) StreamPacketConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ip, err := resolver.ResolveFirstIP(metadata.Host)
if err != nil {
return c, fmt.Errorf("can't resolve ip: %w", err)
}
@ -255,7 +255,7 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
if v.transport != nil && len(opts) == 0 {
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ip, err := resolver.ResolveFirstIP(metadata.Host)
if err != nil {
return nil, fmt.Errorf("can't resolve ip: %w", err)
}

View File

@ -3,6 +3,8 @@ package outboundgroup
import (
"time"
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
)
@ -11,14 +13,19 @@ const (
defaultGetProxiesDuration = time.Second * 5
)
var defaultRejectProxy = adapter.NewProxy(outbound.NewReject())
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
proxies := []C.Proxy{}
for _, provider := range providers {
for _, pd := range providers {
if touch {
proxies = append(proxies, provider.ProxiesWithTouch()...)
proxies = append(proxies, pd.ProxiesWithTouch()...)
} else {
proxies = append(proxies, provider.Proxies()...)
proxies = append(proxies, pd.Proxies()...)
}
}
if len(proxies) == 0 {
proxies = append(proxies, defaultRejectProxy)
}
return proxies
}

View File

@ -78,30 +78,18 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
return nil, errDuplicateProvider
}
// select don't need health check
if groupOption.Type == "select" || groupOption.Type == "relay" {
hc := provider.NewHealthCheck(ps, "", 0, true)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
}
providers = append(providers, pd)
providersMap[groupName] = pd
} else {
if groupOption.URL == "" || groupOption.Interval == 0 {
return nil, errMissHealthCheck
}
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
}
providers = append(providers, pd)
providersMap[groupName] = pd
hc, err := newHealthCheck(ps, groupOption)
if err != nil {
return nil, err
}
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
}
providers = append(providers, pd)
providersMap[groupName] = pd
}
if len(groupOption.Use) != 0 {
@ -109,7 +97,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
if err != nil {
return nil, err
}
providers = append(providers, list...)
if groupOption.Type == "fallback" {
providers = append(list, providers...)
} else {
providers = append(providers, list...)
}
}
var group C.ProxyAdapter
@ -163,22 +156,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
}
if filterRegx != nil {
var hc *provider.HealthCheck
if groupOption.Type == "select" || groupOption.Type == "relay" {
hc = provider.NewHealthCheck([]C.Proxy{}, "", 0, true)
} else {
if groupOption.URL == "" || groupOption.Interval == 0 {
return nil, errMissHealthCheck
}
hc = provider.NewHealthCheck([]C.Proxy{}, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
hc, err := newHealthCheck([]C.Proxy{}, groupOption)
if err != nil {
return nil, err
}
if _, ok = mapping[groupName]; ok {
groupName += "->" + p.Name()
gName := groupName
if _, ok = mapping[gName]; ok {
gName = groupName + " -> " + p.Name()
}
pd := p.(*provider.ProxySetProvider)
p = provider.NewProxyFilterProvider(groupName, pd, hc, filterRegx)
p = provider.NewProxyFilterProvider(gName, pd, hc, filterRegx)
pd.RegisterProvidersInUse(p)
}
@ -186,3 +175,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
}
return ps, nil
}
func newHealthCheck(ps []C.Proxy, groupOption *GroupCommonOption) (*provider.HealthCheck, error) {
var hc *provider.HealthCheck
// select don't need health check
if groupOption.Type == "select" || groupOption.Type == "relay" {
hc = provider.NewHealthCheck(ps, "", 0, true)
} else {
if groupOption.URL == "" || groupOption.Interval == 0 {
return nil, errMissHealthCheck
}
hc = provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
}
return hc, nil
}

View File

@ -98,7 +98,11 @@ func (s *Selector) selectedProxy(touch bool) C.Proxy {
}
func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) *Selector {
selected := providers[0].Proxies()[0].Name()
selected := "REJECT"
if len(providers) != 0 && len(providers[0].Proxies()) != 0 {
selected = providers[0].Proxies()[0].Name()
}
return &Selector{
Base: outbound.NewBase(outbound.BaseOption{
Name: option.Name,

View File

@ -10,7 +10,7 @@ import (
func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
proxyType, existType := mapping["type"].(string)
proxyType, existType := mapping["type"]
if !existType {
return nil, fmt.Errorf("missing type")
}
@ -19,7 +19,7 @@ func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
proxy C.ProxyAdapter
err error
)
switch proxyType {
switch proxyType.(string) {
case "ss":
ssOption := &outbound.ShadowSocksOption{}
err = decoder.Decode(mapping, ssOption)

View File

@ -25,10 +25,16 @@ type HealthCheck struct {
interval uint
lazy bool
lastTouch *atomic.Int64
running *atomic.Bool
done chan struct{}
}
func (hc *HealthCheck) process() {
if hc.running.Load() {
return
}
hc.running.Store(true)
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
go func() {
@ -84,6 +90,10 @@ func (hc *HealthCheck) check() {
}
func (hc *HealthCheck) close() {
if !hc.running.Load() {
return
}
hc.running.Store(false)
hc.done <- struct{}{}
}
@ -94,6 +104,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *He
interval: interval,
lazy: lazy,
lastTouch: atomic.NewInt64(0),
running: atomic.NewBool(false),
done: make(chan struct{}, 1),
}
}

View File

@ -234,7 +234,9 @@ func (pf *proxyFilterProvider) HealthCheck() {
}
func (pf *proxyFilterProvider) Update() error {
var proxies []C.Proxy
pf.healthCheck.close()
proxies := []C.Proxy{}
if pf.filter != nil {
for _, proxy := range pf.psd.Proxies() {
if !pf.filter.MatchString(proxy.Name()) {
@ -248,6 +250,10 @@ func (pf *proxyFilterProvider) Update() error {
pf.proxies = proxies
pf.healthCheck.setProxy(proxies)
if len(proxies) != 0 && pf.healthCheck.auto() {
go pf.healthCheck.process()
}
return nil
}
@ -286,10 +292,6 @@ func NewProxyFilterProvider(name string, psd *ProxySetProvider, hc *HealthCheck,
_ = pd.Update()
if hc.auto() {
go hc.process()
}
wrapper := &ProxyFilterProvider{pd}
runtime.SetFinalizer(wrapper, stopProxyFilterProvider)
return wrapper

View File

@ -28,6 +28,9 @@ func ShouldFindProcess(metadata *C.Metadata) bool {
if metadata.Process != "" {
return false
}
if metadata.SrcIP.IsUnspecified() {
return true
}
for _, ip := range localIPs {
if ip == metadata.SrcIP {
return true
@ -41,7 +44,7 @@ func AppendLocalIPs(ip ...netip.Addr) {
}
func getLocalIPs() []netip.Addr {
ips := []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()}
var ips []netip.Addr
netInterfaces, err := net.Interfaces()
if err != nil {

View File

@ -37,17 +37,17 @@ var (
)
type Resolver interface {
ResolveIP(host string) (ip netip.Addr, err error)
ResolveIPv4(host string) (ip netip.Addr, err error)
ResolveIPv6(host string) (ip netip.Addr, err error)
ResolveIP(host string, random bool) (ip netip.Addr, err error)
ResolveIPv4(host string, random bool) (ip netip.Addr, err error)
ResolveIPv6(host string, random bool) (ip netip.Addr, err error)
}
// ResolveIPv4 with a host, return ipv4
func ResolveIPv4(host string) (netip.Addr, error) {
return ResolveIPv4WithResolver(host, DefaultResolver)
return resolveIPv4(host, true)
}
func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
func ResolveIPv4WithResolver(host string, r Resolver, random bool) (netip.Addr, error) {
if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data; ip.Is4() {
return ip, nil
@ -56,6 +56,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
ip, err := netip.ParseAddr(host)
if err == nil {
ip = ip.Unmap()
if ip.Is4() {
return ip, nil
}
@ -63,7 +64,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
}
if r != nil {
return r.ResolveIPv4(host)
return r.ResolveIPv4(host, random)
}
if DefaultResolver == nil {
@ -76,7 +77,11 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
return netip.Addr{}, ErrIPNotFound
}
ip := ipAddrs[rand.Intn(len(ipAddrs))].To4()
index := 0
if random {
index = rand.Intn(len(ipAddrs))
}
ip := ipAddrs[index].To4()
if ip == nil {
return netip.Addr{}, ErrIPVersion
}
@ -89,10 +94,10 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
// ResolveIPv6 with a host, return ipv6
func ResolveIPv6(host string) (netip.Addr, error) {
return ResolveIPv6WithResolver(host, DefaultResolver)
return ResolveIPv6WithResolver(host, DefaultResolver, true)
}
func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
func ResolveIPv6WithResolver(host string, r Resolver, random bool) (netip.Addr, error) {
if DisableIPv6 {
return netip.Addr{}, ErrIPv6Disabled
}
@ -112,7 +117,7 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
}
if r != nil {
return r.ResolveIPv6(host)
return r.ResolveIPv6(host, random)
}
if DefaultResolver == nil {
@ -125,25 +130,29 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
return netip.Addr{}, ErrIPNotFound
}
return netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))])), nil
index := 0
if random {
index = rand.Intn(len(ipAddrs))
}
return netip.AddrFrom16(*(*[16]byte)(ipAddrs[index])), nil
}
return netip.Addr{}, ErrIPNotFound
}
// ResolveIPWithResolver same as ResolveIP, but with a resolver
func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
func ResolveIPWithResolver(host string, r Resolver, random bool) (netip.Addr, error) {
if node := DefaultHosts.Search(host); node != nil {
return node.Data, nil
}
if r != nil {
if DisableIPv6 {
return r.ResolveIPv4(host)
return r.ResolveIPv4(host, random)
}
return r.ResolveIP(host)
return r.ResolveIP(host, random)
} else if DisableIPv6 {
return ResolveIPv4(host)
return resolveIPv4(host, random)
}
ip, err := netip.ParseAddr(host)
@ -165,13 +174,18 @@ func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
// ResolveIP with a host, return ip
func ResolveIP(host string) (netip.Addr, error) {
return ResolveIPWithResolver(host, DefaultResolver)
return resolveIP(host, true)
}
// ResolveFirstIP with a host, return ip
func ResolveFirstIP(host string) (netip.Addr, error) {
return resolveIP(host, false)
}
// ResolveIPv4ProxyServerHost proxies server host only
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
return ResolveIPv4WithResolver(host, ProxyServerHostResolver, true)
}
return ResolveIPv4(host)
}
@ -179,7 +193,7 @@ func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
// ResolveIPv6ProxyServerHost proxies server host only
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
return ResolveIPv6WithResolver(host, ProxyServerHostResolver, true)
}
return ResolveIPv6(host)
}
@ -187,7 +201,15 @@ func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
// ResolveProxyServerHost proxies server host only
func ResolveProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPWithResolver(host, ProxyServerHostResolver)
return ResolveIPWithResolver(host, ProxyServerHostResolver, true)
}
return ResolveIP(host)
}
func resolveIP(host string, random bool) (netip.Addr, error) {
return ResolveIPWithResolver(host, DefaultResolver, random)
}
func resolveIPv4(host string, random bool) (netip.Addr, error) {
return ResolveIPv4WithResolver(host, DefaultResolver, random)
}

View File

@ -415,11 +415,11 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
// keep the original order of ProxyGroups in config file
for idx, mapping := range groupsConfig {
groupName, existName := mapping["name"].(string)
groupName, existName := mapping["name"]
if !existName {
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
}
proxyList = append(proxyList, groupName)
proxyList = append(proxyList, groupName.(string))
}
// check if any loop exists and sort the ProxyGroups

View File

@ -36,7 +36,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
if c.r == nil {
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
} else {
if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil {
if ip, err = resolver.ResolveIPWithResolver(c.host, c.r, true); err != nil {
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
}
c.host = ip.String()

View File

@ -94,7 +94,7 @@ func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient {
return nil, err
}
ip, err := resolver.ResolveIPWithResolver(host, r)
ip, err := resolver.ResolveIPWithResolver(host, r, true)
if err != nil {
return nil, err
}

View File

@ -21,6 +21,8 @@ import (
"golang.org/x/sync/singleflight"
)
var _ resolver.Resolver = (*Resolver)(nil)
type dnsClient interface {
Exchange(m *D.Msg) (msg *D.Msg, err error)
ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error)
@ -45,18 +47,18 @@ type Resolver struct {
}
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
func (r *Resolver) ResolveIP(host string, random bool) (ip netip.Addr, err error) {
ch := make(chan netip.Addr, 1)
go func() {
defer close(ch)
ip, err := r.resolveIP(host, D.TypeAAAA)
ip, err := r.resolveIP(host, D.TypeAAAA, random)
if err != nil {
return
}
ch <- ip
}()
ip, err = r.resolveIP(host, D.TypeA)
ip, err = r.resolveIP(host, D.TypeA, random)
if err == nil {
return
}
@ -70,13 +72,13 @@ func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
}
// ResolveIPv4 request with TypeA
func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) {
return r.resolveIP(host, D.TypeA)
func (r *Resolver) ResolveIPv4(host string, random bool) (ip netip.Addr, err error) {
return r.resolveIP(host, D.TypeA, random)
}
// ResolveIPv6 request with TypeAAAA
func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) {
return r.resolveIP(host, D.TypeAAAA)
func (r *Resolver) ResolveIPv6(host string, random bool) (ip netip.Addr, err error) {
return r.resolveIP(host, D.TypeAAAA, random)
}
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
@ -255,9 +257,10 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
return
}
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err error) {
func (r *Resolver) resolveIP(host string, dnsType uint16, random bool) (ip netip.Addr, err error) {
ip, err = netip.ParseAddr(host)
if err == nil {
ip = ip.Unmap()
isIPv4 := ip.Is4()
if dnsType == D.TypeAAAA && !isIPv4 {
return ip, nil
@ -282,7 +285,12 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err er
return netip.Addr{}, resolver.ErrIPNotFound
}
ip = ips[rand.Intn(ipLength)]
index := 0
if random {
index = rand.Intn(ipLength)
}
ip = ips[index]
return
}

12
go.mod
View File

@ -9,26 +9,26 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f
github.com/miekg/dns v1.1.49
github.com/miekg/dns v1.1.50
github.com/oschwald/geoip2-golang v1.7.0
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
github.com/stretchr/testify v1.7.2
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.1
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022
golang.org/x/net v0.0.0-20220622184535-263ec571b305
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v3 v3.0.1
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069
)
require (

24
go.sum
View File

@ -45,8 +45,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8=
github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
@ -62,8 +62,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
@ -78,8 +78,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -97,8 +97,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022 h1:0qjDla5xICC2suMtyRH/QqX3B1btXTfNsIt/i4LFgO0=
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I=
golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
@ -123,8 +123,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -158,5 +158,5 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364 h1:D+X2kUQINrsyxdwX71CUWxxQCGRU4tS6gni5WRYLCjs=
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069 h1:Ly12hwbYd06NuYM2/nssGPgQ9ZVw7xaNs2lc087VW1w=
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=

View File

@ -6,6 +6,7 @@ import (
"net/netip"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
"github.com/Dreamacro/clash/log"
)
func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *UDP, error) {
@ -132,7 +133,11 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
continue
}
port = tab.newConn(tup)
port, err = tab.newConn(tup)
if err != nil {
log.Warnln("[STACK] drop tcp packet by system stack: %v", err)
continue
}
}
ip.SetSourceIP(portal)

View File

@ -1,6 +1,7 @@
package nat
import (
"fmt"
"net/netip"
"sync"
@ -31,6 +32,7 @@ type table struct {
ports [portLength]*list.Element[*binding]
available *list.List[*binding]
mux sync.Mutex
count uint16
}
func (t *table) tupleOf(port uint16) tuple {
@ -60,38 +62,50 @@ func (t *table) portOf(tuple tuple) uint16 {
return portBegin + elm.Value.offset
}
func (t *table) newConn(tuple tuple) uint16 {
func (t *table) newConn(tuple tuple) (uint16, error) {
t.mux.Lock()
elm := t.availableConn()
b := elm.Value
elm, err := t.availableConn()
if err != nil {
t.mux.Unlock()
return 0, err
}
elm.Value.tuple = tuple
t.tuples[tuple] = elm
b.tuple = tuple
t.mux.Unlock()
t.available.MoveToFront(elm)
return portBegin + b.offset
return portBegin + elm.Value.offset, nil
}
func (t *table) availableConn() *list.Element[*binding] {
elm := t.available.Back()
offset := elm.Value.offset
_, ok := t.tuples[t.ports[offset].Value.tuple]
if !ok {
if offset != 0 && offset%portLength == 0 { // resize
func (t *table) availableConn() (*list.Element[*binding], error) {
var elm *list.Element[*binding]
for i := 0; i < portLength; i++ {
elm = t.available.Back()
t.available.MoveToFront(elm)
offset := elm.Value.offset
tup := t.ports[offset].Value.tuple
if t.tuples[tup] != nil && tup.SourceAddr.IsValid() {
continue
}
if t.count == portLength { // resize
tuples := make(map[tuple]*list.Element[*binding], portLength)
maps.Copy(tuples, t.tuples)
t.tuples = tuples
t.count = 1
}
return elm
return elm, nil
}
t.available.MoveToFront(elm)
return t.availableConn()
return nil, fmt.Errorf("too many open files, limits [%d, %d]", portLength, len(t.tuples))
}
func (t *table) closeConn(tuple tuple) {
t.mux.Lock()
delete(t.tuples, tuple)
t.count++
t.mux.Unlock()
}
@ -100,6 +114,7 @@ func newTable() *table {
tuples: make(map[tuple]*list.Element[*binding], portLength),
ports: [portLength]*list.Element[*binding]{},
available: list.New[*binding](),
count: 1,
}
for idx := range result.ports {

View File

@ -16,7 +16,7 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
// local resolve UDP dns
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ip, err := resolver.ResolveFirstIP(metadata.Host)
if err != nil {
return err
}

View File

@ -190,19 +190,6 @@ func preHandleMetadata(metadata *C.Metadata) error {
}
}
// pre resolve process name
srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16)
if err == nil && P.ShouldFindProcess(metadata) {
path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort))
if err != nil {
log.Debugln("[Process] find process %s: %v", metadata.String(), err)
} else {
log.Debugln("[Process] %s from process %s", metadata.String(), path)
metadata.Process = filepath.Base(path)
metadata.ProcessPath = path
}
}
return nil
}
@ -385,7 +372,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
configMux.RLock()
defer configMux.RUnlock()
var resolved bool
var (
resolved bool
processFound bool
)
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
metadata.DstIP = node.Data
@ -404,6 +394,22 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
resolved = true
}
if !processFound && rule.ShouldFindProcess() && P.ShouldFindProcess(metadata) {
processFound = true
srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16)
if err == nil {
path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort))
if err != nil {
log.Debugln("[Process] find process %s: %v", metadata.String(), err)
} else {
log.Debugln("[Process] %s from process %s", metadata.String(), path)
metadata.Process = filepath.Base(path)
metadata.ProcessPath = path
}
}
}
if rule.Match(metadata) {
adapter, ok := proxies[rule.Adapter()]
if !ok {