Refactor: metadata use netip.Addr

This commit is contained in:
yaling888 2022-04-20 01:52:51 +08:00 committed by adlyq
parent 6c4791480e
commit 7ca1a03d73
45 changed files with 374 additions and 346 deletions

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"net/netip"
"net/url" "net/url"
"strings" "strings"
"time" "time"
@ -64,9 +65,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
@ -95,7 +96,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()
@ -129,7 +130,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 {
@ -138,7 +141,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
@ -167,8 +170,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
return return
} }
} }
_ = resp.Body.Close()
resp.Body.Close()
t = uint16(time.Since(start) / time.Millisecond) t = uint16(time.Since(start) / time.Millisecond)
return return
} }
@ -199,7 +201,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

@ -37,7 +37,7 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
metadata.DstPort = port metadata.DstPort = port
if host == "" { if host == "" {
metadata.DstIP = ip metadata.DstIP = ip
if ip.To4() == nil { if ip.Is4() {
metadata.AddrType = C.AtypIPv6 metadata.AddrType = C.AtypIPv6
} else { } else {
metadata.AddrType = C.AtypIPv4 metadata.AddrType = C.AtypIPv4

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

@ -22,8 +22,8 @@ var (
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)
} }
} }
@ -48,14 +48,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)
@ -76,6 +76,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

@ -259,11 +259,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

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

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,12 +20,9 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn {
} }
}() }()
ipStr, _, err := net.SplitHostPort(address) addrPort, err := netip.ParseAddrPort(address)
if err == nil { if err == nil && !addrPort.Addr().IsGlobalUnicast() {
ip := net.ParseIP(ipStr) return
if ip != nil && !ip.IsGlobalUnicast() {
return
}
} }
var innerErr error var innerErr error
@ -45,7 +43,7 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn {
} }
} }
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error { func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {
ifaceObj, err := iface.ResolveInterface(ifaceName) ifaceObj, err := iface.ResolveInterface(ifaceName)
if err != nil { if err != nil {
return err return err

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,12 +18,9 @@ func bindControl(ifaceName string, chain controlFn) controlFn {
} }
}() }()
ipStr, _, err := net.SplitHostPort(address) addrPort, err := netip.ParseAddrPort(address)
if err == nil { if err == nil && !addrPort.Addr().IsGlobalUnicast() {
ip := net.ParseIP(ipStr) return
if ip != nil && !ip.IsGlobalUnicast() {
return
}
} }
var innerErr error var innerErr error
@ -38,7 +36,7 @@ func bindControl(ifaceName string, chain controlFn) controlFn {
} }
} }
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error { func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {
dialer.Control = bindControl(ifaceName, dialer.Control) dialer.Control = bindControl(ifaceName, dialer.Control)
return nil return nil

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) return
if ip != nil && !ip.IsGlobalUnicast() {
return
}
} }
return c.Control(func(fd uintptr) { return c.Control(func(fd uintptr) {
switch network { switch network {
case "tcp4", "udp4": case "tcp4", "udp4":
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
case "tcp6", "udp6": case "tcp6", "udp6":
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
} }
}) })
} }

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

@ -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,8 +3,10 @@ package process
import ( import (
"errors" "errors"
"net" "net"
"net/netip"
"runtime" "runtime"
"github.com/Dreamacro/clash/common/nnip"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
@ -19,7 +21,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)
} }
@ -31,23 +33,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
} }
@ -57,7 +59,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))
} }
} }
} }
@ -66,7 +68,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
} }

View file

