Compare commits
7 Commits
v1.12.2_tu
...
v1.12.4_tu
Author | SHA1 | Date | |
---|---|---|---|
54e46d1599 | |||
ae6cc1d67d | |||
e1fe8ce3b2 | |||
b9ee4b902f | |||
7731a684b1 | |||
1f12ef069b | |||
e31be4edc2 |
@ -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) {
|
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
|
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveFirstIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("can't resolve ip")
|
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 {
|
if v.transport != nil && len(opts) == 0 {
|
||||||
// vless use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
// vless use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveFirstIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("can't resolve ip")
|
return nil, errors.New("can't resolve ip")
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
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
|
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveFirstIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c, fmt.Errorf("can't resolve ip: %w", err)
|
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 {
|
if v.transport != nil && len(opts) == 0 {
|
||||||
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveFirstIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't resolve ip: %w", err)
|
return nil, fmt.Errorf("can't resolve ip: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter"
|
||||||
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
)
|
)
|
||||||
@ -11,14 +13,19 @@ const (
|
|||||||
defaultGetProxiesDuration = time.Second * 5
|
defaultGetProxiesDuration = time.Second * 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultRejectProxy = adapter.NewProxy(outbound.NewReject())
|
||||||
|
|
||||||
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
|
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
|
||||||
proxies := []C.Proxy{}
|
proxies := []C.Proxy{}
|
||||||
for _, provider := range providers {
|
for _, pd := range providers {
|
||||||
if touch {
|
if touch {
|
||||||
proxies = append(proxies, provider.ProxiesWithTouch()...)
|
proxies = append(proxies, pd.ProxiesWithTouch()...)
|
||||||
} else {
|
} else {
|
||||||
proxies = append(proxies, provider.Proxies()...)
|
proxies = append(proxies, pd.Proxies()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(proxies) == 0 {
|
||||||
|
proxies = append(proxies, defaultRejectProxy)
|
||||||
|
}
|
||||||
return proxies
|
return proxies
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,8 @@ type LoadBalance struct {
|
|||||||
var errStrategy = errors.New("unsupported strategy")
|
var errStrategy = errors.New("unsupported strategy")
|
||||||
|
|
||||||
func parseStrategy(config map[string]any) string {
|
func parseStrategy(config map[string]any) string {
|
||||||
if elm, ok := config["strategy"]; ok {
|
if strategy, ok := config["strategy"].(string); ok {
|
||||||
if strategy, ok := elm.(string); ok {
|
return strategy
|
||||||
return strategy
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "consistent-hashing"
|
return "consistent-hashing"
|
||||||
}
|
}
|
||||||
|
@ -78,30 +78,18 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
return nil, errDuplicateProvider
|
return nil, errDuplicateProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// select don't need health check
|
hc, err := newHealthCheck(ps, groupOption)
|
||||||
if groupOption.Type == "select" || groupOption.Type == "relay" {
|
if err != nil {
|
||||||
hc := provider.NewHealthCheck(ps, "", 0, true)
|
return nil, err
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
if len(groupOption.Use) != 0 {
|
||||||
@ -109,7 +97,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
providers = append(providers, list...)
|
|
||||||
|
if groupOption.Type == "fallback" {
|
||||||
|
providers = append(list, providers...)
|
||||||
|
} else {
|
||||||
|
providers = append(providers, list...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var group C.ProxyAdapter
|
var group C.ProxyAdapter
|
||||||
@ -163,22 +156,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if filterRegx != nil {
|
if filterRegx != nil {
|
||||||
var hc *provider.HealthCheck
|
hc, err := newHealthCheck([]C.Proxy{}, groupOption)
|
||||||
if groupOption.Type == "select" || groupOption.Type == "relay" {
|
if err != nil {
|
||||||
hc = provider.NewHealthCheck([]C.Proxy{}, "", 0, true)
|
return nil, err
|
||||||
} else {
|
|
||||||
if groupOption.URL == "" || groupOption.Interval == 0 {
|
|
||||||
return nil, errMissHealthCheck
|
|
||||||
}
|
|
||||||
hc = provider.NewHealthCheck([]C.Proxy{}, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok = mapping[groupName]; ok {
|
gName := groupName
|
||||||
groupName += "->" + p.Name()
|
if _, ok = mapping[gName]; ok {
|
||||||
|
gName = groupName + " -> " + p.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
pd := p.(*provider.ProxySetProvider)
|
pd := p.(*provider.ProxySetProvider)
|
||||||
p = provider.NewProxyFilterProvider(groupName, pd, hc, filterRegx)
|
p = provider.NewProxyFilterProvider(gName, pd, hc, filterRegx)
|
||||||
pd.RegisterProvidersInUse(p)
|
pd.RegisterProvidersInUse(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,3 +175,18 @@ func getProviders(mapping map[string]types.ProxyProvider, groupOption *GroupComm
|
|||||||
}
|
}
|
||||||
return ps, nil
|
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
|
||||||
|
}
|
||||||
|
@ -98,7 +98,11 @@ func (s *Selector) selectedProxy(touch bool) C.Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) *Selector {
|
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{
|
return &Selector{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
|
@ -125,10 +125,8 @@ func parseURLTestOption(config map[string]any) []urlTestOption {
|
|||||||
opts := []urlTestOption{}
|
opts := []urlTestOption{}
|
||||||
|
|
||||||
// tolerance
|
// tolerance
|
||||||
if elm, ok := config["tolerance"]; ok {
|
if tolerance, ok := config["tolerance"].(int); ok {
|
||||||
if tolerance, ok := elm.(int); ok {
|
opts = append(opts, urlTestWithTolerance(uint16(tolerance)))
|
||||||
opts = append(opts, urlTestWithTolerance(uint16(tolerance)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
@ -14,6 +15,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
fileMode os.FileMode = 0o666
|
fileMode os.FileMode = 0o666
|
||||||
dirMode os.FileMode = 0o755
|
dirMode os.FileMode = 0o755
|
||||||
|
|
||||||
|
commentRegx = regexp.MustCompile(`(.*#.*\n)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser[V any] func([]byte) (V, error)
|
type parser[V any] func([]byte) (V, error)
|
||||||
@ -168,6 +171,18 @@ func safeWrite(path string, buf []byte) error {
|
|||||||
return os.WriteFile(path, buf, fileMode)
|
return os.WriteFile(path, buf, fileMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeComment(buf []byte) []byte {
|
||||||
|
arr := commentRegx.FindAllSubmatch(buf, -1)
|
||||||
|
for _, subs := range arr {
|
||||||
|
sub := subs[0]
|
||||||
|
if !bytes.HasPrefix(bytes.TrimLeft(sub, " "), []byte("#")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf = bytes.Replace(buf, sub, []byte(""), 1)
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser parser[V], onUpdate func(V)) *fetcher[V] {
|
func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser parser[V], onUpdate func(V)) *fetcher[V] {
|
||||||
var ticker *time.Ticker
|
var ticker *time.Ticker
|
||||||
if interval != 0 {
|
if interval != 0 {
|
||||||
|
@ -25,10 +25,16 @@ type HealthCheck struct {
|
|||||||
interval uint
|
interval uint
|
||||||
lazy bool
|
lazy bool
|
||||||
lastTouch *atomic.Int64
|
lastTouch *atomic.Int64
|
||||||
|
running *atomic.Bool
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) process() {
|
func (hc *HealthCheck) process() {
|
||||||
|
if hc.running.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hc.running.Store(true)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -84,6 +90,10 @@ func (hc *HealthCheck) check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) close() {
|
func (hc *HealthCheck) close() {
|
||||||
|
if !hc.running.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hc.running.Store(false)
|
||||||
hc.done <- struct{}{}
|
hc.done <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +104,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *He
|
|||||||
interval: interval,
|
interval: interval,
|
||||||
lazy: lazy,
|
lazy: lazy,
|
||||||
lastTouch: atomic.NewInt64(0),
|
lastTouch: atomic.NewInt64(0),
|
||||||
|
running: atomic.NewBool(false),
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,9 @@ func (pf *proxyFilterProvider) HealthCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pf *proxyFilterProvider) Update() error {
|
func (pf *proxyFilterProvider) Update() error {
|
||||||
var proxies []C.Proxy
|
pf.healthCheck.close()
|
||||||
|
|
||||||
|
proxies := []C.Proxy{}
|
||||||
if pf.filter != nil {
|
if pf.filter != nil {
|
||||||
for _, proxy := range pf.psd.Proxies() {
|
for _, proxy := range pf.psd.Proxies() {
|
||||||
if !pf.filter.MatchString(proxy.Name()) {
|
if !pf.filter.MatchString(proxy.Name()) {
|
||||||
@ -248,6 +250,10 @@ func (pf *proxyFilterProvider) Update() error {
|
|||||||
|
|
||||||
pf.proxies = proxies
|
pf.proxies = proxies
|
||||||
pf.healthCheck.setProxy(proxies)
|
pf.healthCheck.setProxy(proxies)
|
||||||
|
|
||||||
|
if len(proxies) != 0 && pf.healthCheck.auto() {
|
||||||
|
go pf.healthCheck.process()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,10 +292,6 @@ func NewProxyFilterProvider(name string, psd *ProxySetProvider, hc *HealthCheck,
|
|||||||
|
|
||||||
_ = pd.Update()
|
_ = pd.Update()
|
||||||
|
|
||||||
if hc.auto() {
|
|
||||||
go hc.process()
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper := &ProxyFilterProvider{pd}
|
wrapper := &ProxyFilterProvider{pd}
|
||||||
runtime.SetFinalizer(wrapper, stopProxyFilterProvider)
|
runtime.SetFinalizer(wrapper, stopProxyFilterProvider)
|
||||||
return wrapper
|
return wrapper
|
||||||
@ -319,12 +321,13 @@ func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVer
|
|||||||
|
|
||||||
proxies := []C.Proxy{}
|
proxies := []C.Proxy{}
|
||||||
for idx, mapping := range schema.Proxies {
|
for idx, mapping := range schema.Proxies {
|
||||||
if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) {
|
name, ok := mapping["name"].(string)
|
||||||
|
if ok && len(filter) > 0 && !filterReg.MatchString(name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefixName != "" {
|
if prefixName != "" {
|
||||||
mapping["name"] = prefixName + mapping["name"].(string)
|
mapping["name"] = prefixName + name
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy, err := adapter.ParseProxy(mapping, forceCertVerify)
|
proxy, err := adapter.ParseProxy(mapping, forceCertVerify)
|
||||||
|
@ -103,7 +103,7 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, nil
|
return removeComment(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPVehicle(url string, path string, header http.Header) *HTTPVehicle {
|
func NewHTTPVehicle(url string, path string, header http.Header) *HTTPVehicle {
|
||||||
|
@ -2,6 +2,7 @@ package cert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
)
|
)
|
||||||
@ -9,10 +10,14 @@ import (
|
|||||||
// DomainTrieCertsStorage cache wildcard certificates
|
// DomainTrieCertsStorage cache wildcard certificates
|
||||||
type DomainTrieCertsStorage struct {
|
type DomainTrieCertsStorage struct {
|
||||||
certsCache *trie.DomainTrie[*tls.Certificate]
|
certsCache *trie.DomainTrie[*tls.Certificate]
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets the certificate from the storage
|
// Get gets the certificate from the storage
|
||||||
func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
|
func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
|
||||||
ca := c.certsCache.Search(key)
|
ca := c.certsCache.Search(key)
|
||||||
if ca == nil {
|
if ca == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -22,7 +27,9 @@ func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
|
|||||||
|
|
||||||
// Set saves the certificate to the storage
|
// Set saves the certificate to the storage
|
||||||
func (c *DomainTrieCertsStorage) Set(key string, cert *tls.Certificate) {
|
func (c *DomainTrieCertsStorage) Set(key string, cert *tls.Certificate) {
|
||||||
|
c.lock.Lock()
|
||||||
_ = c.certsCache.Insert(key, cert)
|
_ = c.certsCache.Insert(key, cert)
|
||||||
|
c.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainTrieCertsStorage() *DomainTrieCertsStorage {
|
func NewDomainTrieCertsStorage() *DomainTrieCertsStorage {
|
||||||
|
@ -29,13 +29,13 @@ func bindMarkToControl(mark int, chain controlFn) controlFn {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Control(func(fd uintptr) {
|
var innerErr error
|
||||||
switch network {
|
err = c.Control(func(fd uintptr) {
|
||||||
case "tcp4", "udp4":
|
innerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
|
||||||
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
|
|
||||||
case "tcp6", "udp6":
|
|
||||||
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
if err == nil && innerErr != nil {
|
||||||
|
err = innerErr
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,7 @@ package process
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -23,49 +19,3 @@ const (
|
|||||||
func FindProcessName(network string, srcIP netip.Addr, srcPort int) (string, error) {
|
func FindProcessName(network string, srcIP netip.Addr, srcPort int) (string, error) {
|
||||||
return findProcessName(network, srcIP, srcPort)
|
return findProcessName(network, srcIP, srcPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShouldFindProcess(metadata *C.Metadata) bool {
|
|
||||||
if metadata.Process != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, ip := range localIPs {
|
|
||||||
if ip == metadata.SrcIP {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func AppendLocalIPs(ip ...netip.Addr) {
|
|
||||||
localIPs = append(ip, localIPs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLocalIPs() []netip.Addr {
|
|
||||||
ips := []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()}
|
|
||||||
|
|
||||||
netInterfaces, err := net.Interfaces()
|
|
||||||
if err != nil {
|
|
||||||
ips = append(ips, netip.AddrFrom4([4]byte{127, 0, 0, 1}), nnip.IpToAddr(net.IPv6loopback))
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(netInterfaces); i++ {
|
|
||||||
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
|
|
||||||
adds, _ := netInterfaces[i].Addrs()
|
|
||||||
|
|
||||||
for _, address := range adds {
|
|
||||||
if ipNet, ok := address.(*net.IPNet); ok {
|
|
||||||
ips = append(ips, nnip.IpToAddr(ipNet.IP))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
var localIPs []netip.Addr
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
localIPs = getLocalIPs()
|
|
||||||
}
|
|
||||||
|
@ -6,8 +6,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,10 +61,10 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
|||||||
switch {
|
switch {
|
||||||
case flag&0x1 > 0 && isIPv4:
|
case flag&0x1 > 0 && isIPv4:
|
||||||
// ipv4
|
// ipv4
|
||||||
srcIP = nnip.IpToAddr(buf[inp+76 : inp+80])
|
srcIP, _ = netip.AddrFromSlice(buf[inp+76 : inp+80])
|
||||||
case flag&0x2 > 0 && !isIPv4:
|
case flag&0x2 > 0 && !isIPv4:
|
||||||
// ipv6
|
// ipv6
|
||||||
srcIP = nnip.IpToAddr(buf[inp+64 : inp+80])
|
srcIP, _ = netip.AddrFromSlice(buf[inp+64 : inp+80])
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -135,10 +134,10 @@ func (s *searcher) Search(buf []byte, ip netip.Addr, port uint16, isTCP bool) (u
|
|||||||
switch {
|
switch {
|
||||||
case flag&0x1 > 0 && isIPv4:
|
case flag&0x1 > 0 && isIPv4:
|
||||||
// ipv4
|
// ipv4
|
||||||
srcIP = nnip.IpToAddr(buf[inp+s.ip : inp+s.ip+4])
|
srcIP, _ = netip.AddrFromSlice(buf[inp+s.ip : inp+s.ip+4])
|
||||||
case flag&0x2 > 0 && !isIPv4:
|
case flag&0x2 > 0 && !isIPv4:
|
||||||
// ipv6
|
// ipv6
|
||||||
srcIP = nnip.IpToAddr(buf[inp+s.ip-12 : inp+s.ip+4])
|
srcIP, _ = netip.AddrFromSlice(buf[inp+s.ip-12 : inp+s.ip+4])
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@ -132,7 +131,7 @@ func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := nnip.IpToAddr(row[s.ip : s.ip+s.ipSize])
|
srcIP, _ := netip.AddrFromSlice(row[s.ip : s.ip+s.ipSize])
|
||||||
// windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto
|
// windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto
|
||||||
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
|
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
|
||||||
continue
|
continue
|
||||||
|
@ -37,17 +37,17 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Resolver interface {
|
type Resolver interface {
|
||||||
ResolveIP(host string) (ip netip.Addr, err error)
|
ResolveIP(host string, random bool) (ip netip.Addr, err error)
|
||||||
ResolveIPv4(host string) (ip netip.Addr, err error)
|
ResolveIPv4(host string, random bool) (ip netip.Addr, err error)
|
||||||
ResolveIPv6(host string) (ip netip.Addr, err error)
|
ResolveIPv6(host string, random bool) (ip netip.Addr, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 with a host, return ipv4
|
// ResolveIPv4 with a host, return ipv4
|
||||||
func ResolveIPv4(host string) (netip.Addr, error) {
|
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 node := DefaultHosts.Search(host); node != nil {
|
||||||
if ip := node.Data; ip.Is4() {
|
if ip := node.Data; ip.Is4() {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
@ -56,6 +56,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
|
|
||||||
ip, err := netip.ParseAddr(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
ip = ip.Unmap()
|
||||||
if ip.Is4() {
|
if ip.Is4() {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
@ -63,7 +64,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
return r.ResolveIPv4(host)
|
return r.ResolveIPv4(host, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
if DefaultResolver == nil {
|
if DefaultResolver == nil {
|
||||||
@ -76,7 +77,11 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
return netip.Addr{}, ErrIPNotFound
|
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 {
|
if ip == nil {
|
||||||
return netip.Addr{}, ErrIPVersion
|
return netip.Addr{}, ErrIPVersion
|
||||||
}
|
}
|
||||||
@ -89,10 +94,10 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
|
|
||||||
// ResolveIPv6 with a host, return ipv6
|
// ResolveIPv6 with a host, return ipv6
|
||||||
func ResolveIPv6(host string) (netip.Addr, error) {
|
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 {
|
if DisableIPv6 {
|
||||||
return netip.Addr{}, ErrIPv6Disabled
|
return netip.Addr{}, ErrIPv6Disabled
|
||||||
}
|
}
|
||||||
@ -112,7 +117,7 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
return r.ResolveIPv6(host)
|
return r.ResolveIPv6(host, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
if DefaultResolver == nil {
|
if DefaultResolver == nil {
|
||||||
@ -125,25 +130,29 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
return netip.Addr{}, ErrIPNotFound
|
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
|
return netip.Addr{}, ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPWithResolver same as ResolveIP, but with a resolver
|
// 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 {
|
if node := DefaultHosts.Search(host); node != nil {
|
||||||
return node.Data, nil
|
return node.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
if DisableIPv6 {
|
if DisableIPv6 {
|
||||||
return r.ResolveIPv4(host)
|
return r.ResolveIPv4(host, random)
|
||||||
}
|
}
|
||||||
return r.ResolveIP(host)
|
return r.ResolveIP(host, random)
|
||||||
} else if DisableIPv6 {
|
} else if DisableIPv6 {
|
||||||
return ResolveIPv4(host)
|
return resolveIPv4(host, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := netip.ParseAddr(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
@ -165,13 +174,18 @@ func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
|
|||||||
|
|
||||||
// ResolveIP with a host, return ip
|
// ResolveIP with a host, return ip
|
||||||
func ResolveIP(host string) (netip.Addr, error) {
|
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
|
// ResolveIPv4ProxyServerHost proxies server host only
|
||||||
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
|
return ResolveIPv4WithResolver(host, ProxyServerHostResolver, true)
|
||||||
}
|
}
|
||||||
return ResolveIPv4(host)
|
return ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
@ -179,7 +193,7 @@ func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
|||||||
// ResolveIPv6ProxyServerHost proxies server host only
|
// ResolveIPv6ProxyServerHost proxies server host only
|
||||||
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
|
return ResolveIPv6WithResolver(host, ProxyServerHostResolver, true)
|
||||||
}
|
}
|
||||||
return ResolveIPv6(host)
|
return ResolveIPv6(host)
|
||||||
}
|
}
|
||||||
@ -187,7 +201,15 @@ func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
|||||||
// ResolveProxyServerHost proxies server host only
|
// ResolveProxyServerHost proxies server host only
|
||||||
func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPWithResolver(host, ProxyServerHostResolver)
|
return ResolveIPWithResolver(host, ProxyServerHostResolver, true)
|
||||||
}
|
}
|
||||||
return ResolveIP(host)
|
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)
|
||||||
|
}
|
||||||
|
@ -99,12 +99,13 @@ type Profile struct {
|
|||||||
|
|
||||||
// Tun config
|
// Tun config
|
||||||
type Tun struct {
|
type Tun struct {
|
||||||
Enable bool `yaml:"enable" json:"enable"`
|
Enable bool `yaml:"enable" json:"enable"`
|
||||||
Device string `yaml:"device" json:"device"`
|
Device string `yaml:"device" json:"device"`
|
||||||
Stack C.TUNStack `yaml:"stack" json:"stack"`
|
Stack C.TUNStack `yaml:"stack" json:"stack"`
|
||||||
DNSHijack []C.DNSUrl `yaml:"dns-hijack" json:"dns-hijack"`
|
DNSHijack []C.DNSUrl `yaml:"dns-hijack" json:"dns-hijack"`
|
||||||
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
||||||
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
|
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
|
||||||
|
TunAddressPrefix *netip.Prefix `yaml:"_" json:"_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPTables config
|
// IPTables config
|
||||||
|
@ -36,7 +36,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
|||||||
if c.r == nil {
|
if c.r == nil {
|
||||||
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
|
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
|
||||||
} else {
|
} 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)
|
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
|
||||||
}
|
}
|
||||||
c.host = ip.String()
|
c.host = ip.String()
|
||||||
|
@ -94,7 +94,7 @@ func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := resolver.ResolveIPWithResolver(host, r)
|
ip, err := resolver.ResolveIPWithResolver(host, r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ resolver.Resolver = (*Resolver)(nil)
|
||||||
|
|
||||||
type dnsClient interface {
|
type dnsClient interface {
|
||||||
Exchange(m *D.Msg) (msg *D.Msg, err error)
|
Exchange(m *D.Msg) (msg *D.Msg, err error)
|
||||||
ExchangeContext(ctx context.Context, 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
|
// 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)
|
ch := make(chan netip.Addr, 1)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
ip, err := r.resolveIP(host, D.TypeAAAA)
|
ip, err := r.resolveIP(host, D.TypeAAAA, random)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch <- ip
|
ch <- ip
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ip, err = r.resolveIP(host, D.TypeA)
|
ip, err = r.resolveIP(host, D.TypeA, random)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -70,13 +72,13 @@ func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 request with TypeA
|
// ResolveIPv4 request with TypeA
|
||||||
func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) {
|
func (r *Resolver) ResolveIPv4(host string, random bool) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeA)
|
return r.resolveIP(host, D.TypeA, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6 request with TypeAAAA
|
// ResolveIPv6 request with TypeAAAA
|
||||||
func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) {
|
func (r *Resolver) ResolveIPv6(host string, random bool) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeAAAA)
|
return r.resolveIP(host, D.TypeAAAA, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
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
|
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)
|
ip, err = netip.ParseAddr(host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
ip = ip.Unmap()
|
||||||
isIPv4 := ip.Is4()
|
isIPv4 := ip.Is4()
|
||||||
if dnsType == D.TypeAAAA && !isIPv4 {
|
if dnsType == D.TypeAAAA && !isIPv4 {
|
||||||
return ip, nil
|
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
|
return netip.Addr{}, resolver.ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ips[rand.Intn(ipLength)]
|
index := 0
|
||||||
|
if random {
|
||||||
|
index = rand.Intn(ipLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = ips[index]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
go.mod
10
go.mod
@ -9,7 +9,7 @@ require (
|
|||||||
github.com/gofrs/uuid v4.2.0+incompatible
|
github.com/gofrs/uuid v4.2.0+incompatible
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f
|
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/oschwald/geoip2-golang v1.7.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.7.2
|
github.com/stretchr/testify v1.7.2
|
||||||
@ -17,18 +17,18 @@ require (
|
|||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
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-20220525230936-793ad666bf5e
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d
|
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
|
golang.org/x/net v0.0.0-20220630215102-69896b714898
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4
|
golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
|
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069
|
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
20
go.sum
20
go.sum
@ -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/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-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/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.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||||
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
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 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8=
|
||||||
github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=
|
github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=
|
||||||
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
|
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
|
||||||
@ -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-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-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-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-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
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 h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
|
||||||
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
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=
|
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-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-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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=
|
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220630215102-69896b714898/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-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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
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-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-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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069 h1:Ly12hwbYd06NuYM2/nssGPgQ9ZVw7xaNs2lc087VW1w=
|
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3 h1:rnajnEGeshXCEr3k/7pWAM6zXh1Ws4jkZgWuWXjCJSw=
|
||||||
gvisor.dev/gvisor v0.0.0-20220616232550-d004a30ec069/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||||
|
@ -82,7 +82,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
updateHosts(cfg.Hosts)
|
updateHosts(cfg.Hosts)
|
||||||
updateMitm(cfg.Mitm)
|
updateMitm(cfg.Mitm)
|
||||||
updateProfile(cfg)
|
updateProfile(cfg)
|
||||||
updateDNS(cfg.DNS, cfg.General.Tun)
|
updateDNS(cfg.DNS, &cfg.General.Tun)
|
||||||
updateGeneral(cfg.General, force)
|
updateGeneral(cfg.General, force)
|
||||||
updateIPTables(cfg)
|
updateIPTables(cfg)
|
||||||
updateExperimental(cfg)
|
updateExperimental(cfg)
|
||||||
@ -121,7 +121,7 @@ func GetGeneral() *config.General {
|
|||||||
|
|
||||||
func updateExperimental(_ *config.Config) {}
|
func updateExperimental(_ *config.Config) {}
|
||||||
|
|
||||||
func updateDNS(c *config.DNS, t config.Tun) {
|
func updateDNS(c *config.DNS, t *config.Tun) {
|
||||||
cfg := dns.Config{
|
cfg := dns.Config{
|
||||||
Main: c.NameServer,
|
Main: c.NameServer,
|
||||||
Fallback: c.Fallback,
|
Fallback: c.Fallback,
|
||||||
@ -179,7 +179,7 @@ func updateDNS(c *config.DNS, t config.Tun) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Pool != nil {
|
if cfg.Pool != nil {
|
||||||
P.SetTunAddressPrefix(cfg.Pool.IPNet())
|
t.TunAddressPrefix = cfg.Pool.IPNet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,11 +31,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allowLan = false
|
allowLan = false
|
||||||
bindAddress = "*"
|
bindAddress = "*"
|
||||||
lastTunConf *config.Tun
|
lastTunConf *config.Tun
|
||||||
lastTunAddressPrefix *netip.Prefix
|
|
||||||
tunAddressPrefix *netip.Prefix
|
|
||||||
|
|
||||||
socksListener *socks.Listener
|
socksListener *socks.Listener
|
||||||
socksUDPListener *socks.UDPListener
|
socksUDPListener *socks.UDPListener
|
||||||
@ -109,10 +107,6 @@ func SetBindAddress(host string) {
|
|||||||
bindAddress = host
|
bindAddress = host
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetTunAddressPrefix(tunAddress *netip.Prefix) {
|
|
||||||
tunAddressPrefix = tunAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
||||||
httpMux.Lock()
|
httpMux.Lock()
|
||||||
defer httpMux.Unlock()
|
defer httpMux.Unlock()
|
||||||
@ -363,14 +357,10 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if tunAddressPrefix == nil {
|
|
||||||
tunAddressPrefix = lastTunAddressPrefix
|
|
||||||
}
|
|
||||||
|
|
||||||
tunConf.DNSHijack = C.RemoveDuplicateDNSUrl(tunConf.DNSHijack)
|
tunConf.DNSHijack = C.RemoveDuplicateDNSUrl(tunConf.DNSHijack)
|
||||||
|
|
||||||
if tunStackListener != nil {
|
if tunStackListener != nil {
|
||||||
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
|
if !hasTunConfigChange(tunConf) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +369,6 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastTunConf = tunConf
|
lastTunConf = tunConf
|
||||||
lastTunAddressPrefix = tunAddressPrefix
|
|
||||||
|
|
||||||
if !tunConf.Enable {
|
if !tunConf.Enable {
|
||||||
return
|
return
|
||||||
@ -391,7 +380,7 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
|
|||||||
udpIn: udpIn,
|
udpIn: udpIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn, callback)
|
tunStackListener, err = tun.New(tunConf, tcpIn, udpIn, callback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -535,7 +524,7 @@ func genAddr(host string, port int, allowLan bool) string {
|
|||||||
return fmt.Sprintf("127.0.0.1:%d", port)
|
return fmt.Sprintf("127.0.0.1:%d", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool {
|
func hasTunConfigChange(tunConf *config.Tun) bool {
|
||||||
if lastTunConf == nil {
|
if lastTunConf == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -558,11 +547,11 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) {
|
if (lastTunConf.TunAddressPrefix != nil && tunConf.TunAddressPrefix == nil) || (lastTunConf.TunAddressPrefix == nil && tunConf.TunAddressPrefix != nil) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
|
if lastTunConf.TunAddressPrefix != nil && tunConf.TunAddressPrefix != nil && *lastTunConf.TunAddressPrefix != *tunConf.TunAddressPrefix {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,11 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rAddr.Addr().Is4() {
|
||||||
|
// try to unmap 4in6 address
|
||||||
|
lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())
|
||||||
|
}
|
||||||
handlePacketConn(in, buf[:n], lAddr, rAddr)
|
handlePacketConn(in, buf[:n], lAddr, rAddr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -41,6 +41,10 @@ func (f *FD) Name() string {
|
|||||||
return strconv.Itoa(f.fd)
|
return strconv.Itoa(f.fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FD) MTU() uint32 {
|
||||||
|
return f.mtu
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FD) Close() error {
|
func (f *FD) Close() error {
|
||||||
err := unix.Close(f.fd)
|
err := unix.Close(f.fd)
|
||||||
if f.file != nil {
|
if f.file != nil {
|
||||||
|
@ -4,8 +4,48 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (device.Device, error) {
|
type FD struct {
|
||||||
|
stack.LinkEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(_ string, _ uint32) (device.Device, error) {
|
||||||
return nil, errors.New("not supported")
|
return nil, errors.New("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FD) Name() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) Type() string {
|
||||||
|
return Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) Read(_ []byte) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) Write(_ []byte) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) UseEndpoint() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) UseIOBased() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FD) MTU() uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ device.Device = (*FD)(nil)
|
||||||
|
@ -59,10 +59,13 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get mtu: %w", err)
|
return nil, fmt.Errorf("get mtu: %w", err)
|
||||||
}
|
}
|
||||||
t.mtu = uint32(tunMTU)
|
|
||||||
|
if t.mtu == 0 {
|
||||||
|
t.mtu = uint32(tunMTU)
|
||||||
|
}
|
||||||
|
|
||||||
if t.offset > 0 {
|
if t.offset > 0 {
|
||||||
t.cache = make([]byte, 65535)
|
t.cache = make([]byte, int(t.mtu)+t.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
@ -108,6 +111,10 @@ func (t *TUN) Name() string {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TUN) MTU() uint32 {
|
||||||
|
return t.mtu
|
||||||
|
}
|
||||||
|
|
||||||
func (t *TUN) UseEndpoint() error {
|
func (t *TUN) UseEndpoint() error {
|
||||||
ep, err := iobased.New(t, t.mtu, t.offset)
|
ep, err := iobased.New(t, t.mtu, t.offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,8 +5,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
dev "github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/fdbased"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/tun"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
"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) {
|
func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *UDP, error) {
|
||||||
@ -19,10 +21,27 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
t dev.Device
|
||||||
|
mtu uint32
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if t, ok = device.(*tun.TUN); ok {
|
||||||
|
mtu = t.MTU()
|
||||||
|
} else if t, ok = device.(*fdbased.FD); ok {
|
||||||
|
mtu = t.MTU()
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferSize := int(mtu)
|
||||||
|
|
||||||
|
if bufferSize == 0 {
|
||||||
|
bufferSize = 64 * 1024
|
||||||
|
}
|
||||||
|
|
||||||
tab := newTable()
|
tab := newTable()
|
||||||
udp := &UDP{
|
udp := &UDP{
|
||||||
device: device,
|
device: device,
|
||||||
buf: [0xffff]byte{},
|
buf: make([]byte, bufferSize),
|
||||||
}
|
}
|
||||||
tcp := &TCP{
|
tcp := &TCP{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
@ -38,7 +57,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
_ = udp.Close()
|
_ = udp.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := make([]byte, 0xffff)
|
buf := make([]byte, bufferSize)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, err := device.Read(buf)
|
n, err := device.Read(buf)
|
||||||
@ -133,11 +152,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
port, err = tab.newConn(tup)
|
port = tab.newConn(tup)
|
||||||
if err != nil {
|
|
||||||
log.Warnln("[STACK] drop tcp packet by system stack: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ip.SetSourceIP(portal)
|
ip.SetSourceIP(portal)
|
||||||
@ -156,7 +171,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go udp.handleUDPPacket(ip, u)
|
udp.handleUDPPacket(ip, u)
|
||||||
case tcpip.ICMP:
|
case tcpip.ICMP:
|
||||||
i := tcpip.ICMPPacket(ip.Payload())
|
i := tcpip.ICMPPacket(ip.Payload())
|
||||||
|
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package nat
|
package nat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/generics/list"
|
"github.com/Dreamacro/clash/common/generics/list"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -31,8 +27,6 @@ type table struct {
|
|||||||
tuples map[tuple]*list.Element[*binding]
|
tuples map[tuple]*list.Element[*binding]
|
||||||
ports [portLength]*list.Element[*binding]
|
ports [portLength]*list.Element[*binding]
|
||||||
available *list.List[*binding]
|
available *list.List[*binding]
|
||||||
mux sync.Mutex
|
|
||||||
count uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) tupleOf(port uint16) tuple {
|
func (t *table) tupleOf(port uint16) tuple {
|
||||||
@ -49,64 +43,27 @@ func (t *table) tupleOf(port uint16) tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) portOf(tuple tuple) uint16 {
|
func (t *table) portOf(tuple tuple) uint16 {
|
||||||
t.mux.Lock()
|
|
||||||
elm := t.tuples[tuple]
|
elm := t.tuples[tuple]
|
||||||
if elm == nil {
|
if elm == nil {
|
||||||
t.mux.Unlock()
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
t.mux.Unlock()
|
|
||||||
|
|
||||||
t.available.MoveToFront(elm)
|
t.available.MoveToFront(elm)
|
||||||
|
|
||||||
return portBegin + elm.Value.offset
|
return portBegin + elm.Value.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) newConn(tuple tuple) (uint16, error) {
|
func (t *table) newConn(tuple tuple) uint16 {
|
||||||
t.mux.Lock()
|
elm := t.available.Back()
|
||||||
elm, err := t.availableConn()
|
b := elm.Value
|
||||||
if err != nil {
|
|
||||||
t.mux.Unlock()
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
elm.Value.tuple = tuple
|
delete(t.tuples, b.tuple)
|
||||||
t.tuples[tuple] = elm
|
t.tuples[tuple] = elm
|
||||||
t.mux.Unlock()
|
b.tuple = tuple
|
||||||
|
|
||||||
return portBegin + elm.Value.offset, nil
|
t.available.MoveToFront(elm)
|
||||||
}
|
|
||||||
|
|
||||||
func (t *table) availableConn() (*list.Element[*binding], error) {
|
return portBegin + b.offset
|
||||||
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, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTable() *table {
|
func newTable() *table {
|
||||||
@ -114,7 +71,6 @@ func newTable() *table {
|
|||||||
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
||||||
ports: [portLength]*list.Element[*binding]{},
|
ports: [portLength]*list.Element[*binding]{},
|
||||||
available: list.New[*binding](),
|
available: list.New[*binding](),
|
||||||
count: 1,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx := range result.ports {
|
for idx := range result.ports {
|
||||||
|
@ -16,8 +16,6 @@ type conn struct {
|
|||||||
net.Conn
|
net.Conn
|
||||||
|
|
||||||
tuple tuple
|
tuple tuple
|
||||||
|
|
||||||
close func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TCP) Accept() (net.Conn, error) {
|
func (t *TCP) Accept() (net.Conn, error) {
|
||||||
@ -41,9 +39,6 @@ func (t *TCP) Accept() (net.Conn, error) {
|
|||||||
return &conn{
|
return &conn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
tuple: tup,
|
tuple: tup,
|
||||||
close: func() {
|
|
||||||
t.table.closeConn(tup)
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +55,6 @@ func (t *TCP) SetDeadline(time time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Close() error {
|
func (c *conn) Close() error {
|
||||||
c.close()
|
|
||||||
return c.Conn.Close()
|
return c.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ type UDP struct {
|
|||||||
queueLock sync.Mutex
|
queueLock sync.Mutex
|
||||||
queue []*call
|
queue []*call
|
||||||
bufLock sync.Mutex
|
bufLock sync.Mutex
|
||||||
buf [0xffff]byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UDP) ReadFrom(buf []byte) (int, netip.AddrPort, netip.AddrPort, error) {
|
func (u *UDP) ReadFrom(buf []byte) (int, netip.AddrPort, netip.AddrPort, error) {
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
"github.com/Dreamacro/clash/common/cmd"
|
"github.com/Dreamacro/clash/common/cmd"
|
||||||
"github.com/Dreamacro/clash/component/process"
|
|
||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
@ -24,7 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// New TunAdapter
|
// New TunAdapter
|
||||||
func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, tunChangeCallback C.TUNChangeCallback) (ipstack.Stack, error) {
|
func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, tunChangeCallback C.TUNChangeCallback) (ipstack.Stack, error) {
|
||||||
var (
|
var (
|
||||||
tunAddress = netip.Prefix{}
|
tunAddress = netip.Prefix{}
|
||||||
devName = tunConf.Device
|
devName = tunConf.Device
|
||||||
@ -48,16 +47,14 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
|
|||||||
devName = generateDeviceName()
|
devName = generateDeviceName()
|
||||||
}
|
}
|
||||||
|
|
||||||
if tunAddressPrefix != nil {
|
if tunConf.TunAddressPrefix != nil {
|
||||||
tunAddress = *tunAddressPrefix
|
tunAddress = *tunConf.TunAddressPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tunAddress.IsValid() || !tunAddress.Addr().Is4() {
|
if !tunAddress.IsValid() || !tunAddress.Addr().Is4() {
|
||||||
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
|
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
|
||||||
}
|
}
|
||||||
|
|
||||||
process.AppendLocalIPs(tunAddress.Masked().Addr().Next())
|
|
||||||
|
|
||||||
// open tun device
|
// open tun device
|
||||||
tunDevice, err = parseDevice(devName, uint32(mtu))
|
tunDevice, err = parseDevice(devName, uint32(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -16,7 +16,7 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
|
|||||||
|
|
||||||
// local resolve UDP dns
|
// local resolve UDP dns
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(metadata.Host)
|
ip, err := resolver.ResolveFirstIP(metadata.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +372,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
|||||||
configMux.RLock()
|
configMux.RLock()
|
||||||
defer configMux.RUnlock()
|
defer configMux.RUnlock()
|
||||||
|
|
||||||
var resolved bool
|
var (
|
||||||
|
resolved bool
|
||||||
|
processFound bool
|
||||||
|
)
|
||||||
|
|
||||||
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
||||||
metadata.DstIP = node.Data
|
metadata.DstIP = node.Data
|
||||||
@ -404,6 +394,22 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
|||||||
resolved = true
|
resolved = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !processFound && rule.ShouldFindProcess() {
|
||||||
|
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) {
|
if rule.Match(metadata) {
|
||||||
adapter, ok := proxies[rule.Adapter()]
|
adapter, ok := proxies[rule.Adapter()]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
Reference in New Issue
Block a user