Compare commits

...

16 Commits

58 changed files with 541 additions and 474 deletions

View File

@ -13,11 +13,11 @@
<img src="https://goreportcard.com/badge/github.com/Dreamacro/clash?style=flat-square">
</a>
<img src="https://img.shields.io/github/go-mod/go-version/Dreamacro/clash?style=flat-square">
<a href="https://github.com/Dreamacro/clash/releases">
<img src="https://img.shields.io/github/release/Dreamacro/clash/all.svg?style=flat-square">
<a href="https://github.com/yaling888/clash/releases">
<img src="https://img.shields.io/github/release/yaling888/clash/all.svg?style=flat-square">
</a>
<a href="https://github.com/Dreamacro/clash/releases/tag/premium">
<img src="https://img.shields.io/badge/release-Premium-00b4f0?style=flat-square">
<a href="https://github.com/yaling888/clash/releases/tag/plus_pro">
<img src="https://img.shields.io/badge/release-Plus Pro-00b4f0?style=flat-square">
</a>
</p>

View File

@ -23,7 +23,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
tcpKeepAlive(c)
if !metadata.DstIP.IsValid() && c.RemoteAddr() != nil {
if !metadata.Resolved() && c.RemoteAddr() != nil {
if h, _, err := net.SplitHostPort(c.RemoteAddr().String()); err == nil {
metadata.DstIP = netip.MustParseAddr(h)
}

View File

@ -24,6 +24,7 @@ func (m *Mitm) DialContext(_ context.Context, metadata *C.Metadata, _ ...dialer.
_ = c.SetKeepAlive(true)
_ = c.SetKeepAlivePeriod(60 * time.Second)
_ = c.SetLinger(0)
metadata.Type = C.MITM

View File

@ -15,6 +15,7 @@ func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
_ = tcp.SetLinger(0)
}
}

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

@ -91,17 +91,16 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
wsOpts := &vmess.WebsocketConfig{
Host: host,
Port: port,
Headers: http.Header{},
Path: v.option.WSOpts.Path,
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
}
if len(v.option.WSOpts.Headers) != 0 {
header := http.Header{}
for key, value := range v.option.WSOpts.Headers {
header.Add(key, value)
wsOpts.Headers.Add(key, value)
}
wsOpts.Headers = header
}
if v.option.TLS {
@ -117,7 +116,9 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
wsOpts.TLSConfig.ServerName = host
}
} else {
wsOpts.Headers.Set("Host", convert.RandHost())
if wsOpts.Headers.Get("Host") == "" {
wsOpts.Headers.Set("Host", convert.RandHost())
}
convert.SetUserAgent(wsOpts.Headers)
}
c, err = vmess.StreamWebsocketConn(c, wsOpts)
@ -138,9 +139,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if err != nil {
return nil, err
}
} else {
http.Header(v.option.HTTPOpts.Headers).Set("Host", convert.RandHost())
convert.SetUserAgent(v.option.HTTPOpts.Headers)
}
host, _, _ := net.SplitHostPort(v.addr)
@ -205,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)
}
@ -257,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