@ -1,20 +1,18 @@
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)
StoreFakePoolState() StoreFakePoolState()
} }
@ -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)
} }

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 netip.AddrFrom4(*(*[4]byte)(ip)), nil
} }
return nil, ErrIPNotFound return netip.Addr{}, ErrIPNotFound
} }
// ResolveIPv6 with a host, return ipv6 // ResolveIPv6 with a host, return ipv6
func ResolveIPv6(host string) (net.IP, error) { func ResolveIPv6(host string) (netip.Addr, error) {
return ResolveIPv6WithResolver(host, DefaultResolver) return ResolveIPv6WithResolver(host, DefaultResolver)
} }
func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) { func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
if DisableIPv6 { if DisableIPv6 {
return nil, ErrIPv6Disabled return netip.Addr{}, ErrIPv6Disabled
} }
if node := DefaultHosts.Search(host); node != nil { if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data; ip.Is6() { if ip := node.Data; ip.Is6() {
return ip.AsSlice(), nil return ip, nil
} }
} }
ip := net.ParseIP(host) ip, err := netip.ParseAddr(host)
if ip != nil { if err == nil {
if strings.Contains(host, ":") { if ip.Is6() {
return ip, nil return ip, nil
} }
return nil, ErrIPVersion return netip.Addr{}, ErrIPVersion
} }
if r != nil { if r != nil {
@ -115,22 +120,21 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
defer cancel() defer cancel()
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host) ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
if err != nil { if err != nil {
return nil, err return netip.Addr{}, err
} else if len(ipAddrs) == 0 { } else if len(ipAddrs) == 0 {
return nil, ErrIPNotFound return netip.Addr{}, ErrIPNotFound
} }
return ipAddrs[rand.Intn(len(ipAddrs))], nil return netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))])), nil
} }
return nil, ErrIPNotFound return netip.Addr{}, ErrIPNotFound
} }
// ResolveIPWithResolver same as ResolveIP, but with a resolver // ResolveIPWithResolver same as ResolveIP, but with a resolver
func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) { func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
if node := DefaultHosts.Search(host); node != nil { if node := DefaultHosts.Search(host); node != nil {
ip := node.Data return node.Data, nil
return ip.Unmap().AsSlice(), nil
} }
if r != nil { if r != nil {
@ -142,30 +146,30 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
return ResolveIPv4(host) return ResolveIPv4(host)
} }
ip := net.ParseIP(host) ip, err := netip.ParseAddr(host)
if ip != nil { if err == nil {
return ip, nil return ip, nil
} }
if DefaultResolver == nil { if DefaultResolver == nil {
ipAddr, err := net.ResolveIPAddr("ip", host) ipAddr, err := net.ResolveIPAddr("ip", host)
if err != nil { if err != nil {
return nil, err return netip.Addr{}, err
} }
return ipAddr.IP, nil return nnip.IpToAddr(ipAddr.IP), nil
} }
return nil, ErrIPNotFound return netip.Addr{}, ErrIPNotFound
} }
// ResolveIP with a host, return ip // ResolveIP with a host, return ip
func ResolveIP(host string) (net.IP, error) { func ResolveIP(host string) (netip.Addr, error) {
return ResolveIPWithResolver(host, DefaultResolver) return ResolveIPWithResolver(host, DefaultResolver)
} }
// ResolveIPv4ProxyServerHost proxies server host only // ResolveIPv4ProxyServerHost proxies server host only
func ResolveIPv4ProxyServerHost(host string) (net.IP, error) { func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil { if ProxyServerHostResolver != nil {
return ResolveIPv4WithResolver(host, ProxyServerHostResolver) return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
} }
@ -173,7 +177,7 @@ func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
} }
// ResolveIPv6ProxyServerHost proxies server host only // ResolveIPv6ProxyServerHost proxies server host only
func ResolveIPv6ProxyServerHost(host string) (net.IP, error) { func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil { if ProxyServerHostResolver != nil {
return ResolveIPv6WithResolver(host, ProxyServerHostResolver) return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
} }
@ -181,7 +185,7 @@ func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
} }
// ResolveProxyServerHost proxies server host only // ResolveProxyServerHost proxies server host only
func ResolveProxyServerHost(host string) (net.IP, error) { func ResolveProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil { if ProxyServerHostResolver != nil {
return ResolveIPWithResolver(host, ProxyServerHostResolver) return ResolveIPWithResolver(host, ProxyServerHostResolver)
} }

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
"net" "net"
"net/netip"
CN "github.com/Dreamacro/clash/common/net" CN "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
@ -58,7 +59,7 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) {
metadata.Host = host metadata.Host = host
metadata.DNSMode = C.DNSMapping metadata.DNSMode = C.DNSMapping
resolver.InsertHostByIP(metadata.DstIP, host) resolver.InsertHostByIP(metadata.DstIP, host)
metadata.DstIP = nil metadata.DstIP = netip.Addr{}
} }
func (sd *SnifferDispatcher) Enable() bool { func (sd *SnifferDispatcher) Enable() bool {

View file

@ -87,7 +87,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"`
} }
@ -702,15 +702,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
@ -763,7 +763,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

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"net/netip"
"strconv" "strconv"
) )
@ -73,22 +74,26 @@ func (t Type) MarshalJSON() ([]byte, error) {
// Metadata is used to store connection address // Metadata is used to store connection address
type Metadata struct { type Metadata struct {
NetWork NetWork `json:"network"` NetWork NetWork `json:"network"`
Type Type `json:"type"` Type Type `json:"type"`
SrcIP net.IP `json:"sourceIP"` SrcIP netip.Addr `json:"sourceIP"`
DstIP net.IP `json:"destinationIP"` DstIP netip.Addr `json:"destinationIP"`
SrcPort string `json:"sourcePort"` SrcPort string `json:"sourcePort"`
DstPort string `json:"destinationPort"` DstPort string `json:"destinationPort"`
AddrType int `json:"-"` AddrType int `json:"-"`
Host string `json:"host"` Host string `json:"host"`
DNSMode DNSMode `json:"dnsMode"` DNSMode DNSMode `json:"dnsMode"`
Process string `json:"process"` Process string `json:"process"`
ProcessPath string `json:"processPath"` ProcessPath string `json:"processPath"`
UserAgent string `json:"userAgent"`
} }
func (m *Metadata) RemoteAddress() string { func (m *Metadata) RemoteAddress() string {
return net.JoinHostPort(m.String(), m.DstPort) if m.DstIP.IsValid() {
return net.JoinHostPort(m.DstIP.String(), m.DstPort)
} else {
return net.JoinHostPort(m.String(), m.DstPort)
}
} }
func (m *Metadata) SourceAddress() string { func (m *Metadata) SourceAddress() string {
@ -108,33 +113,33 @@ func (m *Metadata) SourceDetail() 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),
} }
} }
@ -142,7 +147,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>"
@ -150,5 +155,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,11 +1,9 @@
package dns package dns
import ( import (
"net"
"net/netip" "net/netip"
"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"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
@ -24,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(nnip.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 := nnip.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() == nnip.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 := nnip.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
} }
} }
@ -79,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(nnip.IpToAddr(ip), host) h.mapping.Set(ip, host)
} }
} }

