Chore: merge branch 'with-tun' into plus-pro
This commit is contained in:
commit
053366c3e1
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@ -49,6 +49,8 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: 'dreamacro/clash:dev,ghcr.io/dreamacro/clash:dev'
|
tags: 'dreamacro/clash:dev,ghcr.io/dreamacro/clash:dev'
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
- name: Get all docker tags
|
- name: Get all docker tags
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
@ -74,3 +76,5 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{steps.tags.outputs.result}}
|
tags: ${{steps.tags.outputs.result}}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -20,7 +20,9 @@ jobs:
|
|||||||
- name: Cache go module
|
- name: Cache go module
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: |
|
||||||
|
~/go/pkg/mod
|
||||||
|
~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-go-
|
${{ runner.os }}-go-
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
@ -62,9 +63,9 @@ func (p *Proxy) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
|
|
||||||
// DelayHistory implements C.Proxy
|
// DelayHistory implements C.Proxy
|
||||||
func (p *Proxy) DelayHistory() []C.DelayHistory {
|
func (p *Proxy) DelayHistory() []C.DelayHistory {
|
||||||
queue := p.history.Copy()
|
queueM := p.history.Copy()
|
||||||
histories := []C.DelayHistory{}
|
histories := []C.DelayHistory{}
|
||||||
for _, item := range queue {
|
for _, item := range queueM {
|
||||||
histories = append(histories, item)
|
histories = append(histories, item)
|
||||||
}
|
}
|
||||||
return histories
|
return histories
|
||||||
@ -93,7 +94,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mapping := map[string]any{}
|
mapping := map[string]any{}
|
||||||
json.Unmarshal(inner, &mapping)
|
_ = json.Unmarshal(inner, &mapping)
|
||||||
mapping["history"] = p.DelayHistory()
|
mapping["history"] = p.DelayHistory()
|
||||||
mapping["name"] = p.Name()
|
mapping["name"] = p.Name()
|
||||||
mapping["udp"] = p.SupportUDP()
|
mapping["udp"] = p.SupportUDP()
|
||||||
@ -125,7 +126,9 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer instance.Close()
|
defer func() {
|
||||||
|
_ = instance.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodHead, url, nil)
|
req, err := http.NewRequest(http.MethodHead, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -134,7 +137,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
|
|||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
Dial: func(string, string) (net.Conn, error) {
|
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
||||||
return instance, nil
|
return instance, nil
|
||||||
},
|
},
|
||||||
// from http.DefaultTransport
|
// from http.DefaultTransport
|
||||||
@ -156,7 +159,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
t = uint16(time.Since(start) / time.Millisecond)
|
t = uint16(time.Since(start) / time.Millisecond)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -187,7 +190,7 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
|||||||
addr = C.Metadata{
|
addr = C.Metadata{
|
||||||
AddrType: C.AtypDomainName,
|
AddrType: C.AtypDomainName,
|
||||||
Host: u.Hostname(),
|
Host: u.Hostname(),
|
||||||
DstIP: nil,
|
DstIP: netip.Addr{},
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -3,9 +3,11 @@ package inbound
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
)
|
)
|
||||||
@ -21,12 +23,10 @@ func parseSocksAddr(target socks5.Addr) *C.Metadata {
|
|||||||
metadata.Host = strings.TrimRight(string(target[2:2+target[1]]), ".")
|
metadata.Host = strings.TrimRight(string(target[2:2+target[1]]), ".")
|
||||||
metadata.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))
|
metadata.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))
|
||||||
case socks5.AtypIPv4:
|
case socks5.AtypIPv4:
|
||||||
ip := net.IP(target[1 : 1+net.IPv4len])
|
metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv4len]))
|
||||||
metadata.DstIP = ip
|
|
||||||
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
|
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
|
||||||
case socks5.AtypIPv6:
|
case socks5.AtypIPv6:
|
||||||
ip := net.IP(target[1 : 1+net.IPv6len])
|
metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv6len]))
|
||||||
metadata.DstIP = ip
|
|
||||||
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
|
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,14 +47,14 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
|
|||||||
NetWork: C.TCP,
|
NetWork: C.TCP,
|
||||||
AddrType: C.AtypDomainName,
|
AddrType: C.AtypDomainName,
|
||||||
Host: host,
|
Host: host,
|
||||||
DstIP: nil,
|
DstIP: netip.Addr{},
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if ip != nil {
|
if err == nil {
|
||||||
switch {
|
switch {
|
||||||
case ip.To4() == nil:
|
case ip.Is6():
|
||||||
metadata.AddrType = C.AtypIPv6
|
metadata.AddrType = C.AtypIPv6
|
||||||
default:
|
default:
|
||||||
metadata.AddrType = C.AtypIPv4
|
metadata.AddrType = C.AtypIPv4
|
||||||
@ -65,12 +65,12 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
|
|||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAddr(addr string) (net.IP, string, error) {
|
func parseAddr(addr string) (netip.Addr, string, error) {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return netip.Addr{}, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
return ip, port, nil
|
return ip, port, err
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if metadata.Type == C.MITM {
|
if metadata.Type == C.MITM {
|
||||||
req.Header.Add("Origin-Request-Source-Address", metadata.SourceAddress())
|
req.Header.Set("Origin-Request-Source-Address", metadata.SourceAddress())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := req.Write(rw); err != nil {
|
if err := req.Write(rw); err != nil {
|
||||||
|
@ -7,26 +7,23 @@ import (
|
|||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errIgnored = errors.New("not match in mitm host lists")
|
errIgnored = errors.New("not match in mitm host lists")
|
||||||
httpProxyClient = NewHttp(HttpOption{})
|
httpProxyClient = NewHttp(HttpOption{})
|
||||||
|
|
||||||
MiddlemanServerAddress = atomic.NewString("")
|
MiddlemanRewriteHosts *trie.DomainTrie[bool]
|
||||||
MiddlemanRewriteHosts *trie.DomainTrie[bool]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mitm struct {
|
type Mitm struct {
|
||||||
*Base
|
*Base
|
||||||
|
serverAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
|
func (m *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
|
||||||
addr := MiddlemanServerAddress.Load()
|
if MiddlemanRewriteHosts == nil {
|
||||||
if addr == "" || MiddlemanRewriteHosts == nil {
|
|
||||||
return nil, errIgnored
|
return nil, errIgnored
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,12 +33,7 @@ func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...diale
|
|||||||
|
|
||||||
metadata.Type = C.MITM
|
metadata.Type = C.MITM
|
||||||
|
|
||||||
if metadata.Host != "" {
|
c, err := dialer.DialContext(ctx, "tcp", m.serverAddr, []dialer.Option{dialer.WithInterface(""), dialer.WithRoutingMark(0)}...)
|
||||||
metadata.AddrType = C.AtypDomainName
|
|
||||||
metadata.DstIP = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", addr, []dialer.Option{dialer.WithInterface(""), dialer.WithRoutingMark(0)}...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -55,14 +47,15 @@ func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...diale
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewConn(c, d), nil
|
return NewConn(c, m), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMitm() *Mitm {
|
func NewMitm(serverAddr string) *Mitm {
|
||||||
return &Mitm{
|
return &Mitm{
|
||||||
Base: &Base{
|
Base: &Base{
|
||||||
name: "Mitm",
|
name: "Mitm",
|
||||||
tp: C.Mitm,
|
tp: C.Mitm,
|
||||||
},
|
},
|
||||||
|
serverAddr: serverAddr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,12 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
||||||
|
// SSR protocol compatibility
|
||||||
|
// https://github.com/Dreamacro/clash/pull/2056
|
||||||
|
if option.Cipher == "none" {
|
||||||
|
option.Cipher = "dummy"
|
||||||
|
}
|
||||||
|
|
||||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||||
cipher := option.Cipher
|
cipher := option.Cipher
|
||||||
password := option.Password
|
password := option.Password
|
||||||
@ -103,13 +109,14 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
|||||||
ivSize int
|
ivSize int
|
||||||
key []byte
|
key []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
if option.Cipher == "dummy" {
|
if option.Cipher == "dummy" {
|
||||||
ivSize = 0
|
ivSize = 0
|
||||||
key = core.Kdf(option.Password, 16)
|
key = core.Kdf(option.Password, 16)
|
||||||
} else {
|
} else {
|
||||||
ciph, ok := coreCiph.(*core.StreamCipher)
|
ciph, ok := coreCiph.(*core.StreamCipher)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s is not dummy or a supported stream cipher in ssr", cipher)
|
return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
|
||||||
}
|
}
|
||||||
ivSize = ciph.IVSize()
|
ivSize = ciph.IVSize()
|
||||||
key = ciph.Key
|
key = ciph.Key
|
||||||
|
@ -13,8 +13,8 @@ import (
|
|||||||
|
|
||||||
func tcpKeepAlive(c net.Conn) {
|
func tcpKeepAlive(c net.Conn) {
|
||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
if tcp, ok := c.(*net.TCPConn); ok {
|
||||||
tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlive(true)
|
||||||
tcp.SetKeepAlivePeriod(30 * time.Second)
|
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +25,14 @@ func serializesSocksAddr(metadata *C.Metadata) []byte {
|
|||||||
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
|
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
|
||||||
switch metadata.AddrType {
|
switch metadata.AddrType {
|
||||||
case socks5.AtypDomainName:
|
case socks5.AtypDomainName:
|
||||||
len := uint8(len(metadata.Host))
|
lenM := uint8(len(metadata.Host))
|
||||||
host := []byte(metadata.Host)
|
host := []byte(metadata.Host)
|
||||||
buf = [][]byte{{aType, len}, host, port}
|
buf = [][]byte{{aType, lenM}, host, port}
|
||||||
case socks5.AtypIPv4:
|
case socks5.AtypIPv4:
|
||||||
host := metadata.DstIP.To4()
|
host := metadata.DstIP.AsSlice()
|
||||||
buf = [][]byte{{aType}, host, port}
|
buf = [][]byte{{aType}, host, port}
|
||||||
case socks5.AtypIPv6:
|
case socks5.AtypIPv6:
|
||||||
host := metadata.DstIP.To16()
|
host := metadata.DstIP.AsSlice()
|
||||||
buf = [][]byte{{aType}, host, port}
|
buf = [][]byte{{aType}, host, port}
|
||||||
}
|
}
|
||||||
return bytes.Join(buf, nil)
|
return bytes.Join(buf, nil)
|
||||||
@ -53,6 +53,6 @@ func resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
|||||||
|
|
||||||
func safeConnClose(c net.Conn, err error) {
|
func safeConnClose(c net.Conn, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,11 +263,11 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
|
|||||||
case C.AtypIPv4:
|
case C.AtypIPv4:
|
||||||
addrType = byte(vless.AtypIPv4)
|
addrType = byte(vless.AtypIPv4)
|
||||||
addr = make([]byte, net.IPv4len)
|
addr = make([]byte, net.IPv4len)
|
||||||
copy(addr[:], metadata.DstIP.To4())
|
copy(addr[:], metadata.DstIP.AsSlice())
|
||||||
case C.AtypIPv6:
|
case C.AtypIPv6:
|
||||||
addrType = byte(vless.AtypIPv6)
|
addrType = byte(vless.AtypIPv6)
|
||||||
addr = make([]byte, net.IPv6len)
|
addr = make([]byte, net.IPv6len)
|
||||||
copy(addr[:], metadata.DstIP.To16())
|
copy(addr[:], metadata.DstIP.AsSlice())
|
||||||
case C.AtypDomainName:
|
case C.AtypDomainName:
|
||||||
addrType = byte(vless.AtypDomainName)
|
addrType = byte(vless.AtypDomainName)
|
||||||
addr = make([]byte, len(metadata.Host)+1)
|
addr = make([]byte, len(metadata.Host)+1)
|
||||||
|
@ -342,11 +342,11 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
|
|||||||
case C.AtypIPv4:
|
case C.AtypIPv4:
|
||||||
addrType = byte(vmess.AtypIPv4)
|
addrType = byte(vmess.AtypIPv4)
|
||||||
addr = make([]byte, net.IPv4len)
|
addr = make([]byte, net.IPv4len)
|
||||||
copy(addr[:], metadata.DstIP.To4())
|
copy(addr[:], metadata.DstIP.AsSlice())
|
||||||
case C.AtypIPv6:
|
case C.AtypIPv6:
|
||||||
addrType = byte(vmess.AtypIPv6)
|
addrType = byte(vmess.AtypIPv6)
|
||||||
addr = make([]byte, net.IPv6len)
|
addr = make([]byte, net.IPv6len)
|
||||||
copy(addr[:], metadata.DstIP.To16())
|
copy(addr[:], metadata.DstIP.AsSlice())
|
||||||
case C.AtypDomainName:
|
case C.AtypDomainName:
|
||||||
addrType = byte(vmess.AtypDomainName)
|
addrType = byte(vmess.AtypDomainName)
|
||||||
addr = make([]byte, len(metadata.Host)+1)
|
addr = make([]byte, len(metadata.Host)+1)
|
||||||
|
@ -50,7 +50,7 @@ func getKey(metadata *C.Metadata) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.DstIP == nil {
|
if !metadata.DstIP.IsValid() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -15,20 +16,20 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
addr = &C.Metadata{
|
addr = &C.Metadata{
|
||||||
AddrType: C.AtypDomainName,
|
AddrType: C.AtypDomainName,
|
||||||
Host: host,
|
Host: host,
|
||||||
DstIP: nil,
|
DstIP: netip.Addr{},
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if ip4 := ip.To4(); ip4 != nil {
|
} else if ip.Is4() {
|
||||||
addr = &C.Metadata{
|
addr = &C.Metadata{
|
||||||
AddrType: C.AtypIPv4,
|
AddrType: C.AtypIPv4,
|
||||||
Host: "",
|
Host: "",
|
||||||
DstIP: ip4,
|
DstIP: ip,
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -45,7 +46,7 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
|
|||||||
|
|
||||||
func tcpKeepAlive(c net.Conn) {
|
func tcpKeepAlive(c net.Conn) {
|
||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
if tcp, ok := c.(*net.TCPConn); ok {
|
||||||
tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlive(true)
|
||||||
tcp.SetKeepAlivePeriod(30 * time.Second)
|
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
53
common/nnip/netip.go
Normal file
53
common/nnip/netip.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package nnip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IpToAddr converts the net.IP to netip.Addr.
|
||||||
|
// If slice's length is not 4 or 16, IpToAddr returns netip.Addr{}
|
||||||
|
func IpToAddr(slice net.IP) netip.Addr {
|
||||||
|
ip := slice
|
||||||
|
if len(ip) != 4 {
|
||||||
|
if ip = slice.To4(); ip == nil {
|
||||||
|
ip = slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr, ok := netip.AddrFromSlice(ip); ok {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
return netip.Addr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnMasked returns p's last IP address.
|
||||||
|
// If p is invalid, UnMasked returns netip.Addr{}
|
||||||
|
func UnMasked(p netip.Prefix) netip.Addr {
|
||||||
|
if !p.IsValid() {
|
||||||
|
return netip.Addr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := p.Addr().As16()
|
||||||
|
|
||||||
|
hi := binary.BigEndian.Uint64(buf[:8])
|
||||||
|
lo := binary.BigEndian.Uint64(buf[8:])
|
||||||
|
|
||||||
|
bits := p.Bits()
|
||||||
|
if bits <= 32 {
|
||||||
|
bits += 96
|
||||||
|
}
|
||||||
|
|
||||||
|
hi = hi | ^uint64(0)>>bits
|
||||||
|
lo = lo | ^(^uint64(0) << (128 - bits))
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint64(buf[:8], hi)
|
||||||
|
binary.BigEndian.PutUint64(buf[8:], lo)
|
||||||
|
|
||||||
|
addr := netip.AddrFrom16(buf)
|
||||||
|
if p.Addr().Is4() {
|
||||||
|
return addr.Unmap()
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
@ -4,7 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/component/iface"
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
|
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
@ -15,14 +17,16 @@ var (
|
|||||||
ErrNotFound = errors.New("DNS option not found")
|
ErrNotFound = errors.New("DNS option not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]net.IP, error) {
|
func ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]netip.Addr, error) {
|
||||||
conn, err := ListenDHCPClient(context, ifaceName)
|
conn, err := ListenDHCPClient(context, ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer func() {
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
result := make(chan []net.IP, 1)
|
result := make(chan []netip.Addr, 1)
|
||||||
|
|
||||||
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,7 +56,7 @@ func ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]net.IP, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []net.IP) {
|
func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []netip.Addr) {
|
||||||
defer close(result)
|
defer close(result)
|
||||||
|
|
||||||
buf := make([]byte, dhcpv4.MaxMessageSize)
|
buf := make([]byte, dhcpv4.MaxMessageSize)
|
||||||
@ -77,11 +81,17 @@ func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []
|
|||||||
}
|
}
|
||||||
|
|
||||||
dns := pkt.DNS()
|
dns := pkt.DNS()
|
||||||
if len(dns) == 0 {
|
l := len(dns)
|
||||||
|
if l == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result <- dns
|
dnsAddr := make([]netip.Addr, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
dnsAddr[i] = nnip.IpToAddr(dns[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
result <- dnsAddr
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package dialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/iface"
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
@ -19,12 +20,9 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ipStr, _, err := net.SplitHostPort(address)
|
addrPort, err := netip.ParseAddrPort(address)
|
||||||
if err == nil {
|
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
|
||||||
ip := net.ParseIP(ipStr)
|
return
|
||||||
if ip != nil && !ip.IsGlobalUnicast() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerErr error
|
var innerErr error
|
||||||
@ -45,7 +43,7 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error {
|
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {
|
||||||
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2,6 +2,7 @@ package dialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@ -17,12 +18,9 @@ func bindControl(ifaceName string, chain controlFn) controlFn {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ipStr, _, err := net.SplitHostPort(address)
|
addrPort, err := netip.ParseAddrPort(address)
|
||||||
if err == nil {
|
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
|
||||||
ip := net.ParseIP(ipStr)
|
return
|
||||||
if ip != nil && !ip.IsGlobalUnicast() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerErr error
|
var innerErr error
|
||||||
@ -38,7 +36,7 @@ func bindControl(ifaceName string, chain controlFn) controlFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error {
|
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {
|
||||||
dialer.Control = bindControl(ifaceName, dialer.Control)
|
dialer.Control = bindControl(ifaceName, dialer.Control)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,27 +4,28 @@ package dialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/iface"
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lookupLocalAddr(ifaceName string, network string, destination net.IP, port int) (net.Addr, error) {
|
func lookupLocalAddr(ifaceName string, network string, destination netip.Addr, port int) (net.Addr, error) {
|
||||||
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
ifaceObj, err := iface.ResolveInterface(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr *net.IPNet
|
var addr *netip.Prefix
|
||||||
switch network {
|
switch network {
|
||||||
case "udp4", "tcp4":
|
case "udp4", "tcp4":
|
||||||
addr, err = ifaceObj.PickIPv4Addr(destination)
|
addr, err = ifaceObj.PickIPv4Addr(destination)
|
||||||
case "tcp6", "udp6":
|
case "tcp6", "udp6":
|
||||||
addr, err = ifaceObj.PickIPv6Addr(destination)
|
addr, err = ifaceObj.PickIPv6Addr(destination)
|
||||||
default:
|
default:
|
||||||
if destination != nil {
|
if destination.IsValid() {
|
||||||
if destination.To4() != nil {
|
if destination.Is4() {
|
||||||
addr, err = ifaceObj.PickIPv4Addr(destination)
|
addr, err = ifaceObj.PickIPv4Addr(destination)
|
||||||
} else {
|
} else {
|
||||||
addr, err = ifaceObj.PickIPv6Addr(destination)
|
addr, err = ifaceObj.PickIPv6Addr(destination)
|
||||||
@ -39,12 +40,12 @@ func lookupLocalAddr(ifaceName string, network string, destination net.IP, port
|
|||||||
|
|
||||||
if strings.HasPrefix(network, "tcp") {
|
if strings.HasPrefix(network, "tcp") {
|
||||||
return &net.TCPAddr{
|
return &net.TCPAddr{
|
||||||
IP: addr.IP,
|
IP: addr.Addr().AsSlice(),
|
||||||
Port: port,
|
Port: port,
|
||||||
}, nil
|
}, nil
|
||||||
} else if strings.HasPrefix(network, "udp") {
|
} else if strings.HasPrefix(network, "udp") {
|
||||||
return &net.UDPAddr{
|
return &net.UDPAddr{
|
||||||
IP: addr.IP,
|
IP: addr.Addr().AsSlice(),
|
||||||
Port: port,
|
Port: port,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -52,7 +53,7 @@ func lookupLocalAddr(ifaceName string, network string, destination net.IP, port
|
|||||||
return nil, iface.ErrAddrNotFound
|
return nil, iface.ErrAddrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination net.IP) error {
|
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
|
||||||
if !destination.IsGlobalUnicast() {
|
if !destination.IsGlobalUnicast() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -83,7 +84,7 @@ func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, add
|
|||||||
|
|
||||||
local, _ := strconv.ParseUint(port, 10, 16)
|
local, _ := strconv.ParseUint(port, 10, 16)
|
||||||
|
|
||||||
addr, err := lookupLocalAddr(ifaceName, network, nil, int(local))
|
addr, err := lookupLocalAddr(ifaceName, network, netip.Addr{}, int(local))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
)
|
)
|
||||||
@ -29,7 +30,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip net.IP
|
var ip netip.Addr
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp4", "udp4":
|
case "tcp4", "udp4":
|
||||||
if !opt.direct {
|
if !opt.direct {
|
||||||
@ -88,7 +89,7 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
|||||||
return lc.ListenPacket(ctx, network, address)
|
return lc.ListenPacket(ctx, network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialContext(ctx context.Context, network string, destination net.IP, port string, opt *option) (net.Conn, error) {
|
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||||
dialer := &net.Dialer{}
|
dialer := &net.Dialer{}
|
||||||
if opt.interfaceName != "" {
|
if opt.interfaceName != "" {
|
||||||
if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil {
|
if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil {
|
||||||
@ -128,12 +129,12 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
|||||||
case results <- result:
|
case results <- result:
|
||||||
case <-returned:
|
case <-returned:
|
||||||
if result.Conn != nil {
|
if result.Conn != nil {
|
||||||
result.Conn.Close()
|
_ = result.Conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var ip net.IP
|
var ip netip.Addr
|
||||||
if ipv6 {
|
if ipv6 {
|
||||||
if !direct {
|
if !direct {
|
||||||
ip, result.error = resolver.ResolveIPv6ProxyServerHost(host)
|
ip, result.error = resolver.ResolveIPv6ProxyServerHost(host)
|
||||||
|
@ -4,14 +4,15 @@ package dialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ net.IP) {
|
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) {
|
||||||
dialer.Control = bindMarkToControl(mark, dialer.Control)
|
dialer.Control = bindMarkToControl(mark, dialer.Control)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
|
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {
|
||||||
lc.Control = bindMarkToControl(mark, lc.Control)
|
lc.Control = bindMarkToControl(mark, lc.Control)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,20 +24,17 @@ func bindMarkToControl(mark int, chain controlFn) controlFn {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ipStr, _, err := net.SplitHostPort(address)
|
addrPort, err := netip.ParseAddrPort(address)
|
||||||
if err == nil {
|
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
|
||||||
ip := net.ParseIP(ipStr)
|
return
|
||||||
if ip != nil && !ip.IsGlobalUnicast() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Control(func(fd uintptr) {
|
return c.Control(func(fd uintptr) {
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp4", "udp4":
|
case "tcp4", "udp4":
|
||||||
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":
|
case "tcp6", "udp6":
|
||||||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
|
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ package dialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
@ -17,10 +18,10 @@ func printMarkWarn() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ net.IP) {
|
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) {
|
||||||
printMarkWarn()
|
printMarkWarn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
|
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {
|
||||||
printMarkWarn()
|
printMarkWarn()
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package dialer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -18,9 +19,9 @@ func resolverDialContext(ctx context.Context, network, address string) (net.Conn
|
|||||||
interfaceName := DefaultInterface.Load()
|
interfaceName := DefaultInterface.Load()
|
||||||
|
|
||||||
if interfaceName != "" {
|
if interfaceName != "" {
|
||||||
dstIP := net.ParseIP(address)
|
dstIP, err := netip.ParseAddr(address)
|
||||||
if dstIP != nil {
|
if err == nil {
|
||||||
bindIfaceToDialer(interfaceName, d, network, dstIP)
|
_ = bindIfaceToDialer(interfaceName, d, network, dstIP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package fakeip
|
package fakeip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"math/bits"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/component/profile/cachefile"
|
"github.com/Dreamacro/clash/component/profile/cachefile"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
)
|
)
|
||||||
@ -16,11 +15,6 @@ const (
|
|||||||
cycleKey = "key-cycle-fake-ip"
|
cycleKey = "key-cycle-fake-ip"
|
||||||
)
|
)
|
||||||
|
|
||||||
type uint128 struct {
|
|
||||||
hi uint64
|
|
||||||
lo uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type store interface {
|
type store interface {
|
||||||
GetByHost(host string) (netip.Addr, bool)
|
GetByHost(host string) (netip.Addr, bool)
|
||||||
PutByHost(host string, ip netip.Addr)
|
PutByHost(host string, ip netip.Addr)
|
||||||
@ -122,7 +116,7 @@ func (p *Pool) FlushFakeIP() error {
|
|||||||
err := p.store.FlushFakeIP()
|
err := p.store.FlushFakeIP()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
p.cycle = false
|
p.cycle = false
|
||||||
p.offset = p.first
|
p.offset = p.first.Prev()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -173,10 +167,10 @@ func New(options Options) (*Pool, error) {
|
|||||||
hostAddr = options.IPNet.Masked().Addr()
|
hostAddr = options.IPNet.Masked().Addr()
|
||||||
gateway = hostAddr.Next()
|
gateway = hostAddr.Next()
|
||||||
first = gateway.Next().Next()
|
first = gateway.Next().Next()
|
||||||
last = add(hostAddr, 1<<uint64(hostAddr.BitLen()-options.IPNet.Bits())-1)
|
last = nnip.UnMasked(*options.IPNet)
|
||||||
)
|
)
|
||||||
|
|
||||||
if !options.IPNet.IsValid() || !first.Less(last) || !options.IPNet.Contains(last) {
|
if !options.IPNet.IsValid() || !first.IsValid() || !first.Less(last) {
|
||||||
return nil, errors.New("ipnet don't have valid ip")
|
return nil, errors.New("ipnet don't have valid ip")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,29 +195,3 @@ func New(options Options) (*Pool, error) {
|
|||||||
|
|
||||||
return pool, nil
|
return pool, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// add returns addr + n.
|
|
||||||
func add(addr netip.Addr, n uint64) netip.Addr {
|
|
||||||
buf := addr.As16()
|
|
||||||
|
|
||||||
u := uint128{
|
|
||||||
binary.BigEndian.Uint64(buf[:8]),
|
|
||||||
binary.BigEndian.Uint64(buf[8:]),
|
|
||||||
}
|
|
||||||
|
|
||||||
lo, carry := bits.Add64(u.lo, n, 0)
|
|
||||||
|
|
||||||
u.hi = u.hi + carry
|
|
||||||
u.lo = lo
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint64(buf[:8], u.hi)
|
|
||||||
binary.BigEndian.PutUint64(buf[8:], u.lo)
|
|
||||||
|
|
||||||
a := netip.AddrFrom16(buf)
|
|
||||||
|
|
||||||
if addr.Is4() {
|
|
||||||
return a.Unmap()
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
@ -240,13 +240,15 @@ func TestPool_FlushFileCache(t *testing.T) {
|
|||||||
err = pool.FlushFakeIP()
|
err = pool.FlushFakeIP()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
baz := pool.Lookup("foo.com")
|
|
||||||
next := pool.Lookup("baz.com")
|
next := pool.Lookup("baz.com")
|
||||||
|
baz := pool.Lookup("foo.com")
|
||||||
nero := pool.Lookup("foo.com")
|
nero := pool.Lookup("foo.com")
|
||||||
|
|
||||||
assert.True(t, foo == fox)
|
assert.True(t, foo == fox)
|
||||||
|
assert.True(t, foo == next)
|
||||||
assert.False(t, foo == baz)
|
assert.False(t, foo == baz)
|
||||||
assert.True(t, bar == bax)
|
assert.True(t, bar == bax)
|
||||||
|
assert.True(t, bar == baz)
|
||||||
assert.False(t, bar == next)
|
assert.False(t, bar == next)
|
||||||
assert.True(t, baz == nero)
|
assert.True(t, baz == nero)
|
||||||
}
|
}
|
||||||
@ -267,13 +269,15 @@ func TestPool_FlushMemoryCache(t *testing.T) {
|
|||||||
err := pool.FlushFakeIP()
|
err := pool.FlushFakeIP()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
baz := pool.Lookup("foo.com")
|
|
||||||
next := pool.Lookup("baz.com")
|
next := pool.Lookup("baz.com")
|
||||||
|
baz := pool.Lookup("foo.com")
|
||||||
nero := pool.Lookup("foo.com")
|
nero := pool.Lookup("foo.com")
|
||||||
|
|
||||||
assert.True(t, foo == fox)
|
assert.True(t, foo == fox)
|
||||||
|
assert.True(t, foo == next)
|
||||||
assert.False(t, foo == baz)
|
assert.False(t, foo == baz)
|
||||||
assert.True(t, bar == bax)
|
assert.True(t, bar == bax)
|
||||||
|
assert.True(t, bar == baz)
|
||||||
assert.False(t, bar == next)
|
assert.False(t, bar == next)
|
||||||
assert.True(t, baz == nero)
|
assert.True(t, baz == nero)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/singledo"
|
"github.com/Dreamacro/clash/common/singledo"
|
||||||
@ -11,7 +12,7 @@ import (
|
|||||||
type Interface struct {
|
type Interface struct {
|
||||||
Index int
|
Index int
|
||||||
Name string
|
Name string
|
||||||
Addrs []*net.IPNet
|
Addrs []*netip.Prefix
|
||||||
HardwareAddr net.HardwareAddr
|
HardwareAddr net.HardwareAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,14 +38,18 @@ func ResolveInterface(name string) (*Interface, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ipNets := make([]*net.IPNet, 0, len(addrs))
|
ipNets := make([]*netip.Prefix, 0, len(addrs))
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
ipNet := addr.(*net.IPNet)
|
ipNet := addr.(*net.IPNet)
|
||||||
if v4 := ipNet.IP.To4(); v4 != nil {
|
ip, _ := netip.AddrFromSlice(ipNet.IP)
|
||||||
ipNet.IP = v4
|
|
||||||
|
ones, bits := ipNet.Mask.Size()
|
||||||
|
if bits == 32 {
|
||||||
|
ip = ip.Unmap()
|
||||||
}
|
}
|
||||||
|
|
||||||
ipNets = append(ipNets, ipNet)
|
pf := netip.PrefixFrom(ip, ones)
|
||||||
|
ipNets = append(ipNets, &pf)
|
||||||
}
|
}
|
||||||
|
|
||||||
r[iface.Name] = &Interface{
|
r[iface.Name] = &Interface{
|
||||||
@ -74,35 +79,35 @@ func FlushCache() {
|
|||||||
interfaces.Reset()
|
interfaces.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iface *Interface) PickIPv4Addr(destination net.IP) (*net.IPNet, error) {
|
func (iface *Interface) PickIPv4Addr(destination netip.Addr) (*netip.Prefix, error) {
|
||||||
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
|
return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool {
|
||||||
return addr.IP.To4() != nil
|
return addr.Addr().Is4()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iface *Interface) PickIPv6Addr(destination net.IP) (*net.IPNet, error) {
|
func (iface *Interface) PickIPv6Addr(destination netip.Addr) (*netip.Prefix, error) {
|
||||||
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
|
return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool {
|
||||||
return addr.IP.To4() == nil
|
return addr.Addr().Is6()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iface *Interface) pickIPAddr(destination net.IP, accept func(addr *net.IPNet) bool) (*net.IPNet, error) {
|
func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr *netip.Prefix) bool) (*netip.Prefix, error) {
|
||||||
var fallback *net.IPNet
|
var fallback *netip.Prefix
|
||||||
|
|
||||||
for _, addr := range iface.Addrs {
|
for _, addr := range iface.Addrs {
|
||||||
if !accept(addr) {
|
if !accept(addr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if fallback == nil && !addr.IP.IsLinkLocalUnicast() {
|
if fallback == nil && !addr.Addr().IsLinkLocalUnicast() {
|
||||||
fallback = addr
|
fallback = addr
|
||||||
|
|
||||||
if destination == nil {
|
if !destination.IsValid() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if destination != nil && addr.Contains(destination) {
|
if destination.IsValid() && addr.Contains(destination) {
|
||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ package process
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ const (
|
|||||||
UDP = "udp"
|
UDP = "udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FindProcessName(network string, srcIP net.IP, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,23 +29,23 @@ func ShouldFindProcess(metadata *C.Metadata) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, ip := range localIPs {
|
for _, ip := range localIPs {
|
||||||
if ip.Equal(metadata.SrcIP) {
|
if ip == metadata.SrcIP {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppendLocalIPs(ip ...net.IP) {
|
func AppendLocalIPs(ip ...netip.Addr) {
|
||||||
localIPs = append(ip, localIPs...)
|
localIPs = append(ip, localIPs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLocalIPs() []net.IP {
|
func getLocalIPs() []netip.Addr {
|
||||||
ips := []net.IP{net.IPv4zero, net.IPv6zero}
|
ips := []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()}
|
||||||
|
|
||||||
netInterfaces, err := net.Interfaces()
|
netInterfaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ips = append(ips, net.IPv4(127, 0, 0, 1), net.IPv6loopback)
|
ips = append(ips, netip.AddrFrom4([4]byte{127, 0, 0, 1}), nnip.IpToAddr(net.IPv6loopback))
|
||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ func getLocalIPs() []net.IP {
|
|||||||
|
|
||||||
for _, address := range adds {
|
for _, address := range adds {
|
||||||
if ipNet, ok := address.(*net.IPNet); ok {
|
if ipNet, ok := address.(*net.IPNet); ok {
|
||||||
ips = append(ips, ipNet.IP)
|
ips = append(ips, nnip.IpToAddr(ipNet.IP))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +64,7 @@ func getLocalIPs() []net.IP {
|
|||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
var localIPs []net.IP
|
var localIPs []netip.Addr
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
localIPs = getLocalIPs()
|
localIPs = getLocalIPs()
|
||||||
|
@ -2,10 +2,12 @@ package process
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ const (
|
|||||||
proccallnumpidinfo = 0x2
|
proccallnumpidinfo = 0x2
|
||||||
)
|
)
|
||||||
|
|
||||||
func findProcessName(network string, ip net.IP, port int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||||
var spath string
|
var spath string
|
||||||
switch network {
|
switch network {
|
||||||
case TCP:
|
case TCP:
|
||||||
@ -26,7 +28,7 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
|
|||||||
return "", ErrInvalidNetwork
|
return "", ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
isIPv4 := ip.To4() != nil
|
isIPv4 := ip.Is4()
|
||||||
|
|
||||||
value, err := syscall.Sysctl(spath)
|
value, err := syscall.Sysctl(spath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -57,19 +59,19 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
|
|||||||
// xinpcb_n.inp_vflag
|
// xinpcb_n.inp_vflag
|
||||||
flag := buf[inp+44]
|
flag := buf[inp+44]
|
||||||
|
|
||||||
var srcIP net.IP
|
var srcIP netip.Addr
|
||||||
switch {
|
switch {
|
||||||
case flag&0x1 > 0 && isIPv4:
|
case flag&0x1 > 0 && isIPv4:
|
||||||
// ipv4
|
// ipv4
|
||||||
srcIP = net.IP(buf[inp+76 : inp+80])
|
srcIP = nnip.IpToAddr(buf[inp+76 : inp+80])
|
||||||
case flag&0x2 > 0 && !isIPv4:
|
case flag&0x2 > 0 && !isIPv4:
|
||||||
// ipv6
|
// ipv6
|
||||||
srcIP = net.IP(buf[inp+64 : inp+80])
|
srcIP = nnip.IpToAddr(buf[inp+64 : inp+80])
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ip.Equal(srcIP) && (network == TCP || !srcIP.IsUnspecified()) {
|
if ip != srcIP && (network == TCP || !srcIP.IsUnspecified()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@ package process
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
if err := initSearcher(); err != nil {
|
if err := initSearcher(); err != nil {
|
||||||
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
||||||
@ -102,7 +103,7 @@ type searcher struct {
|
|||||||
pid int
|
pid int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint32, error) {
|
func (s *searcher) Search(buf []byte, ip netip.Addr, port uint16, isTCP bool) (uint32, error) {
|
||||||
var itemSize int
|
var itemSize int
|
||||||
var inpOffset int
|
var inpOffset int
|
||||||
|
|
||||||
@ -116,7 +117,7 @@ func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint3
|
|||||||
inpOffset = s.udpInpOffset
|
inpOffset = s.udpInpOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
isIPv4 := ip.To4() != nil
|
isIPv4 := ip.Is4()
|
||||||
// skip the first xinpgen block
|
// skip the first xinpgen block
|
||||||
for i := s.headSize; i+itemSize <= len(buf); i += itemSize {
|
for i := s.headSize; i+itemSize <= len(buf); i += itemSize {
|
||||||
inp := i + inpOffset
|
inp := i + inpOffset
|
||||||
@ -130,19 +131,19 @@ func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint3
|
|||||||
// xinpcb.inp_vflag
|
// xinpcb.inp_vflag
|
||||||
flag := buf[inp+s.vflag]
|
flag := buf[inp+s.vflag]
|
||||||
|
|
||||||
var srcIP net.IP
|
var srcIP netip.Addr
|
||||||
switch {
|
switch {
|
||||||
case flag&0x1 > 0 && isIPv4:
|
case flag&0x1 > 0 && isIPv4:
|
||||||
// ipv4
|
// ipv4
|
||||||
srcIP = net.IP(buf[inp+s.ip : inp+s.ip+4])
|
srcIP = nnip.IpToAddr(buf[inp+s.ip : inp+s.ip+4])
|
||||||
case flag&0x2 > 0 && !isIPv4:
|
case flag&0x2 > 0 && !isIPv4:
|
||||||
// ipv6
|
// ipv6
|
||||||
srcIP = net.IP(buf[inp+s.ip-12 : inp+s.ip+4])
|
srcIP = nnip.IpToAddr(buf[inp+s.ip-12 : inp+s.ip+4])
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ip.Equal(srcIP) {
|
if ip != srcIP {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@ -31,7 +32,7 @@ const (
|
|||||||
pathProc = "/proc"
|
pathProc = "/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
||||||
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
|
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -40,7 +41,7 @@ func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
|||||||
return resolveProcessNameByProcSearch(inode, uid)
|
return resolveProcessNameByProcSearch(inode, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int32, error) {
|
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {
|
||||||
var family byte
|
var family byte
|
||||||
var protocol byte
|
var protocol byte
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
|
|||||||
return 0, 0, ErrInvalidNetwork
|
return 0, 0, ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip.To4() != nil {
|
if ip.Is4() {
|
||||||
family = syscall.AF_INET
|
family = syscall.AF_INET
|
||||||
} else {
|
} else {
|
||||||
family = syscall.AF_INET6
|
family = syscall.AF_INET6
|
||||||
@ -65,10 +66,12 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("dial netlink: %w", err)
|
return 0, 0, fmt.Errorf("dial netlink: %w", err)
|
||||||
}
|
}
|
||||||
defer syscall.Close(socket)
|
defer func() {
|
||||||
|
_ = syscall.Close(socket)
|
||||||
|
}()
|
||||||
|
|
||||||
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
|
_ = syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
|
||||||
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
|
_ = syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
|
||||||
|
|
||||||
if err := syscall.Connect(socket, &syscall.SockaddrNetlink{
|
if err := syscall.Connect(socket, &syscall.SockaddrNetlink{
|
||||||
Family: syscall.AF_NETLINK,
|
Family: syscall.AF_NETLINK,
|
||||||
@ -84,7 +87,9 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
|
|||||||
}
|
}
|
||||||
|
|
||||||
rb := pool.Get(pool.RelayBufferSize)
|
rb := pool.Get(pool.RelayBufferSize)
|
||||||
defer pool.Put(rb)
|
defer func() {
|
||||||
|
_ = pool.Put(rb)
|
||||||
|
}()
|
||||||
|
|
||||||
n, err := syscall.Read(socket, rb)
|
n, err := syscall.Read(socket, rb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -111,14 +116,10 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
|
|||||||
return inode, uid, nil
|
return inode, uid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte {
|
func packSocketDiagRequest(family, protocol byte, source netip.Addr, sourcePort uint16) []byte {
|
||||||
s := make([]byte, 16)
|
s := make([]byte, 16)
|
||||||
|
|
||||||
if v4 := source.To4(); v4 != nil {
|
copy(s, source.AsSlice())
|
||||||
copy(s, v4)
|
|
||||||
} else {
|
|
||||||
copy(s, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, sizeOfSocketDiagRequest)
|
buf := make([]byte, sizeOfSocketDiagRequest)
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
package process
|
package process
|
||||||
|
|
||||||
import "net"
|
import "net/netip"
|
||||||
|
|
||||||
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
||||||
return "", ErrPlatformNotSupport
|
return "", ErrPlatformNotSupport
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@ package process
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
"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"
|
||||||
@ -57,7 +58,7 @@ func initWin32API() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
err := initWin32API()
|
err := initWin32API()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,7 +68,7 @@ func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
family := windows.AF_INET
|
family := windows.AF_INET
|
||||||
if ip.To4() == nil {
|
if ip.Is6() {
|
||||||
family = windows.AF_INET6
|
family = windows.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +108,7 @@ type searcher struct {
|
|||||||
tcpState int
|
tcpState int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *searcher) Search(b []byte, ip net.IP, port uint16) (uint32, error) {
|
func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error) {
|
||||||
n := int(readNativeUint32(b[:4]))
|
n := int(readNativeUint32(b[:4]))
|
||||||
itemSize := s.itemSize
|
itemSize := s.itemSize
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
@ -131,9 +132,9 @@ func (s *searcher) Search(b []byte, ip net.IP, port uint16) (uint32, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := net.IP(row[s.ip : s.ip+s.ipSize])
|
srcIP := nnip.IpToAddr(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.Equal(srcIP) && (!srcIP.IsUnspecified() || s.tcpState != -1) {
|
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +175,7 @@ func newSearcher(isV4, isTCP bool) *searcher {
|
|||||||
func getTransportTable(fn uintptr, family int, class int) ([]byte, error) {
|
func getTransportTable(fn uintptr, family int, class int) ([]byte, error) {
|
||||||
for size, buf := uint32(8), make([]byte, 8); ; {
|
for size, buf := uint32(8), make([]byte, 8); ; {
|
||||||
ptr := unsafe.Pointer(&buf[0])
|
ptr := unsafe.Pointer(&buf[0])
|
||||||
err, _, _ := syscall.Syscall6(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0)
|
err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0)
|
||||||
|
|
||||||
switch err {
|
switch err {
|
||||||
case 0:
|
case 0:
|
||||||
@ -209,13 +210,13 @@ func getExecPathFromPID(pid uint32) (string, error) {
|
|||||||
|
|
||||||
buf := make([]uint16, syscall.MAX_LONG_PATH)
|
buf := make([]uint16, syscall.MAX_LONG_PATH)
|
||||||
size := uint32(len(buf))
|
size := uint32(len(buf))
|
||||||
r1, _, err := syscall.Syscall6(
|
r1, _, err := syscall.SyscallN(
|
||||||
queryProcName, 4,
|
queryProcName,
|
||||||
uintptr(h),
|
uintptr(h),
|
||||||
uintptr(1),
|
uintptr(1),
|
||||||
uintptr(unsafe.Pointer(&buf[0])),
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
uintptr(unsafe.Pointer(&size)),
|
uintptr(unsafe.Pointer(&size)),
|
||||||
0, 0)
|
)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
12
component/resolver/defaults.go
Normal file
12
component/resolver/defaults.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
|
|
||||||
|
package resolver
|
||||||
|
|
||||||
|
import _ "unsafe"
|
||||||
|
|
||||||
|
//go:linkname defaultNS net.defaultNS
|
||||||
|
var defaultNS []string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"}
|
||||||
|
}
|
@ -1,21 +1,19 @@
|
|||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import (
|
import "net/netip"
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var DefaultHostMapper Enhancer
|
var DefaultHostMapper Enhancer
|
||||||
|
|
||||||
type Enhancer interface {
|
type Enhancer interface {
|
||||||
FakeIPEnabled() bool
|
FakeIPEnabled() bool
|
||||||
MappingEnabled() bool
|
MappingEnabled() bool
|
||||||
IsFakeIP(net.IP) bool
|
IsFakeIP(netip.Addr) bool
|
||||||
IsFakeBroadcastIP(net.IP) bool
|
IsFakeBroadcastIP(netip.Addr) bool
|
||||||
IsExistFakeIP(net.IP) bool
|
IsExistFakeIP(netip.Addr) bool
|
||||||
FindHostByIP(net.IP) (string, bool)
|
FindHostByIP(netip.Addr) (string, bool)
|
||||||
FlushFakeIP() error
|
FlushFakeIP() error
|
||||||
InsertHostByIP(net.IP, string)
|
InsertHostByIP(netip.Addr, string)
|
||||||
StoreFakePoolSate()
|
StoreFakePoolState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func FakeIPEnabled() bool {
|
func FakeIPEnabled() bool {
|
||||||
@ -34,7 +32,7 @@ func MappingEnabled() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsFakeIP(ip net.IP) bool {
|
func IsFakeIP(ip netip.Addr) bool {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
return mapper.IsFakeIP(ip)
|
return mapper.IsFakeIP(ip)
|
||||||
}
|
}
|
||||||
@ -42,7 +40,7 @@ func IsFakeIP(ip net.IP) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsFakeBroadcastIP(ip net.IP) bool {
|
func IsFakeBroadcastIP(ip netip.Addr) bool {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
return mapper.IsFakeBroadcastIP(ip)
|
return mapper.IsFakeBroadcastIP(ip)
|
||||||
}
|
}
|
||||||
@ -50,7 +48,7 @@ func IsFakeBroadcastIP(ip net.IP) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsExistFakeIP(ip net.IP) bool {
|
func IsExistFakeIP(ip netip.Addr) bool {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
return mapper.IsExistFakeIP(ip)
|
return mapper.IsExistFakeIP(ip)
|
||||||
}
|
}
|
||||||
@ -58,13 +56,13 @@ func IsExistFakeIP(ip net.IP) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertHostByIP(ip net.IP, host string) {
|
func InsertHostByIP(ip netip.Addr, host string) {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
mapper.InsertHostByIP(ip, host)
|
mapper.InsertHostByIP(ip, host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindHostByIP(ip net.IP) (string, bool) {
|
func FindHostByIP(ip netip.Addr) (string, bool) {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
return mapper.FindHostByIP(ip)
|
return mapper.FindHostByIP(ip)
|
||||||
}
|
}
|
||||||
@ -79,8 +77,8 @@ func FlushFakeIP() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreFakePoolSate() {
|
func StoreFakePoolState() {
|
||||||
if mapper := DefaultHostMapper; mapper != nil {
|
if mapper := DefaultHostMapper; mapper != nil {
|
||||||
mapper.StoreFakePoolSate()
|
mapper.StoreFakePoolState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,29 +37,29 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Resolver interface {
|
type Resolver interface {
|
||||||
ResolveIP(host string) (ip net.IP, err error)
|
ResolveIP(host string) (ip netip.Addr, err error)
|
||||||
ResolveIPv4(host string) (ip net.IP, err error)
|
ResolveIPv4(host string) (ip netip.Addr, err error)
|
||||||
ResolveIPv6(host string) (ip net.IP, err error)
|
ResolveIPv6(host string) (ip netip.Addr, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 with a host, return ipv4
|
// ResolveIPv4 with a host, return ipv4
|
||||||
func ResolveIPv4(host string) (net.IP, error) {
|
func ResolveIPv4(host string) (netip.Addr, error) {
|
||||||
return ResolveIPv4WithResolver(host, DefaultResolver)
|
return ResolveIPv4WithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
func ResolveIPv4WithResolver(host string, r Resolver) (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.AsSlice(), nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if ip != nil {
|
if err == nil {
|
||||||
if !strings.Contains(host, ":") {
|
if ip.Is4() {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
return nil, ErrIPVersion
|
return netip.Addr{}, ErrIPVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
@ -71,39 +71,44 @@ func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.Addr{}, err
|
||||||
} else if len(ipAddrs) == 0 {
|
} else if len(ipAddrs) == 0 {
|
||||||
return nil, ErrIPNotFound
|
return netip.Addr{}, ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
ip := ipAddrs[rand.Intn(len(ipAddrs))].To4()
|
||||||
|
if ip == nil {
|
||||||
|
return netip.Addr{}, ErrIPVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return netip.AddrFrom4(*(*[4]byte)(ip)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrIPNotFound
|
return netip.Addr{}, ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6 with a host, return ipv6
|
// ResolveIPv6 with a host, return ipv6
|
||||||
func ResolveIPv6(host string) (net.IP, error) {
|
func ResolveIPv6(host string) (netip.Addr, error) {
|
||||||
return ResolveIPv6WithResolver(host, DefaultResolver)
|
return ResolveIPv6WithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
|
||||||
if DisableIPv6 {
|
if DisableIPv6 {
|
||||||
return nil, ErrIPv6Disabled
|
return netip.Addr{}, ErrIPv6Disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
if node := DefaultHosts.Search(host); node != nil {
|
if node := DefaultHosts.Search(host); node != nil {
|
||||||
if ip := node.Data; ip.Is6() {
|
if ip := node.Data; ip.Is6() {
|
||||||
return ip.AsSlice(), nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if ip != nil {
|
if err == nil {
|
||||||
if strings.Contains(host, ":") {
|
if ip.Is6() {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
return nil, ErrIPVersion
|
return netip.Addr{}, ErrIPVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
@ -115,22 +120,21 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
|
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.Addr{}, err
|
||||||
} else if len(ipAddrs) == 0 {
|
} else if len(ipAddrs) == 0 {
|
||||||
return nil, ErrIPNotFound
|
return netip.Addr{}, ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipAddrs[rand.Intn(len(ipAddrs))], nil
|
return netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))])), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, 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) (net.IP, error) {
|
func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
|
||||||
if node := DefaultHosts.Search(host); node != nil {
|
if node := DefaultHosts.Search(host); node != nil {
|
||||||
ip := node.Data
|
return node.Data, nil
|
||||||
return ip.Unmap().AsSlice(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil {
|
||||||
@ -142,30 +146,30 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
|
|||||||
return ResolveIPv4(host)
|
return ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if ip != nil {
|
if err == nil {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if DefaultResolver == nil {
|
if DefaultResolver == nil {
|
||||||
ipAddr, err := net.ResolveIPAddr("ip", host)
|
ipAddr, err := net.ResolveIPAddr("ip", host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.Addr{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipAddr.IP, nil
|
return nnip.IpToAddr(ipAddr.IP), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrIPNotFound
|
return netip.Addr{}, ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIP with a host, return ip
|
// ResolveIP with a host, return ip
|
||||||
func ResolveIP(host string) (net.IP, error) {
|
func ResolveIP(host string) (netip.Addr, error) {
|
||||||
return ResolveIPWithResolver(host, DefaultResolver)
|
return ResolveIPWithResolver(host, DefaultResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4ProxyServerHost proxies server host only
|
// ResolveIPv4ProxyServerHost proxies server host only
|
||||||
func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
|
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
|
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
|
||||||
}
|
}
|
||||||
@ -173,7 +177,7 @@ func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6ProxyServerHost proxies server host only
|
// ResolveIPv6ProxyServerHost proxies server host only
|
||||||
func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
|
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
|
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
|
||||||
}
|
}
|
||||||
@ -181,7 +185,7 @@ func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveProxyServerHost proxies server host only
|
// ResolveProxyServerHost proxies server host only
|
||||||
func ResolveProxyServerHost(host string) (net.IP, error) {
|
func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPWithResolver(host, ProxyServerHostResolver)
|
return ResolveIPWithResolver(host, ProxyServerHostResolver)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//go:build build_local
|
//go:build build_local
|
||||||
// +build build_local
|
|
||||||
|
|
||||||
package script
|
package script
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//go:build !build_local && cgo
|
//go:build !build_local && cgo
|
||||||
// +build !build_local,cgo
|
|
||||||
|
|
||||||
package script
|
package script
|
||||||
|
|
||||||
|
@ -195,10 +195,10 @@ func CallPyMainFunction(mtd *constant.Metadata) (string, error) {
|
|||||||
|
|
||||||
dstIpGo := ""
|
dstIpGo := ""
|
||||||
srcIpGo := ""
|
srcIpGo := ""
|
||||||
if mtd.SrcIP != nil {
|
if mtd.SrcIP.IsValid() {
|
||||||
srcIpGo = mtd.SrcIP.String()
|
srcIpGo = mtd.SrcIP.String()
|
||||||
}
|
}
|
||||||
if mtd.DstIP != nil {
|
if mtd.DstIP.IsValid() {
|
||||||
dstIpGo = mtd.DstIP.String()
|
dstIpGo = mtd.DstIP.String()
|
||||||
}
|
}
|
||||||
srcIp := C.CString(srcIpGo)
|
srcIp := C.CString(srcIpGo)
|
||||||
@ -250,10 +250,10 @@ func CallPyShortcut(fn *PyObject, mtd *constant.Metadata) (bool, error) {
|
|||||||
|
|
||||||
dstIpGo := ""
|
dstIpGo := ""
|
||||||
srcIpGo := ""
|
srcIpGo := ""
|
||||||
if mtd.SrcIP != nil {
|
if mtd.SrcIP.IsValid() {
|
||||||
srcIpGo = mtd.SrcIP.String()
|
srcIpGo = mtd.SrcIP.String()
|
||||||
}
|
}
|
||||||
if mtd.DstIP != nil {
|
if mtd.DstIP.IsValid() {
|
||||||
dstIpGo = mtd.DstIP.String()
|
dstIpGo = mtd.DstIP.String()
|
||||||
}
|
}
|
||||||
srcIp := C.CString(srcIpGo)
|
srcIp := C.CString(srcIpGo)
|
||||||
|
@ -5,7 +5,7 @@ package script
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -50,9 +50,9 @@ func resolveIPCallbackFn(cHost *C.char) *C.char {
|
|||||||
|
|
||||||
//export geoipCallbackFn
|
//export geoipCallbackFn
|
||||||
func geoipCallbackFn(cIP *C.char) *C.char {
|
func geoipCallbackFn(cIP *C.char) *C.char {
|
||||||
dstIP := net.ParseIP(C.GoString(cIP))
|
dstIP, err := netip.ParseAddr(C.GoString(cIP))
|
||||||
|
|
||||||
if dstIP == nil {
|
if err != nil {
|
||||||
emptyC := C.CString("")
|
emptyC := C.CString("")
|
||||||
defer C.free(unsafe.Pointer(emptyC))
|
defer C.free(unsafe.Pointer(emptyC))
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ func geoipCallbackFn(cIP *C.char) *C.char {
|
|||||||
return lanC
|
return lanC
|
||||||
}
|
}
|
||||||
|
|
||||||
record, _ := mmdb.Instance().Country(dstIP)
|
record, _ := mmdb.Instance().Country(dstIP.AsSlice())
|
||||||
|
|
||||||
rc := C.CString(strings.ToUpper(record.Country.IsoCode))
|
rc := C.CString(strings.ToUpper(record.Country.IsoCode))
|
||||||
defer C.free(unsafe.Pointer(rc))
|
defer C.free(unsafe.Pointer(rc))
|
||||||
@ -91,20 +91,22 @@ func ruleProviderCallbackFn(cProviderName *C.char, cMetadata *C.struct_Metadata)
|
|||||||
dstIp := C.GoString(cMetadata.dst_ip)
|
dstIp := C.GoString(cMetadata.dst_ip)
|
||||||
dstPort := strconv.Itoa(int(cMetadata.dst_port))
|
dstPort := strconv.Itoa(int(cMetadata.dst_port))
|
||||||
|
|
||||||
dst := net.ParseIP(dstIp)
|
dst, err := netip.ParseAddr(dstIp)
|
||||||
addrType := constant.AtypDomainName
|
|
||||||
|
|
||||||
if dst != nil {
|
addrType := constant.AtypDomainName
|
||||||
if dst.To4() != nil {
|
if err == nil {
|
||||||
|
if dst.Is4() {
|
||||||
addrType = constant.AtypIPv4
|
addrType = constant.AtypIPv4
|
||||||
} else {
|
} else {
|
||||||
addrType = constant.AtypIPv6
|
addrType = constant.AtypIPv6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
src, _ := netip.ParseAddr(srcIp)
|
||||||
|
|
||||||
metadata := &constant.Metadata{
|
metadata := &constant.Metadata{
|
||||||
Process: processName,
|
Process: processName,
|
||||||
SrcIP: net.ParseIP(srcIp),
|
SrcIP: src,
|
||||||
DstIP: dst,
|
DstIP: dst,
|
||||||
SrcPort: srcPort,
|
SrcPort: srcPort,
|
||||||
DstPort: dstPort,
|
DstPort: dstPort,
|
||||||
|
@ -85,7 +85,7 @@ type DNS struct {
|
|||||||
type FallbackFilter struct {
|
type FallbackFilter struct {
|
||||||
GeoIP bool `yaml:"geoip"`
|
GeoIP bool `yaml:"geoip"`
|
||||||
GeoIPCode string `yaml:"geoip-code"`
|
GeoIPCode string `yaml:"geoip-code"`
|
||||||
IPCIDR []*net.IPNet `yaml:"ipcidr"`
|
IPCIDR []*netip.Prefix `yaml:"ipcidr"`
|
||||||
Domain []string `yaml:"domain"`
|
Domain []string `yaml:"domain"`
|
||||||
GeoSite []*router.DomainMatcher `yaml:"geosite"`
|
GeoSite []*router.DomainMatcher `yaml:"geosite"`
|
||||||
}
|
}
|
||||||
@ -685,15 +685,15 @@ func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServe
|
|||||||
return policy, nil
|
return policy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) {
|
func parseFallbackIPCIDR(ips []string) ([]*netip.Prefix, error) {
|
||||||
var ipNets []*net.IPNet
|
var ipNets []*netip.Prefix
|
||||||
|
|
||||||
for idx, ip := range ips {
|
for idx, ip := range ips {
|
||||||
_, ipnet, err := net.ParseCIDR(ip)
|
ipnet, err := netip.ParsePrefix(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
|
return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
|
||||||
}
|
}
|
||||||
ipNets = append(ipNets, ipnet)
|
ipNets = append(ipNets, &ipnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipNets, nil
|
return ipNets, nil
|
||||||
@ -741,7 +741,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R
|
|||||||
IPv6: cfg.IPv6,
|
IPv6: cfg.IPv6,
|
||||||
EnhancedMode: cfg.EnhancedMode,
|
EnhancedMode: cfg.EnhancedMode,
|
||||||
FallbackFilter: FallbackFilter{
|
FallbackFilter: FallbackFilter{
|
||||||
IPCIDR: []*net.IPNet{},
|
IPCIDR: []*netip.Prefix{},
|
||||||
GeoSite: []*router.DomainMatcher{},
|
GeoSite: []*router.DomainMatcher{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package constant
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,22 +73,22 @@ func (t Type) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
// Metadata is used to store connection address
|
// Metadata is used to store connection address
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
NetWork NetWork `json:"network"`
|
NetWork NetWork `json:"network"`
|
||||||
Type Type `json:"type"`
|
Type Type `json:"type"`
|
||||||
SrcIP net.IP `json:"sourceIP"`
|
SrcIP netip.Addr `json:"sourceIP"`
|
||||||
DstIP net.IP `json:"destinationIP"`
|
DstIP netip.Addr `json:"destinationIP"`
|
||||||
SrcPort string `json:"sourcePort"`
|
SrcPort string `json:"sourcePort"`
|
||||||
DstPort string `json:"destinationPort"`
|
DstPort string `json:"destinationPort"`
|
||||||
AddrType int `json:"-"`
|
AddrType int `json:"-"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
DNSMode DNSMode `json:"dnsMode"`
|
DNSMode DNSMode `json:"dnsMode"`
|
||||||
Process string `json:"process"`
|
Process string `json:"process"`
|
||||||
ProcessPath string `json:"processPath"`
|
ProcessPath string `json:"processPath"`
|
||||||
UserAgent string `json:"userAgent"`
|
UserAgent string `json:"userAgent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) RemoteAddress() string {
|
func (m *Metadata) RemoteAddress() string {
|
||||||
if m.DstIP != nil {
|
if m.DstIP.IsValid() {
|
||||||
return net.JoinHostPort(m.DstIP.String(), m.DstPort)
|
return net.JoinHostPort(m.DstIP.String(), m.DstPort)
|
||||||
} else {
|
} else {
|
||||||
return net.JoinHostPort(m.String(), m.DstPort)
|
return net.JoinHostPort(m.String(), m.DstPort)
|
||||||
@ -99,33 +100,33 @@ func (m *Metadata) SourceAddress() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) Resolved() bool {
|
func (m *Metadata) Resolved() bool {
|
||||||
return m.DstIP != nil
|
return m.DstIP.IsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure is used to solve unexpected behavior
|
// Pure is used to solve unexpected behavior
|
||||||
// when dialing proxy connection in DNSMapping mode.
|
// when dialing proxy connection in DNSMapping mode.
|
||||||
func (m *Metadata) Pure() *Metadata {
|
func (m *Metadata) Pure() *Metadata {
|
||||||
if m.DNSMode == DNSMapping && m.DstIP != nil {
|
if m.DNSMode == DNSMapping && m.DstIP.IsValid() {
|
||||||
copy := *m
|
copyM := *m
|
||||||
copy.Host = ""
|
copyM.Host = ""
|
||||||
if copy.DstIP.To4() != nil {
|
if copyM.DstIP.Is4() {
|
||||||
copy.AddrType = AtypIPv4
|
copyM.AddrType = AtypIPv4
|
||||||
} else {
|
} else {
|
||||||
copy.AddrType = AtypIPv6
|
copyM.AddrType = AtypIPv6
|
||||||
}
|
}
|
||||||
return ©
|
return ©M
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) UDPAddr() *net.UDPAddr {
|
func (m *Metadata) UDPAddr() *net.UDPAddr {
|
||||||
if m.NetWork != UDP || m.DstIP == nil {
|
if m.NetWork != UDP || !m.DstIP.IsValid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
port, _ := strconv.ParseUint(m.DstPort, 10, 16)
|
port, _ := strconv.ParseUint(m.DstPort, 10, 16)
|
||||||
return &net.UDPAddr{
|
return &net.UDPAddr{
|
||||||
IP: m.DstIP,
|
IP: m.DstIP.AsSlice(),
|
||||||
Port: int(port),
|
Port: int(port),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +134,7 @@ func (m *Metadata) UDPAddr() *net.UDPAddr {
|
|||||||
func (m *Metadata) String() string {
|
func (m *Metadata) String() string {
|
||||||
if m.Host != "" {
|
if m.Host != "" {
|
||||||
return m.Host
|
return m.Host
|
||||||
} else if m.DstIP != nil {
|
} else if m.DstIP.IsValid() {
|
||||||
return m.DstIP.String()
|
return m.DstIP.String()
|
||||||
} else {
|
} else {
|
||||||
return "<nil>"
|
return "<nil>"
|
||||||
@ -141,5 +142,5 @@ func (m *Metadata) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) Valid() bool {
|
func (m *Metadata) Valid() bool {
|
||||||
return m.Host != "" || m.DstIP != nil
|
return m.Host != "" || m.DstIP.IsValid()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/geodata/router"
|
"github.com/Dreamacro/clash/component/geodata/router"
|
||||||
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
type RuleExtra struct {
|
type RuleExtra struct {
|
||||||
Network NetWork
|
Network NetWork
|
||||||
SourceIPs []*net.IPNet
|
SourceIPs []*netip.Prefix
|
||||||
ProcessNames []string
|
ProcessNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ func (re *RuleExtra) NotMatchNetwork(network NetWork) bool {
|
|||||||
return re.Network != ALLNet && re.Network != network
|
return re.Network != ALLNet && re.Network != network
|
||||||
}
|
}
|
||||||
|
|
||||||
func (re *RuleExtra) NotMatchSourceIP(srcIP net.IP) bool {
|
func (re *RuleExtra) NotMatchSourceIP(srcIP netip.Addr) bool {
|
||||||
if re.SourceIPs == nil {
|
if re.SourceIPs == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
@ -28,10 +29,10 @@ func (c *client) Exchange(m *D.Msg) (*D.Msg, error) {
|
|||||||
|
|
||||||
func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
||||||
var (
|
var (
|
||||||
ip net.IP
|
ip netip.Addr
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if ip = net.ParseIP(c.host); ip == nil {
|
if ip, err = netip.ParseAddr(c.host); err != nil {
|
||||||
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 {
|
||||||
@ -62,7 +63,9 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer func() {
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
// miekg/dns ExchangeContext doesn't respond to context cancel.
|
// miekg/dns ExchangeContext doesn't respond to context cancel.
|
||||||
// this is a workaround
|
// this is a workaround
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ type dhcpClient struct {
|
|||||||
ifaceInvalidate time.Time
|
ifaceInvalidate time.Time
|
||||||
dnsInvalidate time.Time
|
dnsInvalidate time.Time
|
||||||
|
|
||||||
ifaceAddr *net.IPNet
|
ifaceAddr *netip.Prefix
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
resolver *Resolver
|
resolver *Resolver
|
||||||
err error
|
err error
|
||||||
@ -127,12 +127,12 @@ func (d *dhcpClient) invalidate() (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := ifaceObj.PickIPv4Addr(nil)
|
addr, err := ifaceObj.PickIPv4Addr(netip.Addr{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().Before(d.dnsInvalidate) && d.ifaceAddr.IP.Equal(addr.IP) && bytes.Equal(d.ifaceAddr.Mask, addr.Mask) {
|
if time.Now().Before(d.dnsInvalidate) && d.ifaceAddr == addr {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
@ -23,54 +22,51 @@ func (h *ResolverEnhancer) MappingEnabled() bool {
|
|||||||
return h.mode == C.DNSFakeIP || h.mode == C.DNSMapping
|
return h.mode == C.DNSFakeIP || h.mode == C.DNSMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ResolverEnhancer) IsExistFakeIP(ip net.IP) bool {
|
func (h *ResolverEnhancer) IsExistFakeIP(ip netip.Addr) bool {
|
||||||
if !h.FakeIPEnabled() {
|
if !h.FakeIPEnabled() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if pool := h.fakePool; pool != nil {
|
if pool := h.fakePool; pool != nil {
|
||||||
return pool.Exist(ipToAddr(ip))
|
return pool.Exist(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ResolverEnhancer) IsFakeIP(ip net.IP) bool {
|
func (h *ResolverEnhancer) IsFakeIP(ip netip.Addr) bool {
|
||||||
if !h.FakeIPEnabled() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := ipToAddr(ip)
|
|
||||||
|
|
||||||
if pool := h.fakePool; pool != nil {
|
|
||||||
return pool.IPNet().Contains(addr) && addr != pool.Gateway() && addr != pool.Broadcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ResolverEnhancer) IsFakeBroadcastIP(ip net.IP) bool {
|
|
||||||
if !h.FakeIPEnabled() {
|
if !h.FakeIPEnabled() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if pool := h.fakePool; pool != nil {
|
if pool := h.fakePool; pool != nil {
|
||||||
return pool.Broadcast() == ipToAddr(ip)
|
return pool.IPNet().Contains(ip) && ip != pool.Gateway() && ip != pool.Broadcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
|
func (h *ResolverEnhancer) IsFakeBroadcastIP(ip netip.Addr) bool {
|
||||||
addr := ipToAddr(ip)
|
if !h.FakeIPEnabled() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if pool := h.fakePool; pool != nil {
|
if pool := h.fakePool; pool != nil {
|
||||||
if host, existed := pool.LookBack(addr); existed {
|
return pool.Broadcast() == ip
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ResolverEnhancer) FindHostByIP(ip netip.Addr) (string, bool) {
|
||||||
|
if pool := h.fakePool; pool != nil {
|
||||||
|
if host, existed := pool.LookBack(ip); existed {
|
||||||
return host, true
|
return host, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mapping := h.mapping; mapping != nil {
|
if mapping := h.mapping; mapping != nil {
|
||||||
if host, existed := h.mapping.Get(addr); existed {
|
if host, existed := h.mapping.Get(ip); existed {
|
||||||
return host, true
|
return host, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,9 +74,9 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ResolverEnhancer) InsertHostByIP(ip net.IP, host string) {
|
func (h *ResolverEnhancer) InsertHostByIP(ip netip.Addr, host string) {
|
||||||
if mapping := h.mapping; mapping != nil {
|
if mapping := h.mapping; mapping != nil {
|
||||||
h.mapping.Set(ipToAddr(ip), host)
|
h.mapping.Set(ip, host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +97,7 @@ func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ResolverEnhancer) StoreFakePoolSate() {
|
func (h *ResolverEnhancer) StoreFakePoolState() {
|
||||||
if h.fakePool != nil {
|
if h.fakePool != nil {
|
||||||
h.fakePool.StoreState()
|
h.fakePool.StoreState()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/geodata/router"
|
"github.com/Dreamacro/clash/component/geodata/router"
|
||||||
@ -10,23 +10,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type fallbackIPFilter interface {
|
type fallbackIPFilter interface {
|
||||||
Match(net.IP) bool
|
Match(netip.Addr) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type geoipFilter struct {
|
type geoipFilter struct {
|
||||||
code string
|
code string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gf *geoipFilter) Match(ip net.IP) bool {
|
func (gf *geoipFilter) Match(ip netip.Addr) bool {
|
||||||
record, _ := mmdb.Instance().Country(ip)
|
record, _ := mmdb.Instance().Country(ip.AsSlice())
|
||||||
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate()
|
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipnetFilter struct {
|
type ipnetFilter struct {
|
||||||
ipnet *net.IPNet
|
ipnet *netip.Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inf *ipnetFilter) Match(ip net.IP) bool {
|
func (inf *ipnetFilter) Match(ip netip.Addr) bool {
|
||||||
return inf.ipnet.Contains(ip)
|
return inf.ipnet.Contains(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ type domainFilter struct {
|
|||||||
func NewDomainFilter(domains []string) *domainFilter {
|
func NewDomainFilter(domains []string) *domainFilter {
|
||||||
df := domainFilter{tree: trie.New[bool]()}
|
df := domainFilter{tree: trie.New[bool]()}
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
df.tree.Insert(domain, true)
|
_ = df.tree.Insert(domain, true)
|
||||||
}
|
}
|
||||||
return &df
|
return &df
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/component/fakeip"
|
"github.com/Dreamacro/clash/component/fakeip"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -87,21 +87,21 @@ func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware {
|
|||||||
host := strings.TrimRight(q.Name, ".")
|
host := strings.TrimRight(q.Name, ".")
|
||||||
|
|
||||||
for _, ans := range msg.Answer {
|
for _, ans := range msg.Answer {
|
||||||
var ip net.IP
|
var ip netip.Addr
|
||||||
var ttl uint32
|
var ttl uint32
|
||||||
|
|
||||||
switch a := ans.(type) {
|
switch a := ans.(type) {
|
||||||
case *D.A:
|
case *D.A:
|
||||||
ip = a.A
|
ip = nnip.IpToAddr(a.A)
|
||||||
ttl = a.Hdr.Ttl
|
ttl = a.Hdr.Ttl
|
||||||
case *D.AAAA:
|
case *D.AAAA:
|
||||||
ip = a.AAAA
|
ip = nnip.IpToAddr(a.AAAA)
|
||||||
ttl = a.Hdr.Ttl
|
ttl = a.Hdr.Ttl
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping.SetWithExpire(ipToAddr(ip), host, time.Now().Add(time.Second*time.Duration(ttl)))
|
mapping.SetWithExpire(ip, host, time.Now().Add(time.Second*time.Duration(ttl)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -46,8 +45,8 @@ 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 net.IP, err error) {
|
func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
|
||||||
ch := make(chan net.IP, 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)
|
||||||
@ -64,23 +63,23 @@ func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
|
|||||||
|
|
||||||
ip, open := <-ch
|
ip, open := <-ch
|
||||||
if !open {
|
if !open {
|
||||||
return nil, resolver.ErrIPNotFound
|
return netip.Addr{}, resolver.ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 request with TypeA
|
// ResolveIPv4 request with TypeA
|
||||||
func (r *Resolver) ResolveIPv4(host string) (ip net.IP, err error) {
|
func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeA)
|
return r.resolveIP(host, D.TypeA)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6 request with TypeAAAA
|
// ResolveIPv6 request with TypeAAAA
|
||||||
func (r *Resolver) ResolveIPv6(host string) (ip net.IP, err error) {
|
func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeAAAA)
|
return r.resolveIP(host, D.TypeAAAA)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) shouldIPFallback(ip net.IP) bool {
|
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
||||||
for _, filter := range r.fallbackIPFilters {
|
for _, filter := range r.fallbackIPFilters {
|
||||||
if filter.Match(ip) {
|
if filter.Match(ip) {
|
||||||
return true
|
return true
|
||||||
@ -101,10 +100,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
q := m.Question[0]
|
q := m.Question[0]
|
||||||
cache, expireTime, hit := r.lruCache.GetWithExpire(q.String())
|
cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String())
|
||||||
if hit {
|
if hit {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
msg = cache.Copy()
|
msg = cacheM.Copy()
|
||||||
if expireTime.Before(now) {
|
if expireTime.Before(now) {
|
||||||
setMsgTTL(msg, uint32(1)) // Continue fetch
|
setMsgTTL(msg, uint32(1)) // Continue fetch
|
||||||
go r.exchangeWithoutCache(ctx, m)
|
go r.exchangeWithoutCache(ctx, m)
|
||||||
@ -256,16 +255,16 @@ 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 net.IP, err error) {
|
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err error) {
|
||||||
ip = net.ParseIP(host)
|
ip, err = netip.ParseAddr(host)
|
||||||
if ip != nil {
|
if err == nil {
|
||||||
isIPv4 := ip.To4() != nil
|
isIPv4 := ip.Is4()
|
||||||
if dnsType == D.TypeAAAA && !isIPv4 {
|
if dnsType == D.TypeAAAA && !isIPv4 {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
} else if dnsType == D.TypeA && isIPv4 {
|
} else if dnsType == D.TypeA && isIPv4 {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, resolver.ErrIPVersion
|
return netip.Addr{}, resolver.ErrIPVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,13 +273,13 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error)
|
|||||||
|
|
||||||
msg, err := r.Exchange(query)
|
msg, err := r.Exchange(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.Addr{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ips := msgToIP(msg)
|
ips := msgToIP(msg)
|
||||||
ipLength := len(ips)
|
ipLength := len(ips)
|
||||||
if ipLength == 0 {
|
if ipLength == 0 {
|
||||||
return nil, resolver.ErrIPNotFound
|
return netip.Addr{}, resolver.ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ips[rand.Intn(ipLength)]
|
ip = ips[rand.Intn(ipLength)]
|
||||||
@ -319,7 +318,7 @@ type NameServer struct {
|
|||||||
type FallbackFilter struct {
|
type FallbackFilter struct {
|
||||||
GeoIP bool
|
GeoIP bool
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
IPCIDR []*net.IPNet
|
IPCIDR []*netip.Prefix
|
||||||
Domain []string
|
Domain []string
|
||||||
GeoSite []*router.DomainMatcher
|
GeoSite []*router.DomainMatcher
|
||||||
}
|
}
|
||||||
@ -360,7 +359,7 @@ func NewResolver(config Config) *Resolver {
|
|||||||
if len(config.Policy) != 0 {
|
if len(config.Policy) != 0 {
|
||||||
r.policy = trie.New[*Policy]()
|
r.policy = trie.New[*Policy]()
|
||||||
for domain, nameserver := range config.Policy {
|
for domain, nameserver := range config.Policy {
|
||||||
r.policy.Insert(domain, NewPolicy(transform([]NameServer{nameserver}, defaultResolver)))
|
_ = r.policy.Insert(domain, NewPolicy(transform([]NameServer{nameserver}, defaultResolver)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
dns/util.go
29
dns/util.go
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
@ -97,37 +98,21 @@ func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgToIP(msg *D.Msg) []net.IP {
|
func msgToIP(msg *D.Msg) []netip.Addr {
|
||||||
ips := []net.IP{}
|
ips := []netip.Addr{}
|
||||||
|
|
||||||
for _, answer := range msg.Answer {
|
for _, answer := range msg.Answer {
|
||||||
switch ans := answer.(type) {
|
switch ans := answer.(type) {
|
||||||
case *D.AAAA:
|
case *D.AAAA:
|
||||||
ips = append(ips, ans.AAAA)
|
ips = append(ips, nnip.IpToAddr(ans.AAAA))
|
||||||
case *D.A:
|
case *D.A:
|
||||||
ips = append(ips, ans.A)
|
ips = append(ips, nnip.IpToAddr(ans.A))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipToAddr(ip net.IP) netip.Addr {
|
|
||||||
if ip == nil {
|
|
||||||
return netip.Addr{}
|
|
||||||
}
|
|
||||||
|
|
||||||
l := len(ip)
|
|
||||||
|
|
||||||
if l == 4 {
|
|
||||||
return netip.AddrFrom4(*(*[4]byte)(ip))
|
|
||||||
} else if l == 16 {
|
|
||||||
return netip.AddrFrom16(*(*[16]byte)(ip))
|
|
||||||
} else {
|
|
||||||
return netip.Addr{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type wrapPacketConn struct {
|
type wrapPacketConn struct {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
rAddr net.Addr
|
rAddr net.Addr
|
||||||
@ -146,7 +131,7 @@ func (wpc *wrapPacketConn) RemoteAddr() net.Addr {
|
|||||||
return wpc.rAddr
|
return wpc.rAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialContextWithProxyAdapter(ctx context.Context, adapterName string, network string, dstIP net.IP, port string, opts ...dialer.Option) (net.Conn, error) {
|
func dialContextWithProxyAdapter(ctx context.Context, adapterName string, network string, dstIP netip.Addr, port string, opts ...dialer.Option) (net.Conn, error) {
|
||||||
adapter, ok := tunnel.Proxies()[adapterName]
|
adapter, ok := tunnel.Proxies()[adapterName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("proxy adapter [%s] not found", adapterName)
|
return nil, fmt.Errorf("proxy adapter [%s] not found", adapterName)
|
||||||
@ -161,7 +146,7 @@ func dialContextWithProxyAdapter(ctx context.Context, adapterName string, networ
|
|||||||
}
|
}
|
||||||
|
|
||||||
addrType := C.AtypIPv4
|
addrType := C.AtypIPv4
|
||||||
if dstIP.To4() == nil {
|
if dstIP.Is6() {
|
||||||
addrType = C.AtypIPv6
|
addrType = C.AtypIPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
go.mod
6
go.mod
@ -19,16 +19,16 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
go.uber.org/automaxprocs v1.5.1
|
go.uber.org/automaxprocs v1.5.1
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||||
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-20220411224347-583f2d630306
|
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6
|
golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469
|
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
12
go.sum
12
go.sum
@ -97,8 +97,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
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-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
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=
|
||||||
@ -122,8 +122,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-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||||
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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=
|
||||||
@ -159,5 +159,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b h1:JW1pUBe6A3H+b0B9DwEOcfK+TLS/04A3A9cettPpfV0=
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b h1:zBJp2eKSoNIV6+9LO3bRhlnuK280Oyrwc6OeFIN6VzU=
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
|
||||||
|
@ -354,7 +354,7 @@ func Shutdown() {
|
|||||||
P.Cleanup()
|
P.Cleanup()
|
||||||
S.Py_Finalize()
|
S.Py_Finalize()
|
||||||
tproxy.CleanupTProxyIPTables()
|
tproxy.CleanupTProxyIPTables()
|
||||||
resolver.StoreFakePoolSate()
|
resolver.StoreFakePoolState()
|
||||||
|
|
||||||
log.Warnln("Clash shutting down")
|
log.Warnln("Clash shutting down")
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
@ -362,7 +363,6 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
|
|||||||
if mitmListener.RawAddress() == addr {
|
if mitmListener.RawAddress() == addr {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
outbound.MiddlemanServerAddress.Store("")
|
|
||||||
tunnel.MitmOutbound = nil
|
tunnel.MitmOutbound = nil
|
||||||
_ = mitmListener.Close()
|
_ = mitmListener.Close()
|
||||||
mitmListener = nil
|
mitmListener = nil
|
||||||
@ -403,7 +403,7 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
certOption.SetValidity(cert.TTL << 3)
|
certOption.SetValidity(time.Hour * 24 * 90)
|
||||||
certOption.SetOrganization("Clash ManInTheMiddle Proxy Services")
|
certOption.SetOrganization("Clash ManInTheMiddle Proxy Services")
|
||||||
|
|
||||||
opt := &mitm.Option{
|
opt := &mitm.Option{
|
||||||
@ -418,8 +418,7 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound.MiddlemanServerAddress.Store(mitmListener.Address())
|
tunnel.MitmOutbound = outbound.NewMitm(mitmListener.Address())
|
||||||
tunnel.MitmOutbound = outbound.NewMitm()
|
|
||||||
|
|
||||||
log.Infoln("Mitm proxy listening at: %s", mitmListener.Address())
|
log.Infoln("Mitm proxy listening at: %s", mitmListener.Address())
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import (
|
|||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrCertUnsupported = errors.New("tls: client cert unsupported")
|
|
||||||
|
|
||||||
func newClient(source net.Addr, userAgent string, in chan<- C.ConnContext) *http.Client {
|
func newClient(source net.Addr, userAgent string, in chan<- C.ConnContext) *http.Client {
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
|
@ -9,11 +9,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -33,8 +32,8 @@ func HandleConn(c net.Conn, opt *Option, in chan<- C.ConnContext, cache *cache.C
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
startOver:
|
startOver:
|
||||||
if tc, ok := c.(*net.TCPConn); ok {
|
if tcpConn, ok := c.(*net.TCPConn); ok {
|
||||||
_ = tc.SetKeepAlive(true)
|
_ = tcpConn.SetKeepAlive(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var conn *N.BufferedConn
|
var conn *N.BufferedConn
|
||||||
@ -48,14 +47,13 @@ startOver:
|
|||||||
|
|
||||||
readLoop:
|
readLoop:
|
||||||
for {
|
for {
|
||||||
err := conn.SetDeadline(time.Now().Add(30 * time.Second)) // use SetDeadline instead of Proxy-Connection keep-alive
|
// use SetDeadline instead of Proxy-Connection keep-alive
|
||||||
if err != nil {
|
if err := conn.SetDeadline(time.Now().Add(30 * time.Second)); err != nil {
|
||||||
break readLoop
|
break readLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
request, err := H.ReadRequest(conn.Reader())
|
request, err := H.ReadRequest(conn.Reader())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(opt, nil, err)
|
|
||||||
break readLoop
|
break readLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +61,7 @@ readLoop:
|
|||||||
|
|
||||||
session := newSession(conn, request, response)
|
session := newSession(conn, request, response)
|
||||||
|
|
||||||
source = parseSourceAddress(session.request, c, source)
|
source = parseSourceAddress(session.request, c.RemoteAddr(), source)
|
||||||
session.request.RemoteAddr = source.String()
|
session.request.RemoteAddr = source.String()
|
||||||
|
|
||||||
if !trusted {
|
if !trusted {
|
||||||
@ -80,48 +78,42 @@ readLoop:
|
|||||||
break readLoop // close connection
|
break readLoop // close connection
|
||||||
}
|
}
|
||||||
|
|
||||||
if couldBeWithManInTheMiddleAttack(session.request.URL.Host, opt) {
|
if strings.HasSuffix(session.request.URL.Host, ":80") {
|
||||||
b := make([]byte, 1)
|
goto readLoop
|
||||||
if _, err = session.conn.Read(b); err != nil {
|
}
|
||||||
handleError(opt, session, err)
|
|
||||||
|
b, err := conn.Peek(1)
|
||||||
|
if err != nil {
|
||||||
|
handleError(opt, session, err)
|
||||||
|
break readLoop // close connection
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS handshake.
|
||||||
|
if b[0] == 0x16 {
|
||||||
|
tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
|
||||||
|
|
||||||
|
// Handshake with the local client
|
||||||
|
if err = tlsConn.Handshake(); err != nil {
|
||||||
|
session.response = session.NewErrorResponse(fmt.Errorf("handshake failed: %w", err))
|
||||||
|
_ = writeResponse(session, false)
|
||||||
break readLoop // close connection
|
break readLoop // close connection
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := make([]byte, session.conn.(*N.BufferedConn).Buffered())
|
c = tlsConn
|
||||||
_, _ = session.conn.Read(buff)
|
} else {
|
||||||
|
c = conn
|
||||||
mrc := &multiReaderConn{
|
|
||||||
Conn: session.conn,
|
|
||||||
reader: io.MultiReader(bytes.NewReader(b), bytes.NewReader(buff), session.conn),
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS handshake.
|
|
||||||
if b[0] == 0x16 {
|
|
||||||
// TODO serve by generic host name maybe better?
|
|
||||||
tlsConn := tls.Server(mrc, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
|
|
||||||
|
|
||||||
// Handshake with the local client
|
|
||||||
if err = tlsConn.Handshake(); err != nil {
|
|
||||||
handleError(opt, session, err)
|
|
||||||
break readLoop // close connection
|
|
||||||
}
|
|
||||||
|
|
||||||
c = tlsConn
|
|
||||||
goto startOver // hijack and decrypt tls connection
|
|
||||||
}
|
|
||||||
|
|
||||||
// maybe it's the others encrypted connection
|
|
||||||
in <- inbound.NewHTTPS(session.request, mrc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe it's a http connection
|
goto startOver
|
||||||
goto readLoop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareRequest(c, session.request)
|
prepareRequest(c, session.request)
|
||||||
|
|
||||||
|
H.RemoveHopByHopHeaders(session.request.Header)
|
||||||
|
H.RemoveExtraHTTPHostPort(session.request)
|
||||||
|
|
||||||
// hijack api
|
// hijack api
|
||||||
if session.request.URL.Host == opt.ApiHost {
|
if session.request.URL.Hostname() == opt.ApiHost {
|
||||||
if err = handleApiRequest(session, opt); err != nil {
|
if err = handleApiRequest(session, opt); err != nil {
|
||||||
handleError(opt, session, err)
|
handleError(opt, session, err)
|
||||||
break readLoop
|
break readLoop
|
||||||
@ -149,7 +141,7 @@ readLoop:
|
|||||||
session.request.RequestURI = ""
|
session.request.RequestURI = ""
|
||||||
|
|
||||||
if session.request.URL.Host == "" {
|
if session.request.URL.Host == "" {
|
||||||
session.response = session.NewErrorResponse(errors.New("invalid URL"))
|
session.response = session.NewErrorResponse(ErrInvalidURL)
|
||||||
} else {
|
} else {
|
||||||
client = newClientBySourceAndUserAgentIfNil(client, session.request, source, in)
|
client = newClientBySourceAndUserAgentIfNil(client, session.request, source, in)
|
||||||
|
|
||||||
@ -160,7 +152,6 @@ readLoop:
|
|||||||
handleError(opt, session, err)
|
handleError(opt, session, err)
|
||||||
session.response = session.NewErrorResponse(err)
|
session.response = session.NewErrorResponse(err)
|
||||||
if errors.Is(err, ErrCertUnsupported) || strings.Contains(err.Error(), "x509: ") {
|
if errors.Is(err, ErrCertUnsupported) || strings.Contains(err.Error(), "x509: ") {
|
||||||
// TODO block unsupported host?
|
|
||||||
_ = writeResponse(session, false)
|
_ = writeResponse(session, false)
|
||||||
break readLoop
|
break readLoop
|
||||||
}
|
}
|
||||||
@ -202,9 +193,7 @@ func writeResponse(session *Session, keepAlive bool) error {
|
|||||||
session.response.Header.Set("Keep-Alive", "timeout=25")
|
session.response.Header.Set("Keep-Alive", "timeout=25")
|
||||||
}
|
}
|
||||||
|
|
||||||
// session.response.Close = !keepAlive // let handler do it
|
return session.writeResponse()
|
||||||
|
|
||||||
return session.response.Write(session.conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleApiRequest(session *Session, opt *Option) error {
|
func handleApiRequest(session *Session, opt *Option) error {
|
||||||
@ -224,7 +213,7 @@ func handleApiRequest(session *Session, opt *Option) error {
|
|||||||
session.response.Header.Set("Content-Type", "application/x-x509-ca-cert")
|
session.response.Header.Set("Content-Type", "application/x-x509-ca-cert")
|
||||||
session.response.ContentLength = int64(len(b))
|
session.response.ContentLength = int64(len(b))
|
||||||
|
|
||||||
return session.response.Write(session.conn)
|
return session.writeResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
b := `<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
b := `<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
||||||
@ -254,7 +243,7 @@ func handleApiRequest(session *Session, opt *Option) error {
|
|||||||
session.response.Header.Set("Content-Type", "text/html;charset=utf-8")
|
session.response.Header.Set("Content-Type", "text/html;charset=utf-8")
|
||||||
session.response.ContentLength = int64(len(b))
|
session.response.ContentLength = int64(len(b))
|
||||||
|
|
||||||
return session.response.Write(session.conn)
|
return session.writeResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleError(opt *Option, session *Session, err error) {
|
func handleError(opt *Option, session *Session, err error) {
|
||||||
@ -287,53 +276,29 @@ func prepareRequest(conn net.Conn, request *http.Request) {
|
|||||||
if request.Header.Get("Accept-Encoding") != "" {
|
if request.Header.Get("Accept-Encoding") != "" {
|
||||||
request.Header.Set("Accept-Encoding", "gzip")
|
request.Header.Set("Accept-Encoding", "gzip")
|
||||||
}
|
}
|
||||||
|
|
||||||
H.RemoveHopByHopHeaders(request.Header)
|
|
||||||
H.RemoveExtraHTTPHostPort(request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeWithManInTheMiddleAttack(hostname string, opt *Option) bool {
|
func parseSourceAddress(req *http.Request, connSource, source net.Addr) net.Addr {
|
||||||
if opt.CertConfig == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, port, err := net.SplitHostPort(hostname); err == nil && (port == "443" || port == "8443") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSourceAddress(req *http.Request, c net.Conn, source net.Addr) net.Addr {
|
|
||||||
if source != nil {
|
if source != nil {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceAddress := req.Header.Get("Origin-Request-Source-Address")
|
sourceAddress := req.Header.Get("Origin-Request-Source-Address")
|
||||||
if sourceAddress == "" {
|
if sourceAddress == "" {
|
||||||
return c.RemoteAddr()
|
return connSource
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Del("Origin-Request-Source-Address")
|
req.Header.Del("Origin-Request-Source-Address")
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(sourceAddress)
|
addrPort, err := netip.ParseAddrPort(sourceAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.RemoteAddr()
|
return connSource
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := strconv.ParseUint(port, 10, 16)
|
return &net.TCPAddr{
|
||||||
if err != nil {
|
IP: addrPort.Addr().AsSlice(),
|
||||||
return c.RemoteAddr()
|
Port: int(addrPort.Port()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
return &net.TCPAddr{
|
|
||||||
IP: ip,
|
|
||||||
Port: int(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.RemoteAddr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClientBySourceAndUserAgentIfNil(cli *http.Client, req *http.Request, source net.Addr, in chan<- C.ConnContext) *http.Client {
|
func newClientBySourceAndUserAgentIfNil(cli *http.Client, req *http.Request, source net.Addr, in chan<- C.ConnContext) *http.Client {
|
||||||
|
@ -39,6 +39,13 @@ func (s *Session) NewErrorResponse(err error) *http.Response {
|
|||||||
return NewErrorResponse(s.request, err)
|
return NewErrorResponse(s.request, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) writeResponse() error {
|
||||||
|
if s.response == nil {
|
||||||
|
return ErrInvalidResponse
|
||||||
|
}
|
||||||
|
return s.response.Write(s.conn)
|
||||||
|
}
|
||||||
|
|
||||||
func newSession(conn net.Conn, request *http.Request, response *http.Response) *Session {
|
func newSession(conn net.Conn, request *http.Request, response *http.Response) *Session {
|
||||||
return &Session{
|
return &Session{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
|
@ -3,10 +3,10 @@ package mitm
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -14,14 +14,11 @@ import (
|
|||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
type multiReaderConn struct {
|
var (
|
||||||
net.Conn
|
ErrCertUnsupported = errors.New("tls: client cert unsupported")
|
||||||
reader io.Reader
|
ErrInvalidResponse = errors.New("invalid response")
|
||||||
}
|
ErrInvalidURL = errors.New("invalid URL")
|
||||||
|
)
|
||||||
func (c *multiReaderConn) Read(buf []byte) (int, error) {
|
|
||||||
return c.reader.Read(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewResponse(code int, body io.Reader, req *http.Request) *http.Response {
|
func NewResponse(code int, body io.Reader, req *http.Request) *http.Response {
|
||||||
if body == nil {
|
if body == nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/tun"
|
"github.com/Dreamacro/clash/listener/tun/device/tun"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
@ -226,7 +227,7 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
|
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
|
||||||
if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] {
|
if ip := nnip.IpToAddr(address.Address.IP()); addrHash[ip] {
|
||||||
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
|
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
|
||||||
log.Infoln("[TUN] cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName())
|
log.Infoln("[TUN] cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName())
|
||||||
_ = iface.LUID.DeleteIPAddress(prefix)
|
_ = iface.LUID.DeleteIPAddress(prefix)
|
||||||
@ -260,7 +261,7 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
|
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
|
||||||
nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP())
|
nextHop := nnip.IpToAddr(gatewayAddress.Address.IP())
|
||||||
|
|
||||||
if _, err = iface.LUID.Route(destination, nextHop); err == nil {
|
if _, err = iface.LUID.Route(destination, nextHop); err == nil {
|
||||||
return ifname, nil
|
return ifname, nil
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||||
@ -33,20 +34,22 @@ func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
|
|||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
||||||
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
|
|
||||||
|
|
||||||
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
log.Debugln("[TUN] hijack dns tcp: %s", addrPort.String())
|
log.Debugln("[TUN] hijack dns tcp: %s", addrPort.String())
|
||||||
|
|
||||||
defer tunConn.Close()
|
|
||||||
|
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer pool.Put(buf)
|
defer func() {
|
||||||
|
_ = pool.Put(buf)
|
||||||
|
_ = tunConn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout))
|
if tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout)) != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
length := uint16(0)
|
length := uint16(0)
|
||||||
if err := binary.Read(tunConn, binary.BigEndian, &length); err != nil {
|
if err := binary.Read(tunConn, binary.BigEndian, &length); err != nil {
|
||||||
@ -86,8 +89,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
||||||
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
|
|
||||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
target := socks5.ParseAddrToSocksAddr(rAddr)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -96,7 +98,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
|
|
||||||
n, addr, err := tunConn.ReadFrom(buf)
|
n, addr, err := tunConn.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +106,9 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
|
|
||||||
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
defer pool.Put(buf)
|
defer func() {
|
||||||
|
_ = pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
msg, err1 := D.RelayDnsPacket(payload)
|
msg, err1 := D.RelayDnsPacket(payload)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
|
@ -13,8 +13,8 @@ type StackListener struct {
|
|||||||
udp *nat.UDP
|
udp *nat.UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartListener(device io.ReadWriteCloser, gateway netip.Addr, portal netip.Addr) (*StackListener, error) {
|
func StartListener(device io.ReadWriteCloser, gateway, portal, broadcast netip.Addr) (*StackListener, error) {
|
||||||
tcp, udp, err := nat.Start(device, gateway, portal)
|
tcp, udp, err := nat.Start(device, gateway, portal, broadcast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start(
|
func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *UDP, error) {
|
||||||
device io.ReadWriter,
|
|
||||||
gateway netip.Addr,
|
|
||||||
portal netip.Addr,
|
|
||||||
) (*TCP, *UDP, error) {
|
|
||||||
if !portal.Is4() || !gateway.Is4() {
|
if !portal.Is4() || !gateway.Is4() {
|
||||||
return nil, nil, net.InvalidAddrError("only ipv4 supported")
|
return nil, nil, net.InvalidAddrError("only ipv4 supported")
|
||||||
}
|
}
|
||||||
@ -37,8 +33,10 @@ func Start(
|
|||||||
gatewayPort := uint16(listener.Addr().(*net.TCPAddr).Port)
|
gatewayPort := uint16(listener.Addr().(*net.TCPAddr).Port)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer tcp.Close()
|
defer func() {
|
||||||
defer udp.Close()
|
_ = tcp.Close()
|
||||||
|
_ = udp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
buf := make([]byte, pool.RelayBufferSize)
|
buf := make([]byte, pool.RelayBufferSize)
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ func Start(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipv4.Offset() != 0 {
|
if ipv4.FragmentOffset() != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +90,12 @@ func Start(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destinationIP := ip.DestinationIP()
|
||||||
|
|
||||||
|
if !destinationIP.IsGlobalUnicast() || destinationIP == broadcast {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch ip.Protocol() {
|
switch ip.Protocol() {
|
||||||
case tcpip.TCP:
|
case tcpip.TCP:
|
||||||
t := tcpip.TCPPacket(ip.Payload())
|
t := tcpip.TCPPacket(ip.Payload())
|
||||||
@ -99,7 +103,7 @@ func Start(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip.DestinationIP() == portal {
|
if destinationIP == portal {
|
||||||
if ip.SourceIP() == gateway && t.SourcePort() == gatewayPort {
|
if ip.SourceIP() == gateway && t.SourcePort() == gatewayPort {
|
||||||
tup := tab.tupleOf(t.DestinationPort())
|
tup := tab.tupleOf(t.DestinationPort())
|
||||||
if tup == zeroTuple {
|
if tup == zeroTuple {
|
||||||
@ -120,7 +124,7 @@ func Start(
|
|||||||
} else {
|
} else {
|
||||||
tup := tuple{
|
tup := tuple{
|
||||||
SourceAddr: netip.AddrPortFrom(ip.SourceIP(), t.SourcePort()),
|
SourceAddr: netip.AddrPortFrom(ip.SourceIP(), t.SourcePort()),
|
||||||
DestinationAddr: netip.AddrPortFrom(ip.DestinationIP(), t.DestinationPort()),
|
DestinationAddr: netip.AddrPortFrom(destinationIP, t.DestinationPort()),
|
||||||
}
|
}
|
||||||
|
|
||||||
port := tab.portOf(tup)
|
port := tab.portOf(tup)
|
||||||
@ -158,10 +162,8 @@ func Start(
|
|||||||
|
|
||||||
i.SetType(tcpip.ICMPTypePingResponse)
|
i.SetType(tcpip.ICMPTypePingResponse)
|
||||||
|
|
||||||
source := ip.SourceIP()
|
ip.SetDestinationIP(ip.SourceIP())
|
||||||
destination := ip.DestinationIP()
|
ip.SetSourceIP(destinationIP)
|
||||||
ip.SetSourceIP(destination)
|
|
||||||
ip.SetDestinationIP(source)
|
|
||||||
|
|
||||||
ip.ResetChecksum()
|
ip.ResetChecksum()
|
||||||
i.ResetChecksum()
|
i.ResetChecksum()
|
||||||
@ -176,10 +178,8 @@ func Start(
|
|||||||
|
|
||||||
i.SetType(tcpip.ICMPv6EchoReply)
|
i.SetType(tcpip.ICMPv6EchoReply)
|
||||||
|
|
||||||
source := ip.SourceIP()
|
ip.SetDestinationIP(ip.SourceIP())
|
||||||
destination := ip.DestinationIP()
|
ip.SetSourceIP(destinationIP)
|
||||||
ip.SetSourceIP(destination)
|
|
||||||
ip.SetDestinationIP(source)
|
|
||||||
|
|
||||||
ip.ResetChecksum()
|
ip.ResetChecksum()
|
||||||
i.ResetChecksum(ip.PseudoSum())
|
i.ResetChecksum(ip.PseudoSum())
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
||||||
)
|
)
|
||||||
@ -71,11 +72,8 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
|
|||||||
return 0, net.InvalidAddrError("invalid addr")
|
return 0, net.InvalidAddrError("invalid addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP, _ := netip.AddrFromSlice(srcAddr.IP)
|
srcAddrPort := netip.AddrPortFrom(nnip.IpToAddr(srcAddr.IP), uint16(srcAddr.Port))
|
||||||
dstIp, _ := netip.AddrFromSlice(dstAddr.IP)
|
dstAddrPort := netip.AddrPortFrom(nnip.IpToAddr(dstAddr.IP), uint16(dstAddr.Port))
|
||||||
|
|
||||||
srcAddrPort := netip.AddrPortFrom(srcIP.Unmap(), uint16(srcAddr.Port))
|
|
||||||
dstAddrPort := netip.AddrPortFrom(dstIp.Unmap(), uint16(dstAddr.Port))
|
|
||||||
|
|
||||||
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
|
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
|
||||||
return 0, net.InvalidAddrError("invalid ip version")
|
return 0, net.InvalidAddrError("invalid ip version")
|
||||||
|
@ -118,12 +118,6 @@ func (p IPv4Packet) SetFlags(flags byte) {
|
|||||||
p[6] |= flags << 5
|
p[6] |= flags << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p IPv4Packet) Offset() uint16 {
|
|
||||||
offset := binary.BigEndian.Uint16(p[6:8])
|
|
||||||
|
|
||||||
return (offset & 0x1fff) * 8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p IPv4Packet) SourceIP() netip.Addr {
|
func (p IPv4Packet) SourceIP() netip.Addr {
|
||||||
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
|
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/common/nnip"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/context"
|
"github.com/Dreamacro/clash/context"
|
||||||
@ -45,11 +46,12 @@ func (s *sysStack) Close() error {
|
|||||||
|
|
||||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||||
var (
|
var (
|
||||||
gateway = tunAddress.Masked().Addr().Next()
|
gateway = tunAddress.Masked().Addr().Next()
|
||||||
portal = gateway.Next()
|
portal = gateway.Next()
|
||||||
|
broadcast = nnip.UnMasked(tunAddress)
|
||||||
)
|
)
|
||||||
|
|
||||||
stack, err := mars.StartListener(device, gateway, portal)
|
stack, err := mars.StartListener(device, gateway, portal, broadcast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = device.Close()
|
_ = device.Close()
|
||||||
|
|
||||||
@ -81,24 +83,27 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
lAddr := conn.LocalAddr().(*net.TCPAddr)
|
lAddr := conn.LocalAddr().(*net.TCPAddr)
|
||||||
rAddr := conn.RemoteAddr().(*net.TCPAddr)
|
rAddr := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
|
||||||
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
||||||
|
|
||||||
|
if rAddrPort.Addr().IsLoopback() {
|
||||||
|
_ = conn.Close()
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
||||||
|
|
||||||
defer func(conn net.Conn) {
|
|
||||||
_ = conn.Close()
|
|
||||||
}(conn)
|
|
||||||
|
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer func(buf []byte) {
|
defer func() {
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
}(buf)
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err = conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)); err != nil {
|
if conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)) != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +136,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
metadata := &C.Metadata{
|
metadata := &C.Metadata{
|
||||||
NetWork: C.TCP,
|
NetWork: C.TCP,
|
||||||
Type: C.TUN,
|
Type: C.TUN,
|
||||||
SrcIP: lAddr.IP,
|
SrcIP: lAddrPort.Addr(),
|
||||||
DstIP: rAddr.IP,
|
DstIP: rAddrPort.Addr(),
|
||||||
SrcPort: strconv.Itoa(lAddr.Port),
|
SrcPort: strconv.Itoa(lAddr.Port),
|
||||||
DstPort: strconv.Itoa(rAddr.Port),
|
DstPort: strconv.Itoa(rAddr.Port),
|
||||||
AddrType: C.AtypIPv4,
|
AddrType: C.AtypIPv4,
|
||||||
@ -162,8 +167,13 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
lAddr := lRAddr.(*net.UDPAddr)
|
lAddr := lRAddr.(*net.UDPAddr)
|
||||||
rAddr := rRAddr.(*net.UDPAddr)
|
rAddr := rRAddr.(*net.UDPAddr)
|
||||||
|
|
||||||
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
|
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
||||||
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
|
|
||||||
|
if rAddrPort.Addr().IsLoopback() {
|
||||||
|
_ = pool.Put(buf)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -50,7 +50,7 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
|
|||||||
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
|
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
|
||||||
}
|
}
|
||||||
|
|
||||||
process.AppendLocalIPs(tunAddress.Masked().Addr().Next().AsSlice())
|
process.AppendLocalIPs(tunAddress.Masked().Addr().Next())
|
||||||
|
|
||||||
// open tun device
|
// open tun device
|
||||||
tunDevice, err = parseDevice(devName, uint32(mtu))
|
tunDevice, err = parseDevice(devName, uint32(mtu))
|
||||||
@ -149,7 +149,8 @@ func setAtLatest(stackType C.TUNStack, devName string) {
|
|||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
_, _ = cmd.ExecCmd("sysctl net.inet.ip.forwarding=1")
|
// _, _ = cmd.ExecCmd("sysctl -w net.inet.ip.forwarding=1")
|
||||||
|
// _, _ = cmd.ExecCmd("sysctl -w net.inet6.ip6.forwarding=1")
|
||||||
case "windows":
|
case "windows":
|
||||||
_, _ = cmd.ExecCmd("ipconfig /renew")
|
_, _ = cmd.ExecCmd("ipconfig /renew")
|
||||||
case "linux":
|
case "linux":
|
||||||
|
10
rule/base.go
10
rule/base.go
@ -2,7 +2,7 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -50,17 +50,17 @@ func findNetwork(params []string) C.NetWork {
|
|||||||
return C.ALLNet
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func findSourceIPs(params []string) []*net.IPNet {
|
func findSourceIPs(params []string) []*netip.Prefix {
|
||||||
var ips []*net.IPNet
|
var ips []*netip.Prefix
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
if p == noResolve || len(p) < 7 {
|
if p == noResolve || len(p) < 7 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, ipnet, err := net.ParseCIDR(p)
|
ipnet, err := netip.ParsePrefix(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ips = append(ips, ipnet)
|
ips = append(ips, &ipnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
|
@ -21,7 +21,7 @@ func (g *GEOIP) RuleType() C.RuleType {
|
|||||||
|
|
||||||
func (g *GEOIP) Match(metadata *C.Metadata) bool {
|
func (g *GEOIP) Match(metadata *C.Metadata) bool {
|
||||||
ip := metadata.DstIP
|
ip := metadata.DstIP
|
||||||
if ip == nil {
|
if !ip.IsValid() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool {
|
|||||||
resolver.IsFakeBroadcastIP(ip)
|
resolver.IsFakeBroadcastIP(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
record, _ := mmdb.Instance().Country(ip)
|
record, _ := mmdb.Instance().Country(ip.AsSlice())
|
||||||
return strings.EqualFold(record.Country.IsoCode, g.country)
|
return strings.EqualFold(record.Country.IsoCode, g.country)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
@ -22,7 +22,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
|
|||||||
|
|
||||||
type IPCIDR struct {
|
type IPCIDR struct {
|
||||||
*Base
|
*Base
|
||||||
ipnet *net.IPNet
|
ipnet *netip.Prefix
|
||||||
adapter string
|
adapter string
|
||||||
isSourceIP bool
|
isSourceIP bool
|
||||||
noResolveIP bool
|
noResolveIP bool
|
||||||
@ -40,7 +40,7 @@ func (i *IPCIDR) Match(metadata *C.Metadata) bool {
|
|||||||
if i.isSourceIP {
|
if i.isSourceIP {
|
||||||
ip = metadata.SrcIP
|
ip = metadata.SrcIP
|
||||||
}
|
}
|
||||||
return ip != nil && i.ipnet.Contains(ip)
|
return ip.IsValid() && i.ipnet.Contains(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IPCIDR) Adapter() string {
|
func (i *IPCIDR) Adapter() string {
|
||||||
@ -56,14 +56,14 @@ func (i *IPCIDR) ShouldResolveIP() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
||||||
_, ipnet, err := net.ParseCIDR(s)
|
ipnet, err := netip.ParsePrefix(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errPayload
|
return nil, errPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcidr := &IPCIDR{
|
ipcidr := &IPCIDR{
|
||||||
Base: &Base{},
|
Base: &Base{},
|
||||||
ipnet: ipnet,
|
ipnet: &ipnet,
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ require (
|
|||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/miekg/dns v1.1.48
|
github.com/miekg/dns v1.1.48
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/Dreamacro/clash => ../
|
replace github.com/Dreamacro/clash => ../
|
||||||
@ -42,7 +42,7 @@ require (
|
|||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5 // indirect
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||||
golang.org/x/tools v0.1.9 // indirect
|
golang.org/x/tools v0.1.9 // indirect
|
||||||
@ -56,5 +56,5 @@ require (
|
|||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
gotest.tools/v3 v3.1.0 // indirect
|
gotest.tools/v3 v3.1.0 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b // indirect
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b // indirect
|
||||||
)
|
)
|
||||||
|
12
test/go.sum
12
test/go.sum
@ -1012,8 +1012,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1143,8 +1143,8 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||||
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
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/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@ -1413,8 +1413,8 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
|||||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||||
gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
|
gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
|
||||||
gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
|
gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b h1:JW1pUBe6A3H+b0B9DwEOcfK+TLS/04A3A9cettPpfV0=
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b h1:zBJp2eKSoNIV6+9LO3bRhlnuK280Oyrwc6OeFIN6VzU=
|
||||||
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
|
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/auth"
|
"github.com/Dreamacro/clash/component/auth"
|
||||||
@ -40,6 +41,8 @@ var (
|
|||||||
ErrRequestUnknownCode = errors.New("request failed with unknown code")
|
ErrRequestUnknownCode = errors.New("request failed with unknown code")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var subnet = netip.PrefixFrom(netip.IPv4Unspecified(), 24)
|
||||||
|
|
||||||
func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, err error) {
|
func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, err error) {
|
||||||
var req [8]byte
|
var req [8]byte
|
||||||
if _, err = io.ReadFull(rw, req[:]); err != nil {
|
if _, err = io.ReadFull(rw, req[:]); err != nil {
|
||||||
@ -57,8 +60,8 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dstIP = req[4:8] // [4]byte
|
dstIP = netip.AddrFrom4(*(*[4]byte)(req[4:8])) // [4]byte
|
||||||
dstPort = req[2:4] // [2]byte
|
dstPort = req[2:4] // [2]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -83,7 +86,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
|
|||||||
if host != "" {
|
if host != "" {
|
||||||
addr = net.JoinHostPort(host, port)
|
addr = net.JoinHostPort(host, port)
|
||||||
} else {
|
} else {
|
||||||
addr = net.JoinHostPort(net.IP(dstIP).String(), port)
|
addr = net.JoinHostPort(dstIP.String(), port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SOCKS4 only support USERID auth.
|
// SOCKS4 only support USERID auth.
|
||||||
@ -97,7 +100,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
|
|||||||
var reply [8]byte
|
var reply [8]byte
|
||||||
reply[0] = 0x00 // reply code
|
reply[0] = 0x00 // reply code
|
||||||
reply[1] = code // result code
|
reply[1] = code // result code
|
||||||
copy(reply[4:8], dstIP)
|
copy(reply[4:8], dstIP.AsSlice())
|
||||||
copy(reply[2:4], dstPort)
|
copy(reply[2:4], dstPort)
|
||||||
|
|
||||||
_, wErr := rw.Write(reply[:])
|
_, wErr := rw.Write(reply[:])
|
||||||
@ -118,20 +121,18 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(host)
|
dstIP, err := netip.ParseAddr(host)
|
||||||
if ip == nil /* HOST */ {
|
if err != nil /* HOST */ {
|
||||||
ip = net.IPv4(0, 0, 0, 1).To4()
|
dstIP = netip.AddrFrom4([4]byte{0, 0, 0, 1})
|
||||||
} else if ip.To4() == nil /* IPv6 */ {
|
} else if dstIP.Is6() /* IPv6 */ {
|
||||||
return errIPv6NotSupported
|
return errIPv6NotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
dstIP := ip.To4()
|
|
||||||
|
|
||||||
req := &bytes.Buffer{}
|
req := &bytes.Buffer{}
|
||||||
req.WriteByte(Version)
|
req.WriteByte(Version)
|
||||||
req.WriteByte(command)
|
req.WriteByte(command)
|
||||||
binary.Write(req, binary.BigEndian, uint16(port))
|
_ = binary.Write(req, binary.BigEndian, uint16(port))
|
||||||
req.Write(dstIP)
|
req.Write(dstIP.AsSlice())
|
||||||
req.WriteString(userID)
|
req.WriteString(userID)
|
||||||
req.WriteByte(0) /* NULL */
|
req.WriteByte(0) /* NULL */
|
||||||
|
|
||||||
@ -174,12 +175,7 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
|
|||||||
// Internet Assigned Numbers Authority -- such an address is inadmissible
|
// Internet Assigned Numbers Authority -- such an address is inadmissible
|
||||||
// as a destination IP address and thus should never occur if the client
|
// as a destination IP address and thus should never occur if the client
|
||||||
// can resolve the domain name.)
|
// can resolve the domain name.)
|
||||||
func isReservedIP(ip net.IP) bool {
|
func isReservedIP(ip netip.Addr) bool {
|
||||||
subnet := net.IPNet{
|
|
||||||
IP: net.IPv4zero,
|
|
||||||
Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
|
|
||||||
}
|
|
||||||
|
|
||||||
return !ip.IsUnspecified() && subnet.Contains(ip)
|
return !ip.IsUnspecified() && subnet.Contains(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ type sniffing struct {
|
|||||||
|
|
||||||
metadata *C.Metadata
|
metadata *C.Metadata
|
||||||
totalWrite *atomic.Uint64
|
totalWrite *atomic.Uint64
|
||||||
|
allowBreak bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sniffing) Read(b []byte) (int, error) {
|
func (r *sniffing) Read(b []byte) (int, error) {
|
||||||
@ -30,8 +31,12 @@ func (r *sniffing) Write(b []byte) (int, error) {
|
|||||||
} else {
|
} else {
|
||||||
resolver.InsertHostByIP(r.metadata.DstIP, header.Domain())
|
resolver.InsertHostByIP(r.metadata.DstIP, header.Domain())
|
||||||
log.Warnln("use sni update host: %s ip: %s", header.Domain(), r.metadata.DstIP.String())
|
log.Warnln("use sni update host: %s ip: %s", header.Domain(), r.metadata.DstIP.String())
|
||||||
r.Conn.Close()
|
if r.allowBreak {
|
||||||
return 0, errors.New("sni update, break current link to avoid leaks")
|
_ = r.Conn.Close()
|
||||||
|
return 0, errors.New("sni update, break current link to avoid leaks")
|
||||||
|
} else {
|
||||||
|
r.metadata.Host = header.Domain()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,10 +50,11 @@ func (r *sniffing) Close() error {
|
|||||||
return r.Conn.Close()
|
return r.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSniffing(conn C.Conn, metadata *C.Metadata) C.Conn {
|
func NewSniffing(conn C.Conn, metadata *C.Metadata, rule C.Rule) C.Conn {
|
||||||
return &sniffing{
|
return &sniffing{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
totalWrite: atomic.NewUint64(0),
|
totalWrite: atomic.NewUint64(0),
|
||||||
|
allowBreak: rule != nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
manager.Join(t)
|
manager.Join(t)
|
||||||
return NewSniffing(t, metadata)
|
return NewSniffing(t, metadata, rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
type udpTracker struct {
|
type udpTracker struct {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -132,15 +133,15 @@ func process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func needLookupIP(metadata *C.Metadata) bool {
|
func needLookupIP(metadata *C.Metadata) bool {
|
||||||
return resolver.MappingEnabled() && metadata.Host == "" && metadata.DstIP != nil
|
return resolver.MappingEnabled() && metadata.Host == "" && metadata.DstIP.IsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func preHandleMetadata(metadata *C.Metadata) error {
|
func preHandleMetadata(metadata *C.Metadata) error {
|
||||||
// handle IP string on host
|
// handle IP string on host
|
||||||
if ip := net.ParseIP(metadata.Host); ip != nil {
|
if ip, err := netip.ParseAddr(metadata.Host); err == nil {
|
||||||
metadata.DstIP = ip
|
metadata.DstIP = ip
|
||||||
metadata.Host = ""
|
metadata.Host = ""
|
||||||
if ip.To4() != nil {
|
if ip.Is4() {
|
||||||
metadata.AddrType = C.AtypIPv4
|
metadata.AddrType = C.AtypIPv4
|
||||||
} else {
|
} else {
|
||||||
metadata.AddrType = C.AtypIPv6
|
metadata.AddrType = C.AtypIPv6
|
||||||
@ -155,11 +156,11 @@ func preHandleMetadata(metadata *C.Metadata) error {
|
|||||||
metadata.AddrType = C.AtypDomainName
|
metadata.AddrType = C.AtypDomainName
|
||||||
metadata.DNSMode = C.DNSMapping
|
metadata.DNSMode = C.DNSMapping
|
||||||
if resolver.FakeIPEnabled() {
|
if resolver.FakeIPEnabled() {
|
||||||
metadata.DstIP = nil
|
metadata.DstIP = netip.Addr{}
|
||||||
metadata.DNSMode = C.DNSFakeIP
|
metadata.DNSMode = C.DNSFakeIP
|
||||||
} else if node := resolver.DefaultHosts.Search(host); node != nil {
|
} else if node := resolver.DefaultHosts.Search(host); node != nil {
|
||||||
// redir-host should lookup the hosts
|
// redir-host should lookup the hosts
|
||||||
metadata.DstIP = node.Data.AsSlice()
|
metadata.DstIP = node.Data
|
||||||
}
|
}
|
||||||
} else if resolver.IsFakeIP(metadata.DstIP) {
|
} else if resolver.IsFakeIP(metadata.DstIP) {
|
||||||
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
|
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
|
||||||
@ -308,7 +309,7 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
if MitmOutbound != nil && metadata.Type != C.MITM {
|
if MitmOutbound != nil && metadata.Type != C.MITM {
|
||||||
if remoteConn, err1 := MitmOutbound.DialContext(ctx, metadata); err1 == nil {
|
if remoteConn, err1 := MitmOutbound.DialContext(ctx, metadata); err1 == nil {
|
||||||
remoteConn = statistic.NewSniffing(remoteConn, metadata)
|
remoteConn = statistic.NewSniffing(remoteConn, metadata, nil)
|
||||||
defer func(remoteConn C.Conn) {
|
defer func(remoteConn C.Conn) {
|
||||||
_ = remoteConn.Close()
|
_ = remoteConn.Close()
|
||||||
}(remoteConn)
|
}(remoteConn)
|
||||||
@ -355,7 +356,7 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
|
func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
|
||||||
return rule.ShouldResolveIP() && metadata.Host != "" && metadata.DstIP == nil
|
return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
||||||
@ -365,7 +366,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
|||||||
var resolved bool
|
var resolved bool
|
||||||
|
|
||||||
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
||||||
metadata.DstIP = node.Data.AsSlice()
|
metadata.DstIP = node.Data
|
||||||
resolved = true
|
resolved = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +420,7 @@ func matchScript(metadata *C.Metadata) (C.Proxy, error) {
|
|||||||
defer configMux.RUnlock()
|
defer configMux.RUnlock()
|
||||||
|
|
||||||
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
|
||||||
metadata.DstIP = node.Data.AsSlice()
|
metadata.DstIP = node.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter, err := S.CallPyMainFunction(metadata)
|
adapter, err := S.CallPyMainFunction(metadata)
|
||||||
|
Reference in New Issue
Block a user