@ -30,10 +30,8 @@ type LoadBalance struct {
var errStrategy = errors.New("unsupported strategy")
func parseStrategy(config map[string]any) string {
if elm, ok := config["strategy"]; ok {
if strategy, ok := elm.(string); ok {
return strategy
}
if strategy, ok := config["strategy"].(string); ok {
return strategy
}
return "consistent-hashing"
}

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

@ -125,10 +125,8 @@ func parseURLTestOption(config map[string]any) []urlTestOption {
opts := []urlTestOption{}
// tolerance
if elm, ok := config["tolerance"]; ok {
if tolerance, ok := elm.(int); ok {
opts = append(opts, urlTestWithTolerance(uint16(tolerance)))
}
if tolerance, ok := config["tolerance"].(int); ok {
opts = append(opts, urlTestWithTolerance(uint16(tolerance)))
}
return opts

View File

@ -48,5 +48,6 @@ func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
_ = tcp.SetLinger(0)
}
}

View File

@ -57,8 +57,9 @@ func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
case "vmess":
vmessOption := &outbound.VmessOption{
HTTPOpts: outbound.HTTPOptions{
Method: "GET",
Path: []string{"/"},
Method: "GET",
Path: []string{"/"},
Headers: make(map[string][]string),
},
}
err = decoder.Decode(mapping, vmessOption)

View File

@ -5,6 +5,7 @@ import (
"crypto/md5"
"os"
"path/filepath"
"regexp"
"time"
types "github.com/Dreamacro/clash/constant/provider"
@ -14,6 +15,8 @@ import (
var (
fileMode os.FileMode = 0o666
dirMode os.FileMode = 0o755
commentRegx = regexp.MustCompile(`(.*#.*\n)`)
)
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)
}
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] {
var ticker *time.Ticker
if interval != 0 {

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
@ -319,12 +321,13 @@ func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVer
proxies := []C.Proxy{}
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
}
if prefixName != "" {
mapping["name"] = prefixName + mapping["name"].(string)
mapping["name"] = prefixName + name
}
proxy, err := adapter.ParseProxy(mapping, forceCertVerify)

View File

@ -11,7 +11,6 @@ import (
"github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider"
)
@ -82,13 +81,11 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
conn, err = dialer.DialContext(ctx, network, address) // with direct
if err != nil {
// fallback to tun if tun enabled
conn, err = (&net.Dialer{Timeout: C.DefaultTCPTimeout}).Dial(network, address)
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
if req.URL.Scheme == "https" {
return (&net.Dialer{}).DialContext(ctx, network, address) // forward to tun if tun enabled
}
return
return dialer.DialContext(ctx, network, address, dialer.WithDirect()) // with direct
},
}
@ -106,7 +103,7 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
return nil, err
}
return buf, nil
return removeComment(buf), nil
}
func NewHTTPVehicle(url string, path string, header http.Header) *HTTPVehicle {

View File

@ -2,6 +2,7 @@ package cert
import (
"crypto/tls"
"sync"
"github.com/Dreamacro/clash/component/trie"
)
@ -9,10 +10,14 @@ import (
// DomainTrieCertsStorage cache wildcard certificates
type DomainTrieCertsStorage struct {
certsCache *trie.DomainTrie[*tls.Certificate]
lock sync.RWMutex
}
// Get gets the certificate from the storage
func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
ca := c.certsCache.Search(key)
if ca == nil {
return nil, false
@ -22,7 +27,9 @@ func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
// Set saves the certificate to the storage
func (c *DomainTrieCertsStorage) Set(key string, cert *tls.Certificate) {
c.lock.Lock()
_ = c.certsCache.Insert(key, cert)
c.lock.Unlock()
}
func NewDomainTrieCertsStorage() *DomainTrieCertsStorage {

View File

@ -21,21 +21,24 @@ func DecodeBase64(buf []byte) ([]byte, error) {
return dBuf[:n], nil
}
// DecodeBase64StringToString decode base64 string to string
func DecodeBase64StringToString(s string) (string, error) {
dBuf, err := enc.DecodeString(s)
func DecodeRawBase64(buf []byte) ([]byte, error) {
dBuf := make([]byte, base64.RawStdEncoding.DecodedLen(len(buf)))
n, err := base64.RawStdEncoding.Decode(dBuf, buf)
if err != nil {
return "", err
return nil, err
}
return string(dBuf), nil
return dBuf[:n], nil
}
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
data, err := DecodeBase64(buf)
if err != nil {
data = buf
data, err = DecodeRawBase64(buf)
if err != nil {
data = buf
}
}
arr := strings.Split(string(data), "\n")

View File

@ -4,8 +4,6 @@ import (
"io"
"net"
"time"
"github.com/Dreamacro/clash/common/pool"
)
// Relay copies between left and right bidirectionally.
@ -16,18 +14,14 @@ func Relay(leftConn, rightConn net.Conn) {
tcpKeepAlive(rightConn)
go func() {
buf := pool.Get(pool.RelayBufferSize)
// Wrapping to avoid using *net.TCPConn.(ReadFrom)
// See also https://github.com/Dreamacro/clash/pull/1209
_, err := io.CopyBuffer(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}, buf)
_ = pool.Put(buf)
_, err := io.Copy(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn})
_ = leftConn.SetReadDeadline(time.Now())
ch <- err
}()
buf := pool.Get(pool.RelayBufferSize)
_, _ = io.CopyBuffer(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}, buf)
_ = pool.Put(buf)
_, _ = io.Copy(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn})
_ = rightConn.SetReadDeadline(time.Now())
<-ch
}

View File

@ -29,13 +29,13 @@ func bindMarkToControl(mark int, chain controlFn) controlFn {
return
}
return c.Control(func(fd uintptr) {
switch network {
case "tcp4", "udp4":
_ = 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)
}
var innerErr error
err = c.Control(func(fd uintptr) {
innerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
})
if err == nil && innerErr != nil {
err = innerErr
}
return
}
}

View File

