Chore: merge branch 'with-tun' into plus-pro

This commit is contained in:
yaling888 2022-04-20 02:40:44 +08:00
commit 053366c3e1
73 changed files with 640 additions and 610 deletions

View File

@ -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

View File

@ -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-

View File

@ -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

View File

@ -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
} }

View File

@ -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 {

View File

@ -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,
} }
} }

View File

@ -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

View File

@ -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()
} }
} }

View File

@ -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)

View File

@ -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)

View File

@ -50,7 +50,7 @@ func getKey(metadata *C.Metadata) string {
} }
} }
if metadata.DstIP == nil { if !metadata.DstIP.IsValid() {
return "" return ""
} }

View File

@ -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
View 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
}

View File

@ -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
} }

View File

@ -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,13 +20,10 @@ 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)
if ip != nil && !ip.IsGlobalUnicast() {
return return
} }
}
var innerErr error var innerErr error
err = c.Control(func(fd uintptr) { err = c.Control(func(fd uintptr) {
@ -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

View File

@ -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,13 +18,10 @@ 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)
if ip != nil && !ip.IsGlobalUnicast() {
return return
} }
}
var innerErr error var innerErr error
err = c.Control(func(fd uintptr) { err = c.Control(func(fd uintptr) {
@ -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

View File

@ -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
} }

View File

@ -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)

View File

@ -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)
if ip != nil && !ip.IsGlobalUnicast() {
return 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)
} }
}) })
} }

View File

@ -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()
} }

View File

@ -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)
} }
} }

View File

@ -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
}

View File

@ -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)
} }

View File

@ -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
} }
} }

View File

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

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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
} }

View 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"}
}

View File

@ -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()
} }
} }

View File

@ -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 nil, ErrIPNotFound return netip.AddrFrom4(*(*[4]byte)(ip)), nil
}
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)
} }

View File

@ -1,5 +1,4 @@
//go:build build_local //go:build build_local
// +build build_local
package script package script

View File

@ -1,5 +1,4 @@
//go:build !build_local && cgo //go:build !build_local && cgo
// +build !build_local,cgo
package script package script

View File

@ -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)

View File

@ -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,

View File

@ -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{},
}, },
} }

View File

@ -3,6 +3,7 @@ package constant
import ( import (
"encoding/json" "encoding/json"
"net" "net"
"net/netip"
"strconv" "strconv"
) )
@ -74,8 +75,8 @@ func (t Type) MarshalJSON() ([]byte, error) {
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:"-"`
@ -87,7 +88,7 @@ type Metadata struct {
} }
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 &copy return &copyM
} }
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()
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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()
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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)))
} }
} }

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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")
} }

View File

@ -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())
} }

View File

@ -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{

View File

@ -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)
break readLoop // close connection
} }
buff := make([]byte, session.conn.(*N.BufferedConn).Buffered()) b, err := conn.Peek(1)
_, _ = session.conn.Read(buff) if err != nil {
handleError(opt, session, err)
mrc := &multiReaderConn{ break readLoop // close connection
Conn: session.conn,
reader: io.MultiReader(bytes.NewReader(b), bytes.NewReader(buff), session.conn),
} }
// TLS handshake. // TLS handshake.
if b[0] == 0x16 { if b[0] == 0x16 {
// TODO serve by generic host name maybe better? tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
tlsConn := tls.Server(mrc, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
// Handshake with the local client // Handshake with the local client
if err = tlsConn.Handshake(); err != nil { if err = tlsConn.Handshake(); err != nil {
handleError(opt, session, err) session.response = session.NewErrorResponse(fmt.Errorf("handshake failed: %w", err))
_ = writeResponse(session, false)
break readLoop // close connection break readLoop // close connection
} }
c = tlsConn c = tlsConn
goto startOver // hijack and decrypt tls connection } else {
c = conn
} }
// maybe it's the others encrypted connection goto startOver
in <- inbound.NewHTTPS(session.request, mrc)
}
// maybe it's a http connection
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,55 +276,31 @@ 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)
if err != nil {
return c.RemoteAddr()
}
if ip := net.ParseIP(host); ip != nil {
return &net.TCPAddr{ return &net.TCPAddr{
IP: ip, IP: addrPort.Addr().AsSlice(),
Port: int(p), Port: int(addrPort.Port()),
} }
} }
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 {
if cli != nil { if cli != nil {
return cli return cli

View File

@ -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,

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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
} }

View File

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

View File

@ -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")

View File

@ -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]})
} }

View File

@ -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"
@ -47,9 +48,10 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
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() {

View File

@ -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":

View File

@ -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 {

View File

@ -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)
} }

View File

@ -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,
} }

View File

@ -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
) )

View File

@ -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=

View File

@ -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,7 +60,7 @@ 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
) )
@ -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)
} }

View File

@ -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 {
_ = r.Conn.Close()
return 0, errors.New("sni update, break current link to avoid leaks") 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,
} }
} }

View File

@ -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 {

View File

@ -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)