View file

@ -1,18 +1,19 @@
package dns package dns
import ( import (
"net/netip"
"github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata"
"github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/geodata/router"
"github.com/Dreamacro/clash/component/mmdb" "github.com/Dreamacro/clash/component/mmdb"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"net"
"strings" "strings"
) )
type fallbackIPFilter interface { type fallbackIPFilter interface {
Match(net.IP) bool Match(netip.Addr) bool
} }
type geoipFilter struct { type geoipFilter struct {
@ -21,9 +22,9 @@ type geoipFilter struct {
var geoIPMatcher *router.GeoIPMatcher var geoIPMatcher *router.GeoIPMatcher
func (gf *geoipFilter) Match(ip net.IP) bool { func (gf *geoipFilter) Match(ip netip.Addr) bool {
if !C.GeodataMode { if !C.GeodataMode {
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()
} }
@ -54,14 +55,14 @@ func (gf *geoipFilter) Match(ip net.IP) bool {
return false return false
} }
} }
return !geoIPMatcher.Match(ip) return !geoIPMatcher.Match(ip.AsSlice())
} }
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)
} }
@ -76,7 +77,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,7 +1,6 @@
package dns package dns
import ( import (
"net"
"net/netip" "net/netip"
"strings" "strings"
"time" "time"
@ -88,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(nnip.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)
@ -255,16 +254,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
} }
} }
@ -273,13 +272,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)]
@ -318,7 +317,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
} }
@ -359,7 +358,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

@ -5,9 +5,11 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
"net/netip"
"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"
@ -99,15 +101,15 @@ 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))
} }
} }
@ -132,7 +134,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)
@ -147,7 +149,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
} }

View file

@ -83,6 +83,7 @@ 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)
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port)) rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() { if rAddrPort.Addr().IsLoopback() {
@ -135,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,

View file

@ -50,7 +50,7 @@ func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, u
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))