@ -2,11 +2,7 @@ package process
import (
"errors"
"net"
"net/netip"
"github.com/Dreamacro/clash/common/nnip"
C "github.com/Dreamacro/clash/constant"
)
var (
@ -23,49 +19,3 @@ const (
func FindProcessName(network string, srcIP netip.Addr, srcPort int) (string, error) {
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()
}

View File

@ -6,8 +6,6 @@ import (
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"golang.org/x/sys/unix"
)
@ -63,10 +61,10 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
switch {
case flag&0x1 > 0 && isIPv4:
// ipv4
srcIP = nnip.IpToAddr(buf[inp+76 : inp+80])
srcIP, _ = netip.AddrFromSlice(buf[inp+76 : inp+80])
case flag&0x2 > 0 && !isIPv4:
// ipv6
srcIP = nnip.IpToAddr(buf[inp+64 : inp+80])
srcIP, _ = netip.AddrFromSlice(buf[inp+64 : inp+80])
default:
continue
}

View File

@ -10,7 +10,6 @@ import (
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/log"
)
@ -135,10 +134,10 @@ func (s *searcher) Search(buf []byte, ip netip.Addr, port uint16, isTCP bool) (u
switch {
case flag&0x1 > 0 && isIPv4:
// 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:
// 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:
continue
}

View File

@ -7,7 +7,6 @@ import (
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
@ -132,7 +131,7 @@ func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error)
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
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
continue

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

@ -99,12 +99,13 @@ type Profile struct {
// Tun config
type Tun struct {
Enable bool `yaml:"enable" json:"enable"`
Device string `yaml:"device" json:"device"`
Stack C.TUNStack `yaml:"stack" json:"stack"`
DNSHijack []C.DNSUrl `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
Enable bool `yaml:"enable" json:"enable"`
Device string `yaml:"device" json:"device"`
Stack C.TUNStack `yaml:"stack" json:"stack"`
DNSHijack []C.DNSUrl `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
TunAddressPrefix *netip.Prefix `yaml:"_" json:"_"`
}
// IPTables config

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
}

16
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/exp v0.0.0-20220602145555-4a0574d9293f
golang.org/x/net v0.0.0-20220531201128-c960675eff93
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-20220630215102-69896b714898
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
golang.org/x/time v0.0.0-20220411224347-583f2d630306
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-20220601233344-46e478629075
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3
)
require (

34
go.sum
View File

@ -20,7 +20,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -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,10 +78,10 @@ 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/exp v0.0.0-20220602145555-4a0574d9293f h1:KK6mxegmt5hGJRcAnEDjSNLxIRhZxDcgwMbcO/lMCRM=
golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
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=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
@ -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-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
@ -123,16 +123,16 @@ 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-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
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/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -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-20220601233344-46e478629075 h1:ucwwit0X39HmMQ1iSeNCIXw4g/B8bi+O6TSxxDbPs9E=
gvisor.dev/gvisor v0.0.0-20220601233344-46e478629075/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3 h1:rnajnEGeshXCEr3k/7pWAM6zXh1Ws4jkZgWuWXjCJSw=
gvisor.dev/gvisor v0.0.0-20220630235058-ce952880a3d3/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=

View File

@ -82,7 +82,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateHosts(cfg.Hosts)
updateMitm(cfg.Mitm)
updateProfile(cfg)
updateDNS(cfg.DNS, cfg.General.Tun)
updateDNS(cfg.DNS, &cfg.General.Tun)
updateGeneral(cfg.General, force)
updateIPTables(cfg)
updateExperimental(cfg)
@ -121,7 +121,7 @@ func GetGeneral() *config.General {
func updateExperimental(_ *config.Config) {}
func updateDNS(c *config.DNS, t config.Tun) {
func updateDNS(c *config.DNS, t *config.Tun) {
cfg := dns.Config{
Main: c.NameServer,
Fallback: c.Fallback,
@ -141,6 +141,11 @@ func updateDNS(c *config.DNS, t config.Tun) {
ProxyServer: c.ProxyServerNameserver,
}
// deprecated warnning
if cfg.EnhancedMode == C.DNSMapping {
log.Warnln("[DNS] %s is deprecated, please use %s instead", cfg.EnhancedMode.String(), C.DNSFakeIP.String())
}
r := dns.NewResolver(cfg)
pr := dns.NewProxyServerHostResolver(r)
m := dns.NewEnhancer(cfg)
@ -174,7 +179,7 @@ func updateDNS(c *config.DNS, t config.Tun) {
}
if cfg.Pool != nil {
P.SetTunAddressPrefix(cfg.Pool.IPNet())
t.TunAddressPrefix = cfg.Pool.IPNet()
}
}

View File

@ -31,11 +31,9 @@ import (
)
var (
allowLan = false
bindAddress = "*"
lastTunConf *config.Tun
lastTunAddressPrefix *netip.Prefix
tunAddressPrefix *netip.Prefix
allowLan = false
bindAddress = "*"
lastTunConf *config.Tun
socksListener *socks.Listener
socksUDPListener *socks.UDPListener
@ -109,10 +107,6 @@ func SetBindAddress(host string) {
bindAddress = host
}
func SetTunAddressPrefix(tunAddress *netip.Prefix) {
tunAddressPrefix = tunAddress
}
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
httpMux.Lock()
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)
if tunStackListener != nil {
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
if !hasTunConfigChange(tunConf) {
return
}
@ -379,7 +369,6 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
}
lastTunConf = tunConf
lastTunAddressPrefix = tunAddressPrefix
if !tunConf.Enable {
return
@ -391,7 +380,7 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
udpIn: udpIn,
}
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn, callback)
tunStackListener, err = tun.New(tunConf, tcpIn, udpIn, callback)
if err != nil {
return
}
@ -535,7 +524,7 @@ func genAddr(host string, port int, allowLan bool) string {
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 {
return true
}
@ -558,11 +547,11 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
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
}
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
if lastTunConf.TunAddressPrefix != nil && tunConf.TunAddressPrefix != nil && *lastTunConf.TunAddressPrefix != *tunConf.TunAddressPrefix {
return true
}

View File

@ -2,12 +2,13 @@ package tproxy
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/common/pool"
)
type packet struct {
lAddr *net.UDPAddr
lAddr netip.AddrPort
buf []byte
}
@ -17,21 +18,21 @@ func (c *packet) Data() []byte {
// WriteBack opens a new socket binding `addr` to write UDP packet back
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
tc, err := dialUDP("udp", addr.(*net.UDPAddr), c.lAddr)
tc, err := dialUDP("udp", addr.(*net.UDPAddr).AddrPort(), c.lAddr)
if err != nil {
n = 0
return
}
n, err = tc.Write(b)
tc.Close()
_ = tc.Close()
return
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.lAddr
return net.UDPAddrFromAddrPort(c.lAddr)
}
func (c *packet) Drop() {
pool.Put(c.buf)
_ = pool.Put(c.buf)
}

View File

@ -32,7 +32,7 @@ func (l *Listener) Close() error {
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) {
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
conn.(*net.TCPConn).SetKeepAlive(true)
_ = conn.(*net.TCPConn).SetKeepAlive(true)
in <- inbound.NewSocket(target, conn, C.TPROXY)
}

View File

@ -2,6 +2,7 @@ package tproxy
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
@ -58,28 +59,33 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
oob := make([]byte, 1024)
for {
buf := pool.Get(pool.UDPBufferSize)
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob)
n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)
if err != nil {
pool.Put(buf)
_ = pool.Put(buf)
if rl.closed {
break
}
continue
}
rAddr, err := getOrigDst(oob, oobn)
rAddr, err := getOrigDst(oob[:oobn])
if err != nil {
continue
}
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
if rAddr.Addr().Is4() {
// try to unmap 4in6 address
lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())
}
handlePacketConn(in, buf[:n], lAddr, rAddr)
}
}()
return rl, nil
}
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
target := socks5.ParseAddrToSocksAddr(rAddr)
func handlePacketConn(in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) {
target := socks5.AddrFromStdAddrPort(rAddr)
pkt := &packet{
lAddr: lAddr,
buf: buf,

View File

@ -3,13 +3,14 @@
package tproxy
import (
"encoding/binary"
"errors"
"fmt"
"net"
"net/netip"
"os"
"strconv"
"syscall"
"golang.org/x/sys/unix"
)
const (
@ -19,7 +20,7 @@ const (
// dialUDP acts like net.DialUDP for transparent proxy.
// It binds to a non-local address(`lAddr`).
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
@ -35,23 +36,25 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
return nil, err
}
defer func() {
if err != nil {
syscall.Close(fd)
}
}()
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Bind(fd, lSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Connect(fd, rSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
@ -60,35 +63,26 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
c, err := net.FileConn(fdFile)
if err != nil {
syscall.Close(fd)
return nil, err
}
return c.(*net.UDPConn), nil
}
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
func udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {
if addr.Addr().Is4() {
return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil
}
zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil
}
func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
switch net[len(net)-1] {
case '4':
return syscall.AF_INET
@ -96,29 +90,35 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
return syscall.AF_INET6
}
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
if lAddr.Addr().Is4() && rAddr.Addr().Is4() {
return syscall.AF_INET
}
return syscall.AF_INET6
}
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
func getOrigDst(oob []byte) (netip.AddrPort, error) {
// oob contains socket control messages which we need to parse.
scms, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return nil, err
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
}
for _, msg := range msgs {
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
ip := net.IP(msg.Data[4:8])
port := binary.BigEndian.Uint16(msg.Data[2:4])
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
ip := net.IP(msg.Data[8:24])
port := binary.BigEndian.Uint16(msg.Data[2:4])
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
}
// retrieve the destination address from the SCM.
sa, err := unix.ParseOrigDstAddr(&scms[0])
if err != nil {
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
}
return nil, errors.New("cannot find origDst")
// encode the destination address into a cmsg.
var rAddr netip.AddrPort
switch v := sa.(type) {
case *unix.SockaddrInet4:
rAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port))
case *unix.SockaddrInet6:
rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))
default:
return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v)
}
return rAddr, nil
}

View File

@ -5,12 +5,13 @@ package tproxy
import (
"errors"
"net"
"net/netip"
)
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
return nil, errors.New("UDP redir not supported on current platform")
func getOrigDst(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
}
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
return nil, errors.New("UDP redir not supported on current platform")
}

View File

@ -41,6 +41,10 @@ func (f *FD) Name() string {
return strconv.Itoa(f.fd)
}
func (f *FD) MTU() uint32 {
return f.mtu
}
func (f *FD) Close() error {
err := unix.Close(f.fd)
if f.file != nil {

View File

@ -4,8 +4,48 @@ import (
"errors"
"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")
}
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)

View File

@ -8,8 +8,10 @@ import (
"io"
"sync"
"github.com/Dreamacro/clash/common/pool"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@ -93,23 +95,29 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
mtu := int(e.mtu)
for {
data := make([]byte, mtu)
data := pool.Get(mtu)
n, err := e.rw.Read(data)
if err != nil {
_ = pool.Put(data)
break
}
if n == 0 || n > mtu {
_ = pool.Put(data)
continue
}
if !e.IsAttached() {
_ = pool.Put(data)
continue /* unattached, drop packet */
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(data[:n]).ToVectorisedView(),
Payload: buffer.NewWithData(data[:n]),
OnRelease: func() {
_ = pool.Put(data)
},
})
switch header.IPVersion(data) {
@ -117,6 +125,8 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
case header.IPv6Version:
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
default:
_ = pool.Put(data)
}
pkt.DecRef()
}
@ -138,11 +148,8 @@ func (e *Endpoint) outboundLoop(ctx context.Context) {
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
defer pkt.DecRef()
size := pkt.Size()
views := pkt.Views()
vView := buffer.NewVectorisedView(size, views)
if _, err := e.rw.Write(vView.ToView()); err != nil {
buf := pkt.Buffer()
if _, err := e.rw.Write(buf.Flatten()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil

View File

@ -59,10 +59,13 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = uint32(tunMTU)
if t.mtu == 0 {
t.mtu = uint32(tunMTU)
}
if t.offset > 0 {
t.cache = make([]byte, 65535)
t.cache = make([]byte, int(t.mtu)+t.offset)
}
return t, nil
@ -87,7 +90,11 @@ func (t *TUN) Write(packet []byte) (int, error) {
packet = append(t.cache[:t.offset], packet...)
return t.nt.Write(packet, t.offset)
n, err := t.nt.Write(packet, t.offset)
if n < t.offset {
return 0, err
}
return n - t.offset, err
}
func (t *TUN) Close() error {
@ -104,6 +111,10 @@ func (t *TUN) Name() string {
return name
}
func (t *TUN) MTU() uint32 {
return t.mtu
}
func (t *TUN) UseEndpoint() error {
ep, err := iobased.New(t, t.mtu, t.offset)
if err != nil {

View File

@ -2,23 +2,15 @@ package adapter
import (
"net"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// TCPConn implements the net.Conn interface.
type TCPConn interface {
net.Conn
// ID returns the transport endpoint id of TCPConn.
ID() *stack.TransportEndpointID
}
// UDPConn implements net.Conn and net.PacketConn.
type UDPConn interface {
net.Conn
net.PacketConn
// ID returns the transport endpoint id of UDPConn.
ID() *stack.TransportEndpointID
}

View File

@ -7,7 +7,6 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
@ -27,15 +26,7 @@ type gvHandler struct {
}
func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
id := tunConn.ID()
rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
rAddrPort := tunConn.LocalAddr().(*net.TCPAddr).AddrPort()
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "tcp") {
go func() {
@ -43,8 +34,8 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
_ = tunConn.Close()
_ = pool.Put(buf)
}()
for {
@ -78,26 +69,18 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
return
}
gh.tcpIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
gh.tcpIn <- inbound.NewSocket(socks5.AddrFromStdAddrPort(rAddrPort), tunConn, C.TUN)
}
func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
id := tunConn.ID()
rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
rAddrPort := tunConn.LocalAddr().(*net.UDPAddr).AddrPort()
if rAddrPort.Addr() == gh.gateway {
_ = tunConn.Close()
return
}
target := socks5.ParseAddrToSocksAddr(rAddr)
target := socks5.AddrFromStdAddrPort(rAddrPort)
go func() {
for {
@ -109,22 +92,20 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
break
}
payload := buf[:n]
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "udp") {
go func() {
defer func() {
_ = pool.Put(buf)
}()
msg, err1 := D.RelayDnsPacket(payload)
msg, err1 := D.RelayDnsPacket(buf[:n])
if err1 != nil {
return
}
_, _ = tunConn.WriteTo(msg, addr)
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
}()
continue
@ -133,12 +114,14 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
gvPacket := &packet{
pc: tunConn,
rAddr: addr,
payload: payload,
payload: buf,
offset: n,
}
select {
case gh.udpIn <- inbound.NewPacket(target, gvPacket, C.TUN):
default:
gvPacket.Drop()
}
}
}()

View File

@ -70,7 +70,6 @@ func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
conn := &tcpConn{
TCPConn: gonet.NewTCPConn(&wq, ep),
id: id,
}
handle(conn)
})
@ -113,9 +112,4 @@ func setSocketOptions(s *stack.Stack, ep tcpip.Endpoint) tcpip.Error {
type tcpConn struct {
*gonet.TCPConn
id stack.TransportEndpointID
}
func (c *tcpConn) ID() *stack.TransportEndpointID {
return &c.id
}

View File

@ -29,7 +29,6 @@ func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
conn := &udpConn{
UDPConn: gonet.NewUDPConn(s, &wq, ep),
id: id,
}
handle(conn)
})
@ -40,21 +39,17 @@ func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
type udpConn struct {
*gonet.UDPConn
id stack.TransportEndpointID
}
func (c *udpConn) ID() *stack.TransportEndpointID {
return &c.id
}
type packet struct {
pc adapter.UDPConn
rAddr net.Addr
payload []byte
offset int
}
func (c *packet) Data() []byte {
return c.payload
return c.payload[:c.offset]
}
// WriteBack write UDP packet with source(ip, port) = `addr`

View File

@ -5,7 +5,9 @@ import (
"net"
"net/netip"
"github.com/Dreamacro/clash/common/pool"
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"
)
@ -19,10 +21,27 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
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()
udp := &UDP{
device: device,
buf: [pool.UDPBufferSize]byte{},
buf: make([]byte, bufferSize),
}
tcp := &TCP{
listener: listener,
@ -38,7 +57,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
_ = udp.Close()
}()
buf := make([]byte, pool.RelayBufferSize)
buf := make([]byte, bufferSize)
for {
n, err := device.Read(buf)

View File

@ -24,9 +24,9 @@ func (t *TCP) Accept() (net.Conn, error) {
return nil, err
}
addr := c.RemoteAddr().(*net.TCPAddr)
tup := t.table.tupleOf(uint16(addr.Port))
if !addr.IP.Equal(t.portal.AsSlice()) || tup == zeroTuple {
addr := c.RemoteAddr().(*net.TCPAddr).AddrPort()
tup := t.table.tupleOf(addr.Port())
if addr.Addr() != t.portal || tup == zeroTuple {
_ = c.Close()
return nil, net.InvalidAddrError("unknown remote addr")
@ -34,6 +34,8 @@ func (t *TCP) Accept() (net.Conn, error) {
addition(c)
_ = c.SetLinger(0)
return &conn{
Conn: c,
tuple: tup,
@ -52,16 +54,14 @@ func (t *TCP) SetDeadline(time time.Time) error {
return t.listener.SetDeadline(time)
}
func (c *conn) Close() error {
return c.Conn.Close()
}
func (c *conn) LocalAddr() net.Addr {
return &net.TCPAddr{
IP: c.tuple.SourceAddr.Addr().AsSlice(),
Port: int(c.tuple.SourceAddr.Port()),
}
return net.TCPAddrFromAddrPort(c.tuple.SourceAddr)
}
func (c *conn) RemoteAddr() net.Addr {
return &net.TCPAddr{
IP: c.tuple.DestinationAddr.Addr().AsSlice(),
Port: int(c.tuple.DestinationAddr.Port()),
}
return net.TCPAddrFromAddrPort(c.tuple.DestinationAddr)
}

View File

@ -7,8 +7,6 @@ import (
"net/netip"
"sync"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
)
@ -16,8 +14,8 @@ type call struct {
cond *sync.Cond
buf []byte
n int
source net.Addr
destination net.Addr
source netip.AddrPort
destination netip.AddrPort
}
type UDP struct {
@ -26,10 +24,10 @@ type UDP struct {
queueLock sync.Mutex
queue []*call
bufLock sync.Mutex
buf [pool.UDPBufferSize]byte
buf []byte
}
func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
func (u *UDP) ReadFrom(buf []byte) (int, netip.AddrPort, netip.AddrPort, error) {
u.queueLock.Lock()
defer u.queueLock.Unlock()
@ -38,8 +36,8 @@ func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
cond: sync.NewCond(&u.queueLock),
buf: buf,
n: -1,
source: nil,
destination: nil,
source: netip.AddrPort{},
destination: netip.AddrPort{},
}
u.queue = append(u.queue, c)
@ -51,10 +49,10 @@ func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
}
}
return -1, nil, nil, net.ErrClosed
return -1, netip.AddrPort{}, netip.AddrPort{}, net.ErrClosed
}
func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error) {
func (u *UDP) WriteTo(buf []byte, local netip.AddrPort, remote netip.AddrPort) (int, error) {
if u.closed {
return 0, net.ErrClosed
}
@ -66,16 +64,7 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
return 0, net.InvalidAddrError("invalid ip version")
}
srcAddr, srcOk := local.(*net.UDPAddr)
dstAddr, dstOk := remote.(*net.UDPAddr)
if !srcOk || !dstOk {
return 0, net.InvalidAddrError("invalid addr")
}
srcAddrPort := netip.AddrPortFrom(nnip.IpToAddr(srcAddr.IP), uint16(srcAddr.Port))
dstAddrPort := netip.AddrPortFrom(nnip.IpToAddr(dstAddr.IP), uint16(dstAddr.Port))
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
if !local.Addr().Is4() || !remote.Addr().Is4() {
return 0, net.InvalidAddrError("invalid ip version")
}
@ -89,13 +78,13 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
ip.SetFragmentOffset(0)
ip.SetTimeToLive(64)
ip.SetProtocol(tcpip.UDP)
ip.SetSourceIP(srcAddrPort.Addr())
ip.SetDestinationIP(dstAddrPort.Addr())
ip.SetSourceIP(local.Addr())
ip.SetDestinationIP(remote.Addr())
udp := tcpip.UDPPacket(ip.Payload())
udp.SetLength(tcpip.UDPHeaderSize + uint16(len(buf)))
udp.SetSourcePort(srcAddrPort.Port())
udp.SetDestinationPort(dstAddrPort.Port())
udp.SetSourcePort(local.Port())
udp.SetDestinationPort(remote.Port())
copy(udp.Payload(), buf)
ip.ResetChecksum()
@ -131,14 +120,8 @@ func (u *UDP) handleUDPPacket(ip tcpip.IP, pkt tcpip.UDPPacket) {
u.queueLock.Unlock()
if c != nil {
c.source = &net.UDPAddr{
IP: ip.SourceIP().AsSlice(),
Port: int(pkt.SourcePort()),
}
c.destination = &net.UDPAddr{
IP: ip.DestinationIP().AsSlice(),
Port: int(pkt.DestinationPort()),
}
c.source = netip.AddrPortFrom(ip.SourceIP(), pkt.SourcePort())
c.destination = netip.AddrPortFrom(ip.DestinationIP(), pkt.DestinationPort())
c.n = copy(c.buf, pkt.Payload())
c.cond.Signal()
}

View File

@ -81,26 +81,23 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
continue
}
lAddr := conn.LocalAddr().(*net.TCPAddr)
rAddr := conn.RemoteAddr().(*net.TCPAddr)
lAddr := conn.LocalAddr().(*net.TCPAddr).AddrPort()
rAddr := conn.RemoteAddr().(*net.TCPAddr).AddrPort()
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() {
if rAddr.Addr().IsLoopback() {
_ = conn.Close()
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort, "tcp") {
if D.ShouldHijackDns(dnsAddr, rAddr, "tcp") {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
log.Debugln("[TUN] hijack dns tcp: %s", rAddr.String())
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
_ = conn.Close()
_ = pool.Put(buf)
}()
for {
@ -137,10 +134,10 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.TUN,
SrcIP: lAddrPort.Addr(),
DstIP: rAddrPort.Addr(),
SrcPort: strconv.Itoa(lAddr.Port),
DstPort: strconv.Itoa(rAddr.Port),
SrcIP: lAddr.Addr(),
DstIP: rAddr.Addr(),
SrcPort: strconv.FormatUint(uint64(lAddr.Port()), 10),
DstPort: strconv.FormatUint(uint64(rAddr.Port()), 10),
AddrType: C.AtypIPv4,
Host: "",
}
@ -159,56 +156,50 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
for !ipStack.closed {
buf := pool.Get(pool.UDPBufferSize)
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
n, lAddr, rAddr, err := stack.UDP().ReadFrom(buf)
if err != nil {
_ = pool.Put(buf)
break
}
raw := buf[:n]
lAddr := lRAddr.(*net.UDPAddr)
rAddr := rRAddr.(*net.UDPAddr)
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway {
if rAddr.Addr().IsLoopback() || rAddr.Addr() == gateway {
_ = pool.Put(buf)
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort, "udp") {
if D.ShouldHijackDns(dnsAddr, rAddr, "udp") {
go func() {
msg, err := D.RelayDnsPacket(raw)
if err != nil {
defer func() {
_ = pool.Put(buf)
}()
msg, err := D.RelayDnsPacket(buf[:n])
if err != nil {
return
}
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
_ = pool.Put(buf)
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
}()
continue
}
pkt := &packet{
local: lAddr,
data: raw,
local: lAddr,
data: buf,
offset: n,
writeBack: func(b []byte, addr net.Addr) (int, error) {
return stack.UDP().WriteTo(b, rAddr, lAddr)
},
drop: func() {
_ = pool.Put(buf)
},
}
select {
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN):
case udpIn <- inbound.NewPacket(socks5.AddrFromStdAddrPort(rAddr), pkt, C.TUN):
default:
pkt.Drop()
}
}

View File

@ -1,16 +1,21 @@
package system
import "net"
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/common/pool"
)
type packet struct {
local *net.UDPAddr
local netip.AddrPort
data []byte
offset int
writeBack func(b []byte, addr net.Addr) (int, error)
drop func()
}
func (pkt *packet) Data() []byte {
return pkt.data
return pkt.data[:pkt.offset]
}
func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
@ -18,9 +23,9 @@ func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
}
func (pkt *packet) Drop() {
pkt.drop()
_ = pool.Put(pkt.data)
}
func (pkt *packet) LocalAddr() net.Addr {
return pkt.local
return net.UDPAddrFromAddrPort(pkt.local)
}

View File

@ -9,7 +9,6 @@ import (
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/component/process"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/device"
@ -24,7 +23,7 @@ import (
)
// 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 (
tunAddress = netip.Prefix{}
devName = tunConf.Device
@ -48,16 +47,14 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
devName = generateDeviceName()
}
if tunAddressPrefix != nil {
tunAddress = *tunAddressPrefix
if tunConf.TunAddressPrefix != nil {
tunAddress = *tunConf.TunAddressPrefix
}
if !tunAddress.IsValid() || !tunAddress.Addr().Is4() {
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
}
process.AppendLocalIPs(tunAddress.Masked().Addr().Next())
// open tun device
tunDevice, err = parseDevice(devName, uint32(mtu))
if err != nil {

View File

@ -51,11 +51,11 @@ func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
}
cont := fmt.Sprintf("%d", recordsCount)
count := fmt.Sprintf("%d", recordsCount)
if recordsCount == 0 {
cont = "from cache"
count = "from cache"
}
log.Infoln("Start initial GeoSite rule %s => %s, records: %s", country, adapter, cont)
log.Infoln("Start initial GeoSite rule %s => %s, records: %s", country, adapter, count)
geoSite := &GEOSITE{
Base: &Base{},

View File

@ -6,6 +6,7 @@ import (
"errors"
"io"
"net"
"net/netip"
"strconv"
"github.com/Dreamacro/clash/component/auth"
@ -398,6 +399,21 @@ func ParseAddrToSocksAddr(addr net.Addr) Addr {
return parsed
}
func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {
addr := addrPort.Addr()
if addr.Is4() {
ip4 := addr.As4()
return []byte{AtypIPv4, ip4[0], ip4[1], ip4[2], ip4[3], byte(addrPort.Port() >> 8), byte(addrPort.Port())}
}
buf := make([]byte, 1+net.IPv6len+2)
buf[0] = AtypIPv6
copy(buf[1:], addr.AsSlice())
buf[1+net.IPv6len] = byte(addrPort.Port() >> 8)
buf[1+net.IPv6len+1] = byte(addrPort.Port())
return buf
}
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
if len(packet) < 5 {

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
}
@ -32,19 +32,21 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
return err
}
// reset timeout
pc.SetReadDeadline(time.Now().Add(udpTimeout))
_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))
return nil
}
func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) {
buf := pool.Get(pool.UDPBufferSize)
defer pool.Put(buf)
defer natTable.Delete(key)
defer pc.Close()
defer func() {
_ = pc.Close()
natTable.Delete(key)
_ = pool.Put(buf)
}()
for {
pc.SetReadDeadline(time.Now().Add(udpTimeout))
_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))
n, from, err := pc.ReadFrom(buf)
if err != nil {
return

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() {
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 {