View file

@ -2,7 +2,7 @@ package common
import ( import (
"errors" "errors"
"net" "net/netip"
"strings" "strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -54,17 +54,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

@ -26,7 +26,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
} }
@ -39,10 +39,10 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool {
resolver.IsFakeBroadcastIP(ip) resolver.IsFakeBroadcastIP(ip)
} }
if !C.GeodataMode { if !C.GeodataMode {
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)
} }
return g.geoIPMatcher.Match(ip) return g.geoIPMatcher.Match(ip.AsSlice())
} }
func (g *GEOIP) Adapter() string { func (g *GEOIP) Adapter() string {

View file

@ -1,7 +1,7 @@
package common package common
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

@ -13,7 +13,7 @@ type ipcidrStrategy struct {
} }
func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool { func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool {
return i.trie != nil && i.trie.IsContain(metadata.DstIP) return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())
} }
func (i *ipcidrStrategy) Count() int { func (i *ipcidrStrategy) Count() int {

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,8 +60,8 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
} }
var ( var (
dstIP = req[4:8] // [4]byte dstIP = netip.AddrFrom4(*(*[4]byte)(req[4:8])) // [4]byte
dstPort = req[2:4] // [2]byte dstPort = req[2:4] // [2]byte
) )
var ( var (
@ -83,7 +86,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
if host != "" { if host != "" {
addr = net.JoinHostPort(host, port) addr = net.JoinHostPort(host, port)
} else { } else {
addr = net.JoinHostPort(net.IP(dstIP).String(), port) addr = net.JoinHostPort(dstIP.String(), port)
} }
// SOCKS4 only support USERID auth. // SOCKS4 only support USERID auth.
@ -97,7 +100,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
var reply [8]byte var reply [8]byte
reply[0] = 0x00 // reply code reply[0] = 0x00 // reply code
reply[1] = code // result code reply[1] = code // result code
copy(reply[4:8], dstIP) copy(reply[4:8], dstIP.AsSlice())
copy(reply[2:4], dstPort) copy(reply[2:4], dstPort)
_, wErr := rw.Write(reply[:]) _, wErr := rw.Write(reply[:])
@ -118,20 +121,18 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
return err return err
} }
ip := net.ParseIP(host) dstIP, err := netip.ParseAddr(host)
if ip == nil /* HOST */ { if err != nil /* HOST */ {
ip = net.IPv4(0, 0, 0, 1).To4() dstIP = netip.AddrFrom4([4]byte{0, 0, 0, 1})
} else if ip.To4() == nil /* IPv6 */ { } else if dstIP.Is6() /* IPv6 */ {
return errIPv6NotSupported return errIPv6NotSupported
} }
dstIP := ip.To4()
req := &bytes.Buffer{} req := &bytes.Buffer{}
req.WriteByte(Version) req.WriteByte(Version)
req.WriteByte(command) req.WriteByte(command)
binary.Write(req, binary.BigEndian, uint16(port)) _ = binary.Write(req, binary.BigEndian, uint16(port))
req.Write(dstIP) req.Write(dstIP.AsSlice())
req.WriteString(userID) req.WriteString(userID)
req.WriteByte(0) /* NULL */ req.WriteByte(0) /* NULL */
@ -174,12 +175,7 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
// Internet Assigned Numbers Authority -- such an address is inadmissible // Internet Assigned Numbers Authority -- such an address is inadmissible
// as a destination IP address and thus should never occur if the client // as a destination IP address and thus should never occur if the client
// can resolve the domain name.) // can resolve the domain name.)
func isReservedIP(ip net.IP) bool { func isReservedIP(ip netip.Addr) bool {
subnet := net.IPNet{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
}
return !ip.IsUnspecified() && subnet.Contains(ip) return !ip.IsUnspecified() && subnet.Contains(ip)
} }

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
P "github.com/Dreamacro/clash/component/process" P "github.com/Dreamacro/clash/component/process"
"net" "net"
"net/netip"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
@ -131,15 +132,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
@ -154,11 +155,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)
@ -351,7 +352,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) {
@ -360,7 +361,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
} }