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
push: true
tags: 'dreamacro/clash:dev,ghcr.io/dreamacro/clash:dev'
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Get all docker tags
if: startsWith(github.ref, 'refs/tags/')
@ -74,3 +76,5 @@ jobs:
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
push: true
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
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

View File

@ -7,6 +7,7 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"net/url"
"time"
_ "unsafe"
@ -62,9 +63,9 @@ func (p *Proxy) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
// DelayHistory implements C.Proxy
func (p *Proxy) DelayHistory() []C.DelayHistory {
queue := p.history.Copy()
queueM := p.history.Copy()
histories := []C.DelayHistory{}
for _, item := range queue {
for _, item := range queueM {
histories = append(histories, item)
}
return histories
@ -93,7 +94,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
}
mapping := map[string]any{}
json.Unmarshal(inner, &mapping)
_ = json.Unmarshal(inner, &mapping)
mapping["history"] = p.DelayHistory()
mapping["name"] = p.Name()
mapping["udp"] = p.SupportUDP()
@ -125,7 +126,9 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
if err != nil {
return
}
defer instance.Close()
defer func() {
_ = instance.Close()
}()
req, err := http.NewRequest(http.MethodHead, url, 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)
transport := &http.Transport{
Dial: func(string, string) (net.Conn, error) {
DialContext: func(context.Context, string, string) (net.Conn, error) {
return instance, nil
},
// from http.DefaultTransport
@ -156,7 +159,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
if err != nil {
return
}
resp.Body.Close()
_ = resp.Body.Close()
t = uint16(time.Since(start) / time.Millisecond)
return
}
@ -187,7 +190,7 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
addr = C.Metadata{
AddrType: C.AtypDomainName,
Host: u.Hostname(),
DstIP: nil,
DstIP: netip.Addr{},
DstPort: port,
}
return

View File

@ -3,9 +3,11 @@ package inbound
import (
"net"
"net/http"
"net/netip"
"strconv"
"strings"
"github.com/Dreamacro/clash/common/nnip"
C "github.com/Dreamacro/clash/constant"
"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.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))
case socks5.AtypIPv4:
ip := net.IP(target[1 : 1+net.IPv4len])
metadata.DstIP = ip
metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv4len]))
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
case socks5.AtypIPv6:
ip := net.IP(target[1 : 1+net.IPv6len])
metadata.DstIP = ip
metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv6len]))
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,
AddrType: C.AtypDomainName,
Host: host,
DstIP: nil,
DstIP: netip.Addr{},
DstPort: port,
}
ip := net.ParseIP(host)
if ip != nil {
ip, err := netip.ParseAddr(host)
if err == nil {
switch {
case ip.To4() == nil:
case ip.Is6():
metadata.AddrType = C.AtypIPv6
default:
metadata.AddrType = C.AtypIPv4
@ -65,12 +65,12 @@ func parseHTTPAddr(request *http.Request) *C.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)
if err != nil {
return nil, "", err
return netip.Addr{}, "", err
}
ip := net.ParseIP(host)
return ip, port, nil
ip, err := netip.ParseAddr(host)
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 {
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 {

View File

@ -7,26 +7,23 @@ import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
"go.uber.org/atomic"
)
var (
errIgnored = errors.New("not match in mitm host lists")
httpProxyClient = NewHttp(HttpOption{})
MiddlemanServerAddress = atomic.NewString("")
MiddlemanRewriteHosts *trie.DomainTrie[bool]
)
type Mitm struct {
*Base
serverAddr string
}
// DialContext implements C.ProxyAdapter
func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
addr := MiddlemanServerAddress.Load()
if addr == "" || MiddlemanRewriteHosts == nil {
func (m *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
if MiddlemanRewriteHosts == nil {
return nil, errIgnored
}
@ -36,12 +33,7 @@ func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...diale
metadata.Type = C.MITM
if metadata.Host != "" {
metadata.AddrType = C.AtypDomainName
metadata.DstIP = nil
}
c, err := dialer.DialContext(ctx, "tcp", addr, []dialer.Option{dialer.WithInterface(""), dialer.WithRoutingMark(0)}...)
c, err := dialer.DialContext(ctx, "tcp", m.serverAddr, []dialer.Option{dialer.WithInterface(""), dialer.WithRoutingMark(0)}...)
if err != nil {
return nil, err
}
@ -55,14 +47,15 @@ func (d *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...diale
return nil, err
}
return NewConn(c, d), nil
return NewConn(c, m), nil
}
func NewMitm() *Mitm {
func NewMitm(serverAddr string) *Mitm {
return &Mitm{
Base: &Base{
name: "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) {
// 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))
cipher := option.Cipher
password := option.Password
@ -103,13 +109,14 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
ivSize int
key []byte
)
if option.Cipher == "dummy" {
ivSize = 0
key = core.Kdf(option.Password, 16)
} else {
ciph, ok := coreCiph.(*core.StreamCipher)
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()
key = ciph.Key

View File

@ -13,8 +13,8 @@ import (
func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
tcp.SetKeepAlive(true)
tcp.SetKeepAlivePeriod(30 * time.Second)
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
}
}
@ -25,14 +25,14 @@ func serializesSocksAddr(metadata *C.Metadata) []byte {
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
switch metadata.AddrType {
case socks5.AtypDomainName:
len := uint8(len(metadata.Host))
lenM := uint8(len(metadata.Host))
host := []byte(metadata.Host)
buf = [][]byte{{aType, len}, host, port}
buf = [][]byte{{aType, lenM}, host, port}
case socks5.AtypIPv4:
host := metadata.DstIP.To4()
host := metadata.DstIP.AsSlice()
buf = [][]byte{{aType}, host, port}
case socks5.AtypIPv6:
host := metadata.DstIP.To16()
host := metadata.DstIP.AsSlice()
buf = [][]byte{{aType}, host, port}
}
return bytes.Join(buf, nil)
@ -53,6 +53,6 @@ func resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
func safeConnClose(c net.Conn, err error) {
if err != nil {
c.Close()
_ = c.Close()
}
}

View File

@ -263,11 +263,11 @@ func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
case C.AtypIPv4:
addrType = byte(vless.AtypIPv4)
addr = make([]byte, net.IPv4len)
copy(addr[:], metadata.DstIP.To4())
copy(addr[:], metadata.DstIP.AsSlice())
case C.AtypIPv6:
addrType = byte(vless.AtypIPv6)
addr = make([]byte, net.IPv6len)
copy(addr[:], metadata.DstIP.To16())
copy(addr[:], metadata.DstIP.AsSlice())
case C.AtypDomainName:
addrType = byte(vless.AtypDomainName)
addr = make([]byte, len(metadata.Host)+1)

View File

@ -342,11 +342,11 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
case C.AtypIPv4:
addrType = byte(vmess.AtypIPv4)
addr = make([]byte, net.IPv4len)
copy(addr[:], metadata.DstIP.To4())
copy(addr[:], metadata.DstIP.AsSlice())
case C.AtypIPv6:
addrType = byte(vmess.AtypIPv6)
addr = make([]byte, net.IPv6len)
copy(addr[:], metadata.DstIP.To16())
copy(addr[:], metadata.DstIP.AsSlice())
case C.AtypDomainName:
addrType = byte(vmess.AtypDomainName)
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 ""
}

View File

@ -3,6 +3,7 @@ package outboundgroup
import (
"fmt"
"net"
"net/netip"
"time"
C "github.com/Dreamacro/clash/constant"
@ -15,20 +16,20 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
return
}
ip := net.ParseIP(host)
if ip == nil {
ip, err := netip.ParseAddr(host)
if err != nil {
addr = &C.Metadata{
AddrType: C.AtypDomainName,
Host: host,
DstIP: nil,
DstIP: netip.Addr{},
DstPort: port,
}
return
} else if ip4 := ip.To4(); ip4 != nil {
} else if ip.Is4() {
addr = &C.Metadata{
AddrType: C.AtypIPv4,
Host: "",
DstIP: ip4,
DstIP: ip,
DstPort: port,
}
return
@ -45,7 +46,7 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
tcp.SetKeepAlive(true)
tcp.SetKeepAlivePeriod(30 * time.Second)
_ = tcp.SetKeepAlive(true)
_ = 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"
"errors"
"net"
"net/netip"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/component/iface"
"github.com/insomniacslk/dhcp/dhcpv4"
@ -15,14 +17,16 @@ var (
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)
if err != nil {
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)
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)
buf := make([]byte, dhcpv4.MaxMessageSize)
@ -77,11 +81,17 @@ func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []
}
dns := pkt.DNS()
if len(dns) == 0 {
l := len(dns)
if l == 0 {
return
}
result <- dns
dnsAddr := make([]netip.Addr, l)
for i := 0; i < l; i++ {
dnsAddr[i] = nnip.IpToAddr(dns[i])
}
result <- dnsAddr
return
}

View File

@ -2,6 +2,7 @@ package dialer
import (
"net"
"net/netip"
"syscall"
"github.com/Dreamacro/clash/component/iface"
@ -19,13 +20,10 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn {
}
}()
ipStr, _, err := net.SplitHostPort(address)
if err == nil {
ip := net.ParseIP(ipStr)
if ip != nil && !ip.IsGlobalUnicast() {
addrPort, err := netip.ParseAddrPort(address)
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
return
}
}
var innerErr error
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)
if err != nil {
return err

View File

@ -2,6 +2,7 @@ package dialer
import (
"net"
"net/netip"
"syscall"
"golang.org/x/sys/unix"
@ -17,13 +18,10 @@ func bindControl(ifaceName string, chain controlFn) controlFn {
}
}()
ipStr, _, err := net.SplitHostPort(address)
if err == nil {
ip := net.ParseIP(ipStr)
if ip != nil && !ip.IsGlobalUnicast() {
addrPort, err := netip.ParseAddrPort(address)
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
return
}
}
var innerErr error
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)
return nil

View File

@ -4,27 +4,28 @@ package dialer
import (
"net"
"net/netip"
"strconv"
"strings"
"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)
if err != nil {
return nil, err
}
var addr *net.IPNet
var addr *netip.Prefix
switch network {
case "udp4", "tcp4":
addr, err = ifaceObj.PickIPv4Addr(destination)
case "tcp6", "udp6":
addr, err = ifaceObj.PickIPv6Addr(destination)
default:
if destination != nil {
if destination.To4() != nil {
if destination.IsValid() {
if destination.Is4() {
addr, err = ifaceObj.PickIPv4Addr(destination)
} else {
addr, err = ifaceObj.PickIPv6Addr(destination)
@ -39,12 +40,12 @@ func lookupLocalAddr(ifaceName string, network string, destination net.IP, port
if strings.HasPrefix(network, "tcp") {
return &net.TCPAddr{
IP: addr.IP,
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
} else if strings.HasPrefix(network, "udp") {
return &net.UDPAddr{
IP: addr.IP,
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
}
@ -52,7 +53,7 @@ func lookupLocalAddr(ifaceName string, network string, destination net.IP, port
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() {
return nil
}
@ -83,7 +84,7 @@ func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, add
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 {
return "", err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"net"
"net/netip"
"github.com/Dreamacro/clash/component/resolver"
)
@ -29,7 +30,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option
return nil, err
}
var ip net.IP
var ip netip.Addr
switch network {
case "tcp4", "udp4":
if !opt.direct {
@ -88,7 +89,7 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
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{}
if opt.interfaceName != "" {
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 <-returned:
if result.Conn != nil {
result.Conn.Close()
_ = result.Conn.Close()
}
}
}()
var ip net.IP
var ip netip.Addr
if ipv6 {
if !direct {
ip, result.error = resolver.ResolveIPv6ProxyServerHost(host)

View File

@ -4,14 +4,15 @@ package dialer
import (
"net"
"net/netip"
"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)
}
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {
lc.Control = bindMarkToControl(mark, lc.Control)
}
@ -23,20 +24,17 @@ func bindMarkToControl(mark int, chain controlFn) controlFn {
}
}()
ipStr, _, err := net.SplitHostPort(address)
if err == nil {
ip := net.ParseIP(ipStr)
if ip != nil && !ip.IsGlobalUnicast() {
addrPort, err := netip.ParseAddrPort(address)
if err == nil && !addrPort.Addr().IsGlobalUnicast() {
return
}
}
return c.Control(func(fd uintptr) {
switch network {
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":
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 (
"net"
"net/netip"
"sync"
"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()
}
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {
printMarkWarn()
}

View File

@ -3,6 +3,7 @@ package dialer
import (
"context"
"net"
"net/netip"
)
func init() {
@ -18,9 +19,9 @@ func resolverDialContext(ctx context.Context, network, address string) (net.Conn
interfaceName := DefaultInterface.Load()
if interfaceName != "" {
dstIP := net.ParseIP(address)
if dstIP != nil {
bindIfaceToDialer(interfaceName, d, network, dstIP)
dstIP, err := netip.ParseAddr(address)
if err == nil {
_ = bindIfaceToDialer(interfaceName, d, network, dstIP)
}
}

View File

@ -1,12 +1,11 @@
package fakeip
import (
"encoding/binary"
"errors"
"math/bits"
"net/netip"
"sync"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/component/profile/cachefile"
"github.com/Dreamacro/clash/component/trie"
)
@ -16,11 +15,6 @@ const (
cycleKey = "key-cycle-fake-ip"
)
type uint128 struct {
hi uint64
lo uint64
}
type store interface {
GetByHost(host string) (netip.Addr, bool)
PutByHost(host string, ip netip.Addr)
@ -122,7 +116,7 @@ func (p *Pool) FlushFakeIP() error {
err := p.store.FlushFakeIP()
if err == nil {
p.cycle = false
p.offset = p.first
p.offset = p.first.Prev()
}
return err
}
@ -173,10 +167,10 @@ func New(options Options) (*Pool, error) {
hostAddr = options.IPNet.Masked().Addr()
gateway = hostAddr.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")
}
@ -201,29 +195,3 @@ func New(options Options) (*Pool, error) {
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()
assert.Nil(t, err)
baz := pool.Lookup("foo.com")
next := pool.Lookup("baz.com")
baz := pool.Lookup("foo.com")
nero := pool.Lookup("foo.com")
assert.True(t, foo == fox)
assert.True(t, foo == next)
assert.False(t, foo == baz)
assert.True(t, bar == bax)
assert.True(t, bar == baz)
assert.False(t, bar == next)
assert.True(t, baz == nero)
}
@ -267,13 +269,15 @@ func TestPool_FlushMemoryCache(t *testing.T) {
err := pool.FlushFakeIP()
assert.Nil(t, err)
baz := pool.Lookup("foo.com")
next := pool.Lookup("baz.com")
baz := pool.Lookup("foo.com")
nero := pool.Lookup("foo.com")
assert.True(t, foo == fox)
assert.True(t, foo == next)
assert.False(t, foo == baz)
assert.True(t, bar == bax)
assert.True(t, bar == baz)
assert.False(t, bar == next)
assert.True(t, baz == nero)
}

View File

@ -3,6 +3,7 @@ package iface
import (
"errors"
"net"
"net/netip"
"time"
"github.com/Dreamacro/clash/common/singledo"
@ -11,7 +12,7 @@ import (
type Interface struct {
Index int
Name string
Addrs []*net.IPNet
Addrs []*netip.Prefix
HardwareAddr net.HardwareAddr
}
@ -37,14 +38,18 @@ func ResolveInterface(name string) (*Interface, error) {
continue
}
ipNets := make([]*net.IPNet, 0, len(addrs))
ipNets := make([]*netip.Prefix, 0, len(addrs))
for _, addr := range addrs {
ipNet := addr.(*net.IPNet)
if v4 := ipNet.IP.To4(); v4 != nil {
ipNet.IP = v4
ip, _ := netip.AddrFromSlice(ipNet.IP)
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{
@ -74,35 +79,35 @@ func FlushCache() {
interfaces.Reset()
}
func (iface *Interface) PickIPv4Addr(destination net.IP) (*net.IPNet, error) {
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
return addr.IP.To4() != nil
func (iface *Interface) PickIPv4Addr(destination netip.Addr) (*netip.Prefix, error) {
return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool {
return addr.Addr().Is4()
})
}
func (iface *Interface) PickIPv6Addr(destination net.IP) (*net.IPNet, error) {
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
return addr.IP.To4() == nil
func (iface *Interface) PickIPv6Addr(destination netip.Addr) (*netip.Prefix, error) {
return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool {
return addr.Addr().Is6()
})
}
func (iface *Interface) pickIPAddr(destination net.IP, accept func(addr *net.IPNet) bool) (*net.IPNet, error) {
var fallback *net.IPNet
func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr *netip.Prefix) bool) (*netip.Prefix, error) {
var fallback *netip.Prefix
for _, addr := range iface.Addrs {
if !accept(addr) {
continue
}
if fallback == nil && !addr.IP.IsLinkLocalUnicast() {
if fallback == nil && !addr.Addr().IsLinkLocalUnicast() {
fallback = addr
if destination == nil {
if !destination.IsValid() {
break
}
}
if destination != nil && addr.Contains(destination) {
if destination.IsValid() && addr.Contains(destination) {
return addr, nil
}
}

View File

@ -3,7 +3,9 @@ package process
import (
"errors"
"net"
"net/netip"
"github.com/Dreamacro/clash/common/nnip"
C "github.com/Dreamacro/clash/constant"
)
@ -18,7 +20,7 @@ const (
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)
}
@ -27,23 +29,23 @@ func ShouldFindProcess(metadata *C.Metadata) bool {
return false
}
for _, ip := range localIPs {
if ip.Equal(metadata.SrcIP) {
if ip == metadata.SrcIP {
return true
}
}
return false
}
func AppendLocalIPs(ip ...net.IP) {
func AppendLocalIPs(ip ...netip.Addr) {
localIPs = append(ip, localIPs...)
}
func getLocalIPs() []net.IP {
ips := []net.IP{net.IPv4zero, net.IPv6zero}
func getLocalIPs() []netip.Addr {
ips := []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()}
netInterfaces, err := net.Interfaces()
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
}
@ -53,7 +55,7 @@ func getLocalIPs() []net.IP {
for _, address := range adds {
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
}
var localIPs []net.IP
var localIPs []netip.Addr
func init() {
localIPs = getLocalIPs()

View File

@ -2,10 +2,12 @@ package process
import (
"encoding/binary"
"net"
"net/netip"
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"golang.org/x/sys/unix"
)
@ -15,7 +17,7 @@ const (
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
switch network {
case TCP:
@ -26,7 +28,7 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
return "", ErrInvalidNetwork
}
isIPv4 := ip.To4() != nil
isIPv4 := ip.Is4()
value, err := syscall.Sysctl(spath)
if err != nil {
@ -57,19 +59,19 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
// xinpcb_n.inp_vflag
flag := buf[inp+44]
var srcIP net.IP
var srcIP netip.Addr
switch {
case flag&0x1 > 0 && isIPv4:
// ipv4
srcIP = net.IP(buf[inp+76 : inp+80])
srcIP = nnip.IpToAddr(buf[inp+76 : inp+80])
case flag&0x2 > 0 && !isIPv4:
// ipv6
srcIP = net.IP(buf[inp+64 : inp+80])
srcIP = nnip.IpToAddr(buf[inp+64 : inp+80])
default:
continue
}
if !ip.Equal(srcIP) && (network == TCP || !srcIP.IsUnspecified()) {
if ip != srcIP && (network == TCP || !srcIP.IsUnspecified()) {
continue
}

View File

@ -3,13 +3,14 @@ package process
import (
"encoding/binary"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"sync"
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/log"
)
@ -20,7 +21,7 @@ var (
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() {
if err := initSearcher(); err != nil {
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
@ -102,7 +103,7 @@ type searcher struct {
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 inpOffset int
@ -116,7 +117,7 @@ func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint3
inpOffset = s.udpInpOffset
}
isIPv4 := ip.To4() != nil
isIPv4 := ip.Is4()
// skip the first xinpgen block
for i := s.headSize; i+itemSize <= len(buf); i += itemSize {
inp := i + inpOffset
@ -130,19 +131,19 @@ func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint3
// xinpcb.inp_vflag
flag := buf[inp+s.vflag]
var srcIP net.IP
var srcIP netip.Addr
switch {
case flag&0x1 > 0 && isIPv4:
// 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:
// 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:
continue
}
if !ip.Equal(srcIP) {
if ip != srcIP {
continue
}

View File

@ -5,6 +5,7 @@ import (
"encoding/binary"
"fmt"
"net"
"net/netip"
"os"
"path"
"strings"
@ -31,7 +32,7 @@ const (
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)
if err != nil {
return "", err
@ -40,7 +41,7 @@ func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
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 protocol byte
@ -53,7 +54,7 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
return 0, 0, ErrInvalidNetwork
}
if ip.To4() != nil {
if ip.Is4() {
family = syscall.AF_INET
} else {
family = syscall.AF_INET6
@ -65,10 +66,12 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
if err != nil {
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_RCVTIMEO, &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})
if err := syscall.Connect(socket, &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
@ -84,7 +87,9 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
}
rb := pool.Get(pool.RelayBufferSize)
defer pool.Put(rb)
defer func() {
_ = pool.Put(rb)
}()
n, err := syscall.Read(socket, rb)
if err != nil {
@ -111,14 +116,10 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
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)
if v4 := source.To4(); v4 != nil {
copy(s, v4)
} else {
copy(s, source)
}
copy(s, source.AsSlice())
buf := make([]byte, sizeOfSocketDiagRequest)

View File

@ -2,8 +2,8 @@
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
}

View File

@ -2,11 +2,12 @@ package process
import (
"fmt"
"net"
"net/netip"
"sync"
"syscall"
"unsafe"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
@ -57,7 +58,7 @@ func initWin32API() error {
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() {
err := initWin32API()
if err != nil {
@ -67,7 +68,7 @@ func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
}
})
family := windows.AF_INET
if ip.To4() == nil {
if ip.Is6() {
family = windows.AF_INET6
}
@ -107,7 +108,7 @@ type searcher struct {
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]))
itemSize := s.itemSize
for i := 0; i < n; i++ {
@ -131,9 +132,9 @@ func (s *searcher) Search(b []byte, ip net.IP, port uint16) (uint32, error) {
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
if !ip.Equal(srcIP) && (!srcIP.IsUnspecified() || s.tcpState != -1) {
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
continue
}
@ -174,7 +175,7 @@ func newSearcher(isV4, isTCP bool) *searcher {
func getTransportTable(fn uintptr, family int, class int) ([]byte, error) {
for size, buf := uint32(8), make([]byte, 8); ; {
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 {
case 0:
@ -209,13 +210,13 @@ func getExecPathFromPID(pid uint32) (string, error) {
buf := make([]uint16, syscall.MAX_LONG_PATH)
size := uint32(len(buf))
r1, _, err := syscall.Syscall6(
queryProcName, 4,
r1, _, err := syscall.SyscallN(
queryProcName,
uintptr(h),
uintptr(1),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&size)),
0, 0)
)
if r1 == 0 {
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
import (
"net"
)
import "net/netip"
var DefaultHostMapper Enhancer
type Enhancer interface {
FakeIPEnabled() bool
MappingEnabled() bool
IsFakeIP(net.IP) bool
IsFakeBroadcastIP(net.IP) bool
IsExistFakeIP(net.IP) bool
FindHostByIP(net.IP) (string, bool)
IsFakeIP(netip.Addr) bool
IsFakeBroadcastIP(netip.Addr) bool
IsExistFakeIP(netip.Addr) bool
FindHostByIP(netip.Addr) (string, bool)
FlushFakeIP() error
InsertHostByIP(net.IP, string)
StoreFakePoolSate()
InsertHostByIP(netip.Addr, string)
StoreFakePoolState()
}
func FakeIPEnabled() bool {
@ -34,7 +32,7 @@ func MappingEnabled() bool {
return false
}
func IsFakeIP(ip net.IP) bool {
func IsFakeIP(ip netip.Addr) bool {
if mapper := DefaultHostMapper; mapper != nil {
return mapper.IsFakeIP(ip)
}
@ -42,7 +40,7 @@ func IsFakeIP(ip net.IP) bool {
return false
}
func IsFakeBroadcastIP(ip net.IP) bool {
func IsFakeBroadcastIP(ip netip.Addr) bool {
if mapper := DefaultHostMapper; mapper != nil {
return mapper.IsFakeBroadcastIP(ip)
}
@ -50,7 +48,7 @@ func IsFakeBroadcastIP(ip net.IP) bool {
return false
}
func IsExistFakeIP(ip net.IP) bool {
func IsExistFakeIP(ip netip.Addr) bool {
if mapper := DefaultHostMapper; mapper != nil {
return mapper.IsExistFakeIP(ip)
}
@ -58,13 +56,13 @@ func IsExistFakeIP(ip net.IP) bool {
return false
}
func InsertHostByIP(ip net.IP, host string) {
func InsertHostByIP(ip netip.Addr, host string) {
if mapper := DefaultHostMapper; mapper != nil {
mapper.InsertHostByIP(ip, host)
}
}
func FindHostByIP(ip net.IP) (string, bool) {
func FindHostByIP(ip netip.Addr) (string, bool) {
if mapper := DefaultHostMapper; mapper != nil {
return mapper.FindHostByIP(ip)
}
@ -79,8 +77,8 @@ func FlushFakeIP() error {
return nil
}
func StoreFakePoolSate() {
func StoreFakePoolState() {
if mapper := DefaultHostMapper; mapper != nil {
mapper.StoreFakePoolSate()
mapper.StoreFakePoolState()
}
}

View File

@ -6,9 +6,9 @@ import (
"math/rand"
"net"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/component/trie"
)
@ -37,29 +37,29 @@ var (
)
type Resolver interface {
ResolveIP(host string) (ip net.IP, err error)
ResolveIPv4(host string) (ip net.IP, err error)
ResolveIPv6(host string) (ip net.IP, err error)
ResolveIP(host string) (ip netip.Addr, err error)
ResolveIPv4(host string) (ip netip.Addr, err error)
ResolveIPv6(host string) (ip netip.Addr, err error)
}
// ResolveIPv4 with a host, return ipv4
func ResolveIPv4(host string) (net.IP, error) {
func ResolveIPv4(host string) (netip.Addr, error) {
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 ip := node.Data; ip.Is4() {
return ip.AsSlice(), nil
return ip, nil
}
}
ip := net.ParseIP(host)
if ip != nil {
if !strings.Contains(host, ":") {
ip, err := netip.ParseAddr(host)
if err == nil {
if ip.Is4() {
return ip, nil
}
return nil, ErrIPVersion
return netip.Addr{}, ErrIPVersion
}
if r != nil {
@ -71,39 +71,44 @@ func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
defer cancel()
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
if err != nil {
return nil, err
return netip.Addr{}, err
} 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
func ResolveIPv6(host string) (net.IP, error) {
func ResolveIPv6(host string) (netip.Addr, error) {
return ResolveIPv6WithResolver(host, DefaultResolver)
}
func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
if DisableIPv6 {
return nil, ErrIPv6Disabled
return netip.Addr{}, ErrIPv6Disabled
}
if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data; ip.Is6() {
return ip.AsSlice(), nil
return ip, nil
}
}
ip := net.ParseIP(host)
if ip != nil {
if strings.Contains(host, ":") {
ip, err := netip.ParseAddr(host)
if err == nil {
if ip.Is6() {
return ip, nil
}
return nil, ErrIPVersion
return netip.Addr{}, ErrIPVersion
}
if r != nil {
@ -115,22 +120,21 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
defer cancel()
ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host)
if err != nil {
return nil, err
return netip.Addr{}, err
} 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
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 {
ip := node.Data
return ip.Unmap().AsSlice(), nil
return node.Data, nil
}
if r != nil {
@ -142,30 +146,30 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
return ResolveIPv4(host)
}
ip := net.ParseIP(host)
if ip != nil {
ip, err := netip.ParseAddr(host)
if err == nil {
return ip, nil
}
if DefaultResolver == nil {
ipAddr, err := net.ResolveIPAddr("ip", host)
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
func ResolveIP(host string) (net.IP, error) {
func ResolveIP(host string) (netip.Addr, error) {
return ResolveIPWithResolver(host, DefaultResolver)
}
// ResolveIPv4ProxyServerHost proxies server host only
func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
}
@ -173,7 +177,7 @@ func ResolveIPv4ProxyServerHost(host string) (net.IP, error) {
}
// ResolveIPv6ProxyServerHost proxies server host only
func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
}
@ -181,7 +185,7 @@ func ResolveIPv6ProxyServerHost(host string) (net.IP, error) {
}
// ResolveProxyServerHost proxies server host only
func ResolveProxyServerHost(host string) (net.IP, error) {
func ResolveProxyServerHost(host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPWithResolver(host, ProxyServerHostResolver)
}

View File

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

View File

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

View File

@ -195,10 +195,10 @@ func CallPyMainFunction(mtd *constant.Metadata) (string, error) {
dstIpGo := ""
srcIpGo := ""
if mtd.SrcIP != nil {
if mtd.SrcIP.IsValid() {
srcIpGo = mtd.SrcIP.String()
}
if mtd.DstIP != nil {
if mtd.DstIP.IsValid() {
dstIpGo = mtd.DstIP.String()
}
srcIp := C.CString(srcIpGo)
@ -250,10 +250,10 @@ func CallPyShortcut(fn *PyObject, mtd *constant.Metadata) (bool, error) {
dstIpGo := ""
srcIpGo := ""
if mtd.SrcIP != nil {
if mtd.SrcIP.IsValid() {
srcIpGo = mtd.SrcIP.String()
}
if mtd.DstIP != nil {
if mtd.DstIP.IsValid() {
dstIpGo = mtd.DstIP.String()
}
srcIp := C.CString(srcIpGo)

View File

@ -5,7 +5,7 @@ package script
*/
import "C"
import (
"net"
"net/netip"
"strconv"
"strings"
"unsafe"
@ -50,9 +50,9 @@ func resolveIPCallbackFn(cHost *C.char) *C.char {
//export geoipCallbackFn
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("")
defer C.free(unsafe.Pointer(emptyC))
@ -72,7 +72,7 @@ func geoipCallbackFn(cIP *C.char) *C.char {
return lanC
}
record, _ := mmdb.Instance().Country(dstIP)
record, _ := mmdb.Instance().Country(dstIP.AsSlice())
rc := C.CString(strings.ToUpper(record.Country.IsoCode))
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)
dstPort := strconv.Itoa(int(cMetadata.dst_port))
dst := net.ParseIP(dstIp)
addrType := constant.AtypDomainName
dst, err := netip.ParseAddr(dstIp)
if dst != nil {
if dst.To4() != nil {
addrType := constant.AtypDomainName
if err == nil {
if dst.Is4() {
addrType = constant.AtypIPv4
} else {
addrType = constant.AtypIPv6
}
}
src, _ := netip.ParseAddr(srcIp)
metadata := &constant.Metadata{
Process: processName,
SrcIP: net.ParseIP(srcIp),
SrcIP: src,
DstIP: dst,
SrcPort: srcPort,
DstPort: dstPort,

View File

@ -85,7 +85,7 @@ type DNS struct {
type FallbackFilter struct {
GeoIP bool `yaml:"geoip"`
GeoIPCode string `yaml:"geoip-code"`
IPCIDR []*net.IPNet `yaml:"ipcidr"`
IPCIDR []*netip.Prefix `yaml:"ipcidr"`
Domain []string `yaml:"domain"`
GeoSite []*router.DomainMatcher `yaml:"geosite"`
}
@ -685,15 +685,15 @@ func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServe
return policy, nil
}
func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) {
var ipNets []*net.IPNet
func parseFallbackIPCIDR(ips []string) ([]*netip.Prefix, error) {
var ipNets []*netip.Prefix
for idx, ip := range ips {
_, ipnet, err := net.ParseCIDR(ip)
ipnet, err := netip.ParsePrefix(ip)
if err != nil {
return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
}
ipNets = append(ipNets, ipnet)
ipNets = append(ipNets, &ipnet)
}
return ipNets, nil
@ -741,7 +741,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R
IPv6: cfg.IPv6,
EnhancedMode: cfg.EnhancedMode,
FallbackFilter: FallbackFilter{
IPCIDR: []*net.IPNet{},
IPCIDR: []*netip.Prefix{},
GeoSite: []*router.DomainMatcher{},
},
}

View File

@ -3,6 +3,7 @@ package constant
import (
"encoding/json"
"net"
"net/netip"
"strconv"
)
@ -74,8 +75,8 @@ func (t Type) MarshalJSON() ([]byte, error) {
type Metadata struct {
NetWork NetWork `json:"network"`
Type Type `json:"type"`
SrcIP net.IP `json:"sourceIP"`
DstIP net.IP `json:"destinationIP"`
SrcIP netip.Addr `json:"sourceIP"`
DstIP netip.Addr `json:"destinationIP"`
SrcPort string `json:"sourcePort"`
DstPort string `json:"destinationPort"`
AddrType int `json:"-"`
@ -87,7 +88,7 @@ type Metadata struct {
}
func (m *Metadata) RemoteAddress() string {
if m.DstIP != nil {
if m.DstIP.IsValid() {
return net.JoinHostPort(m.DstIP.String(), m.DstPort)
} else {
return net.JoinHostPort(m.String(), m.DstPort)
@ -99,33 +100,33 @@ func (m *Metadata) SourceAddress() string {
}
func (m *Metadata) Resolved() bool {
return m.DstIP != nil
return m.DstIP.IsValid()
}
// Pure is used to solve unexpected behavior
// when dialing proxy connection in DNSMapping mode.
func (m *Metadata) Pure() *Metadata {
if m.DNSMode == DNSMapping && m.DstIP != nil {
copy := *m
copy.Host = ""
if copy.DstIP.To4() != nil {
copy.AddrType = AtypIPv4
if m.DNSMode == DNSMapping && m.DstIP.IsValid() {
copyM := *m
copyM.Host = ""
if copyM.DstIP.Is4() {
copyM.AddrType = AtypIPv4
} else {
copy.AddrType = AtypIPv6
copyM.AddrType = AtypIPv6
}
return &copy
return &copyM
}
return m
}
func (m *Metadata) UDPAddr() *net.UDPAddr {
if m.NetWork != UDP || m.DstIP == nil {
if m.NetWork != UDP || !m.DstIP.IsValid() {
return nil
}
port, _ := strconv.ParseUint(m.DstPort, 10, 16)
return &net.UDPAddr{
IP: m.DstIP,
IP: m.DstIP.AsSlice(),
Port: int(port),
}
}
@ -133,7 +134,7 @@ func (m *Metadata) UDPAddr() *net.UDPAddr {
func (m *Metadata) String() string {
if m.Host != "" {
return m.Host
} else if m.DstIP != nil {
} else if m.DstIP.IsValid() {
return m.DstIP.String()
} else {
return "<nil>"
@ -141,5 +142,5 @@ func (m *Metadata) String() string {
}
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
import (
"net"
"net/netip"
"strings"
"github.com/Dreamacro/clash/component/geodata/router"
@ -9,7 +9,7 @@ import (
type RuleExtra struct {
Network NetWork
SourceIPs []*net.IPNet
SourceIPs []*netip.Prefix
ProcessNames []string
}
@ -17,7 +17,7 @@ func (re *RuleExtra) NotMatchNetwork(network NetWork) bool {
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 {
return false
}

View File

@ -5,6 +5,7 @@ import (
"crypto/tls"
"fmt"
"net"
"net/netip"
"strings"
"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) {
var (
ip net.IP
ip netip.Addr
err error
)
if ip = net.ParseIP(c.host); ip == nil {
if ip, err = netip.ParseAddr(c.host); err != nil {
if c.r == nil {
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
} else {
@ -62,7 +63,9 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
if err != nil {
return nil, err
}
defer conn.Close()
defer func() {
_ = conn.Close()
}()
// miekg/dns ExchangeContext doesn't respond to context cancel.
// this is a workaround

View File

@ -1,9 +1,9 @@
package dns
import (
"bytes"
"context"
"net"
"net/netip"
"sync"
"time"
@ -27,7 +27,7 @@ type dhcpClient struct {
ifaceInvalidate time.Time
dnsInvalidate time.Time
ifaceAddr *net.IPNet
ifaceAddr *netip.Prefix
done chan struct{}
resolver *Resolver
err error
@ -127,12 +127,12 @@ func (d *dhcpClient) invalidate() (bool, error) {
return false, err
}
addr, err := ifaceObj.PickIPv4Addr(nil)
addr, err := ifaceObj.PickIPv4Addr(netip.Addr{})
if err != nil {
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
}

View File

@ -1,7 +1,6 @@
package dns
import (
"net"
"net/netip"
"github.com/Dreamacro/clash/common/cache"
@ -23,54 +22,51 @@ func (h *ResolverEnhancer) MappingEnabled() bool {
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() {
return false
}
if pool := h.fakePool; pool != nil {
return pool.Exist(ipToAddr(ip))
return pool.Exist(ip)
}
return false
}
func (h *ResolverEnhancer) IsFakeIP(ip net.IP) 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 {
func (h *ResolverEnhancer) IsFakeIP(ip netip.Addr) bool {
if !h.FakeIPEnabled() {
return false
}
if pool := h.fakePool; pool != nil {
return pool.Broadcast() == ipToAddr(ip)
return pool.IPNet().Contains(ip) && ip != pool.Gateway() && ip != pool.Broadcast()
}
return false
}
func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
addr := ipToAddr(ip)
func (h *ResolverEnhancer) IsFakeBroadcastIP(ip netip.Addr) bool {
if !h.FakeIPEnabled() {
return false
}
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
}
}
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
}
}
@ -78,9 +74,9 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
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 {
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 {
h.fakePool.StoreState()
}

View File

@ -1,7 +1,7 @@
package dns
import (
"net"
"net/netip"
"strings"
"github.com/Dreamacro/clash/component/geodata/router"
@ -10,23 +10,23 @@ import (
)
type fallbackIPFilter interface {
Match(net.IP) bool
Match(netip.Addr) bool
}
type geoipFilter struct {
code string
}
func (gf *geoipFilter) Match(ip net.IP) bool {
record, _ := mmdb.Instance().Country(ip)
func (gf *geoipFilter) Match(ip netip.Addr) bool {
record, _ := mmdb.Instance().Country(ip.AsSlice())
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate()
}
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)
}
@ -41,7 +41,7 @@ type domainFilter struct {
func NewDomainFilter(domains []string) *domainFilter {
df := domainFilter{tree: trie.New[bool]()}
for _, domain := range domains {
df.tree.Insert(domain, true)
_ = df.tree.Insert(domain, true)
}
return &df
}

View File

@ -1,12 +1,12 @@
package dns
import (
"net"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
@ -87,21 +87,21 @@ func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware {
host := strings.TrimRight(q.Name, ".")
for _, ans := range msg.Answer {
var ip net.IP
var ip netip.Addr
var ttl uint32
switch a := ans.(type) {
case *D.A:
ip = a.A
ip = nnip.IpToAddr(a.A)
ttl = a.Hdr.Ttl
case *D.AAAA:
ip = a.AAAA
ip = nnip.IpToAddr(a.AAAA)
ttl = a.Hdr.Ttl
default:
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

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/netip"
"strings"
"time"
@ -46,8 +45,8 @@ type Resolver struct {
}
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
ch := make(chan net.IP, 1)
func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
ch := make(chan netip.Addr, 1)
go func() {
defer close(ch)
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
if !open {
return nil, resolver.ErrIPNotFound
return netip.Addr{}, resolver.ErrIPNotFound
}
return ip, nil
}
// 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)
}
// 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)
}
func (r *Resolver) shouldIPFallback(ip net.IP) bool {
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
for _, filter := range r.fallbackIPFilters {
if filter.Match(ip) {
return true
@ -101,10 +100,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
}
q := m.Question[0]
cache, expireTime, hit := r.lruCache.GetWithExpire(q.String())
cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String())
if hit {
now := time.Now()
msg = cache.Copy()
msg = cacheM.Copy()
if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch
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
}
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {
ip = net.ParseIP(host)
if ip != nil {
isIPv4 := ip.To4() != nil
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err error) {
ip, err = netip.ParseAddr(host)
if err == nil {
isIPv4 := ip.Is4()
if dnsType == D.TypeAAAA && !isIPv4 {
return ip, nil
} else if dnsType == D.TypeA && isIPv4 {
return ip, nil
} 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)
if err != nil {
return nil, err
return netip.Addr{}, err
}
ips := msgToIP(msg)
ipLength := len(ips)
if ipLength == 0 {
return nil, resolver.ErrIPNotFound
return netip.Addr{}, resolver.ErrIPNotFound
}
ip = ips[rand.Intn(ipLength)]
@ -319,7 +318,7 @@ type NameServer struct {
type FallbackFilter struct {
GeoIP bool
GeoIPCode string
IPCIDR []*net.IPNet
IPCIDR []*netip.Prefix
Domain []string
GeoSite []*router.DomainMatcher
}
@ -360,7 +359,7 @@ func NewResolver(config Config) *Resolver {
if len(config.Policy) != 0 {
r.policy = trie.New[*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"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
@ -97,37 +98,21 @@ func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
return msg
}
func msgToIP(msg *D.Msg) []net.IP {
ips := []net.IP{}
func msgToIP(msg *D.Msg) []netip.Addr {
ips := []netip.Addr{}
for _, answer := range msg.Answer {
switch ans := answer.(type) {
case *D.AAAA:
ips = append(ips, ans.AAAA)
ips = append(ips, nnip.IpToAddr(ans.AAAA))
case *D.A:
ips = append(ips, ans.A)
ips = append(ips, nnip.IpToAddr(ans.A))
}
}
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 {
net.PacketConn
rAddr net.Addr
@ -146,7 +131,7 @@ func (wpc *wrapPacketConn) RemoteAddr() net.Addr {
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]
if !ok {
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
if dstIP.To4() == nil {
if dstIP.Is6() {
addrType = C.AtypIPv6
}

6
go.mod
View File

@ -19,16 +19,16 @@ require (
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.1
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/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/time v0.0.0-20220411224347-583f2d630306
golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469
google.golang.org/protobuf v1.28.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 (

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-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-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
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-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-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As=
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
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-20220412020520-6917e582612b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b h1:zBJp2eKSoNIV6+9LO3bRhlnuK280Oyrwc6OeFIN6VzU=
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=

View File

@ -354,7 +354,7 @@ func Shutdown() {
P.Cleanup()
S.Py_Finalize()
tproxy.CleanupTProxyIPTables()
resolver.StoreFakePoolSate()
resolver.StoreFakePoolState()
log.Warnln("Clash shutting down")
}

View File

@ -10,6 +10,7 @@ import (
"os"
"strconv"
"sync"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/adapter/outbound"
@ -362,7 +363,6 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
if mitmListener.RawAddress() == addr {
return
}
outbound.MiddlemanServerAddress.Store("")
tunnel.MitmOutbound = nil
_ = mitmListener.Close()
mitmListener = nil
@ -403,7 +403,7 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
return
}
certOption.SetValidity(cert.TTL << 3)
certOption.SetValidity(time.Hour * 24 * 90)
certOption.SetOrganization("Clash ManInTheMiddle Proxy Services")
opt := &mitm.Option{
@ -418,8 +418,7 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
return
}
outbound.MiddlemanServerAddress.Store(mitmListener.Address())
tunnel.MitmOutbound = outbound.NewMitm()
tunnel.MitmOutbound = outbound.NewMitm(mitmListener.Address())
log.Infoln("Mitm proxy listening at: %s", mitmListener.Address())
}

View File

@ -13,8 +13,6 @@ import (
"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 {
return &http.Client{
Transport: &http.Transport{

View File

@ -9,11 +9,10 @@ import (
"io"
"net"
"net/http"
"strconv"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cache"
N "github.com/Dreamacro/clash/common/net"
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:
if tc, ok := c.(*net.TCPConn); ok {
_ = tc.SetKeepAlive(true)
if tcpConn, ok := c.(*net.TCPConn); ok {
_ = tcpConn.SetKeepAlive(true)
}
var conn *N.BufferedConn
@ -48,14 +47,13 @@ startOver:
readLoop:
for {
err := conn.SetDeadline(time.Now().Add(30 * time.Second)) // use SetDeadline instead of Proxy-Connection keep-alive
if err != nil {
// use SetDeadline instead of Proxy-Connection keep-alive
if err := conn.SetDeadline(time.Now().Add(30 * time.Second)); err != nil {
break readLoop
}
request, err := H.ReadRequest(conn.Reader())
if err != nil {
handleError(opt, nil, err)
break readLoop
}
@ -63,7 +61,7 @@ readLoop:
session := newSession(conn, request, response)
source = parseSourceAddress(session.request, c, source)
source = parseSourceAddress(session.request, c.RemoteAddr(), source)
session.request.RemoteAddr = source.String()
if !trusted {
@ -80,48 +78,42 @@ readLoop:
break readLoop // close connection
}
if couldBeWithManInTheMiddleAttack(session.request.URL.Host, opt) {
b := make([]byte, 1)
if _, err = session.conn.Read(b); err != nil {
handleError(opt, session, err)
break readLoop // close connection
if strings.HasSuffix(session.request.URL.Host, ":80") {
goto readLoop
}
buff := make([]byte, session.conn.(*N.BufferedConn).Buffered())
_, _ = session.conn.Read(buff)
mrc := &multiReaderConn{
Conn: session.conn,
reader: io.MultiReader(bytes.NewReader(b), bytes.NewReader(buff), session.conn),
b, err := conn.Peek(1)
if err != nil {
handleError(opt, session, err)
break readLoop // close connection
}
// TLS handshake.
if b[0] == 0x16 {
// TODO serve by generic host name maybe better?
tlsConn := tls.Server(mrc, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
// Handshake with the local client
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
}
c = tlsConn
goto startOver // hijack and decrypt tls connection
} else {
c = conn
}
// maybe it's the others encrypted connection
in <- inbound.NewHTTPS(session.request, mrc)
}
// maybe it's a http connection
goto readLoop
goto startOver
}
prepareRequest(c, session.request)
H.RemoveHopByHopHeaders(session.request.Header)
H.RemoveExtraHTTPHostPort(session.request)
// hijack api
if session.request.URL.Host == opt.ApiHost {
if session.request.URL.Hostname() == opt.ApiHost {
if err = handleApiRequest(session, opt); err != nil {
handleError(opt, session, err)
break readLoop
@ -149,7 +141,7 @@ readLoop:
session.request.RequestURI = ""
if session.request.URL.Host == "" {
session.response = session.NewErrorResponse(errors.New("invalid URL"))
session.response = session.NewErrorResponse(ErrInvalidURL)
} else {
client = newClientBySourceAndUserAgentIfNil(client, session.request, source, in)
@ -160,7 +152,6 @@ readLoop:
handleError(opt, session, err)
session.response = session.NewErrorResponse(err)
if errors.Is(err, ErrCertUnsupported) || strings.Contains(err.Error(), "x509: ") {
// TODO block unsupported host?
_ = writeResponse(session, false)
break readLoop
}
@ -202,9 +193,7 @@ func writeResponse(session *Session, keepAlive bool) error {
session.response.Header.Set("Keep-Alive", "timeout=25")
}
// session.response.Close = !keepAlive // let handler do it
return session.response.Write(session.conn)
return session.writeResponse()
}
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.ContentLength = int64(len(b))
return session.response.Write(session.conn)
return session.writeResponse()
}
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.ContentLength = int64(len(b))
return session.response.Write(session.conn)
return session.writeResponse()
}
func handleError(opt *Option, session *Session, err error) {
@ -287,53 +276,29 @@ func prepareRequest(conn net.Conn, request *http.Request) {
if request.Header.Get("Accept-Encoding") != "" {
request.Header.Set("Accept-Encoding", "gzip")
}
H.RemoveHopByHopHeaders(request.Header)
H.RemoveExtraHTTPHostPort(request)
}
func couldBeWithManInTheMiddleAttack(hostname string, opt *Option) bool {
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 {
func parseSourceAddress(req *http.Request, connSource, source net.Addr) net.Addr {
if source != nil {
return source
}
sourceAddress := req.Header.Get("Origin-Request-Source-Address")
if sourceAddress == "" {
return c.RemoteAddr()
return connSource
}
req.Header.Del("Origin-Request-Source-Address")
host, port, err := net.SplitHostPort(sourceAddress)
addrPort, err := netip.ParseAddrPort(sourceAddress)
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{
IP: ip,
Port: int(p),
IP: addrPort.Addr().AsSlice(),
Port: int(addrPort.Port()),
}
}
return c.RemoteAddr()
}
func newClientBySourceAndUserAgentIfNil(cli *http.Client, req *http.Request, source net.Addr, in chan<- C.ConnContext) *http.Client {

View File

@ -39,6 +39,13 @@ func (s *Session) NewErrorResponse(err error) *http.Response {
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 {
return &Session{
conn: conn,

View File

@ -3,10 +3,10 @@ package mitm
import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"time"
@ -14,14 +14,11 @@ import (
"golang.org/x/text/transform"
)
type multiReaderConn struct {
net.Conn
reader io.Reader
}
func (c *multiReaderConn) Read(buf []byte) (int, error) {
return c.reader.Read(buf)
}
var (
ErrCertUnsupported = errors.New("tls: client cert unsupported")
ErrInvalidResponse = errors.New("invalid response")
ErrInvalidURL = errors.New("invalid URL")
)
func NewResponse(code int, body io.Reader, req *http.Request) *http.Response {
if body == nil {

View File

@ -6,6 +6,7 @@ import (
"net/netip"
"time"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/device/tun"
"github.com/Dreamacro/clash/log"
@ -226,7 +227,7 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add
continue
}
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))
log.Infoln("[TUN] cleaning up stale address %s from interface %s", prefix.String(), iface.FriendlyName())
_ = iface.LUID.DeleteIPAddress(prefix)
@ -260,7 +261,7 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
}
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 {
return ifname, nil

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
@ -33,20 +34,22 @@ func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
Zone: "",
}
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", addrPort.String())
defer tunConn.Close()
buf := pool.Get(pool.UDPBufferSize)
defer pool.Put(buf)
defer func() {
_ = pool.Put(buf)
_ = tunConn.Close()
}()
for {
tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout))
if tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout)) != nil {
break
}
length := uint16(0)
if err := binary.Read(tunConn, binary.BigEndian, &length); err != nil {
@ -86,8 +89,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
Zone: "",
}
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
addrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
target := socks5.ParseAddrToSocksAddr(rAddr)
go func() {
@ -96,7 +98,7 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
n, addr, err := tunConn.ReadFrom(buf)
if err != nil {
pool.Put(buf)
_ = pool.Put(buf)
break
}
@ -104,7 +106,9 @@ func (gh *GVHandler) HandleUDP(tunConn adapter.UDPConn) {
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
go func() {
defer pool.Put(buf)
defer func() {
_ = pool.Put(buf)
}()
msg, err1 := D.RelayDnsPacket(payload)
if err1 != nil {

View File

@ -13,8 +13,8 @@ type StackListener struct {
udp *nat.UDP
}
func StartListener(device io.ReadWriteCloser, gateway netip.Addr, portal netip.Addr) (*StackListener, error) {
tcp, udp, err := nat.Start(device, gateway, portal)
func StartListener(device io.ReadWriteCloser, gateway, portal, broadcast netip.Addr) (*StackListener, error) {
tcp, udp, err := nat.Start(device, gateway, portal, broadcast)
if err != nil {
return nil, err
}

View File

@ -9,11 +9,7 @@ import (
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
)
func Start(
device io.ReadWriter,
gateway netip.Addr,
portal netip.Addr,
) (*TCP, *UDP, error) {
func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *UDP, error) {
if !portal.Is4() || !gateway.Is4() {
return nil, nil, net.InvalidAddrError("only ipv4 supported")
}
@ -37,8 +33,10 @@ func Start(
gatewayPort := uint16(listener.Addr().(*net.TCPAddr).Port)
go func() {
defer tcp.Close()
defer udp.Close()
defer func() {
_ = tcp.Close()
_ = udp.Close()
}()
buf := make([]byte, pool.RelayBufferSize)
@ -72,7 +70,7 @@ func Start(
continue
}
if ipv4.Offset() != 0 {
if ipv4.FragmentOffset() != 0 {
continue
}
@ -92,6 +90,12 @@ func Start(
continue
}
destinationIP := ip.DestinationIP()
if !destinationIP.IsGlobalUnicast() || destinationIP == broadcast {
continue
}
switch ip.Protocol() {
case tcpip.TCP:
t := tcpip.TCPPacket(ip.Payload())
@ -99,7 +103,7 @@ func Start(
continue
}
if ip.DestinationIP() == portal {
if destinationIP == portal {
if ip.SourceIP() == gateway && t.SourcePort() == gatewayPort {
tup := tab.tupleOf(t.DestinationPort())
if tup == zeroTuple {
@ -120,7 +124,7 @@ func Start(
} else {
tup := tuple{
SourceAddr: netip.AddrPortFrom(ip.SourceIP(), t.SourcePort()),
DestinationAddr: netip.AddrPortFrom(ip.DestinationIP(), t.DestinationPort()),
DestinationAddr: netip.AddrPortFrom(destinationIP, t.DestinationPort()),
}
port := tab.portOf(tup)
@ -158,10 +162,8 @@ func Start(
i.SetType(tcpip.ICMPTypePingResponse)
source := ip.SourceIP()
destination := ip.DestinationIP()
ip.SetSourceIP(destination)
ip.SetDestinationIP(source)
ip.SetDestinationIP(ip.SourceIP())
ip.SetSourceIP(destinationIP)
ip.ResetChecksum()
i.ResetChecksum()
@ -176,10 +178,8 @@ func Start(
i.SetType(tcpip.ICMPv6EchoReply)
source := ip.SourceIP()
destination := ip.DestinationIP()
ip.SetSourceIP(destination)
ip.SetDestinationIP(source)
ip.SetDestinationIP(ip.SourceIP())
ip.SetSourceIP(destinationIP)
ip.ResetChecksum()
i.ResetChecksum(ip.PseudoSum())

View File

@ -7,6 +7,7 @@ import (
"net/netip"
"sync"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
)
@ -71,11 +72,8 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
return 0, net.InvalidAddrError("invalid addr")
}
srcIP, _ := netip.AddrFromSlice(srcAddr.IP)
dstIp, _ := netip.AddrFromSlice(dstAddr.IP)
srcAddrPort := netip.AddrPortFrom(srcIP.Unmap(), uint16(srcAddr.Port))
dstAddrPort := netip.AddrPortFrom(dstIp.Unmap(), uint16(dstAddr.Port))
srcAddrPort := netip.AddrPortFrom(nnip.IpToAddr(srcAddr.IP), uint16(srcAddr.Port))
dstAddrPort := netip.AddrPortFrom(nnip.IpToAddr(dstAddr.IP), uint16(dstAddr.Port))
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
return 0, net.InvalidAddrError("invalid ip version")

View File

@ -118,12 +118,6 @@ func (p IPv4Packet) SetFlags(flags byte) {
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 {
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
}

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
@ -47,9 +48,10 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
var (
gateway = tunAddress.Masked().Addr().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 {
_ = device.Close()
@ -81,24 +83,27 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
lAddr := conn.LocalAddr().(*net.TCPAddr)
rAddr := conn.RemoteAddr().(*net.TCPAddr)
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() {
_ = conn.Close()
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
defer func(conn net.Conn) {
_ = conn.Close()
}(conn)
buf := pool.Get(pool.UDPBufferSize)
defer func(buf []byte) {
defer func() {
_ = pool.Put(buf)
}(buf)
_ = conn.Close()
}()
for {
if err = conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)); err != nil {
if conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)) != nil {
break
}
@ -131,8 +136,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.TUN,
SrcIP: lAddr.IP,
DstIP: rAddr.IP,
SrcIP: lAddrPort.Addr(),
DstIP: rAddrPort.Addr(),
SrcPort: strconv.Itoa(lAddr.Port),
DstPort: strconv.Itoa(rAddr.Port),
AddrType: C.AtypIPv4,
@ -162,8 +167,13 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
lAddr := lRAddr.(*net.UDPAddr)
rAddr := rRAddr.(*net.UDPAddr)
rAddrIp, _ := netip.AddrFromSlice(rAddr.IP)
rAddrPort := netip.AddrPortFrom(rAddrIp, uint16(rAddr.Port))
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() {
_ = pool.Put(buf)
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
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")
}
process.AppendLocalIPs(tunAddress.Masked().Addr().Next().AsSlice())
process.AppendLocalIPs(tunAddress.Masked().Addr().Next())
// open tun device
tunDevice, err = parseDevice(devName, uint32(mtu))
@ -149,7 +149,8 @@ func setAtLatest(stackType C.TUNStack, devName string) {
switch runtime.GOOS {
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":
_, _ = cmd.ExecCmd("ipconfig /renew")
case "linux":

View File

@ -2,7 +2,7 @@ package rules
import (
"errors"
"net"
"net/netip"
"strings"
C "github.com/Dreamacro/clash/constant"
@ -50,17 +50,17 @@ func findNetwork(params []string) C.NetWork {
return C.ALLNet
}
func findSourceIPs(params []string) []*net.IPNet {
var ips []*net.IPNet
func findSourceIPs(params []string) []*netip.Prefix {
var ips []*netip.Prefix
for _, p := range params {
if p == noResolve || len(p) < 7 {
continue
}
_, ipnet, err := net.ParseCIDR(p)
ipnet, err := netip.ParsePrefix(p)
if err != nil {
continue
}
ips = append(ips, ipnet)
ips = append(ips, &ipnet)
}
if len(ips) > 0 {

View File

@ -21,7 +21,7 @@ func (g *GEOIP) RuleType() C.RuleType {
func (g *GEOIP) Match(metadata *C.Metadata) bool {
ip := metadata.DstIP
if ip == nil {
if !ip.IsValid() {
return false
}
@ -34,7 +34,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool {
resolver.IsFakeBroadcastIP(ip)
}
record, _ := mmdb.Instance().Country(ip)
record, _ := mmdb.Instance().Country(ip.AsSlice())
return strings.EqualFold(record.Country.IsoCode, g.country)
}

View File

@ -1,7 +1,7 @@
package rules
import (
"net"
"net/netip"
C "github.com/Dreamacro/clash/constant"
)
@ -22,7 +22,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
type IPCIDR struct {
*Base
ipnet *net.IPNet
ipnet *netip.Prefix
adapter string
isSourceIP bool
noResolveIP bool
@ -40,7 +40,7 @@ func (i *IPCIDR) Match(metadata *C.Metadata) bool {
if i.isSourceIP {
ip = metadata.SrcIP
}
return ip != nil && i.ipnet.Contains(ip)
return ip.IsValid() && i.ipnet.Contains(ip)
}
func (i *IPCIDR) Adapter() string {
@ -56,14 +56,14 @@ func (i *IPCIDR) ShouldResolveIP() bool {
}
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
_, ipnet, err := net.ParseCIDR(s)
ipnet, err := netip.ParsePrefix(s)
if err != nil {
return nil, errPayload
}
ipcidr := &IPCIDR{
Base: &Base{},
ipnet: ipnet,
ipnet: &ipnet,
adapter: adapter,
}

View File

@ -8,7 +8,7 @@ require (
github.com/docker/go-connections v0.4.0
github.com/miekg/dns v1.1.48
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 => ../
@ -42,7 +42,7 @@ require (
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/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/time v0.0.0-20220411224347-583f2d630306 // 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.v3 v3.0.0-20210107192922-496545a6307b // 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-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-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
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-20190226205417-e64efc72b421/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-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-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As=
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
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-20201126162022-7de9c90e9dd1/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.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
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-20220412020520-6917e582612b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
gvisor.dev/gvisor v0.0.0-20220419020849-1f2f4462d45b h1:zBJp2eKSoNIV6+9LO3bRhlnuK280Oyrwc6OeFIN6VzU=
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-20190106161140-3f1c8253044a/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"
"io"
"net"
"net/netip"
"strconv"
"github.com/Dreamacro/clash/component/auth"
@ -40,6 +41,8 @@ var (
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) {
var req [8]byte
if _, err = io.ReadFull(rw, req[:]); err != nil {
@ -57,7 +60,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
}
var (
dstIP = req[4:8] // [4]byte
dstIP = netip.AddrFrom4(*(*[4]byte)(req[4:8])) // [4]byte
dstPort = req[2:4] // [2]byte
)
@ -83,7 +86,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
if host != "" {
addr = net.JoinHostPort(host, port)
} else {
addr = net.JoinHostPort(net.IP(dstIP).String(), port)
addr = net.JoinHostPort(dstIP.String(), port)
}
// SOCKS4 only support USERID auth.
@ -97,7 +100,7 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
var reply [8]byte
reply[0] = 0x00 // reply code
reply[1] = code // result code
copy(reply[4:8], dstIP)
copy(reply[4:8], dstIP.AsSlice())
copy(reply[2:4], dstPort)
_, wErr := rw.Write(reply[:])
@ -118,20 +121,18 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
return err
}
ip := net.ParseIP(host)
if ip == nil /* HOST */ {
ip = net.IPv4(0, 0, 0, 1).To4()
} else if ip.To4() == nil /* IPv6 */ {
dstIP, err := netip.ParseAddr(host)
if err != nil /* HOST */ {
dstIP = netip.AddrFrom4([4]byte{0, 0, 0, 1})
} else if dstIP.Is6() /* IPv6 */ {
return errIPv6NotSupported
}
dstIP := ip.To4()
req := &bytes.Buffer{}
req.WriteByte(Version)
req.WriteByte(command)
binary.Write(req, binary.BigEndian, uint16(port))
req.Write(dstIP)
_ = binary.Write(req, binary.BigEndian, uint16(port))
req.Write(dstIP.AsSlice())
req.WriteString(userID)
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
// as a destination IP address and thus should never occur if the client
// can resolve the domain name.)
func isReservedIP(ip net.IP) bool {
subnet := net.IPNet{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
}
func isReservedIP(ip netip.Addr) bool {
return !ip.IsUnspecified() && subnet.Contains(ip)
}

View File

@ -16,6 +16,7 @@ type sniffing struct {
metadata *C.Metadata
totalWrite *atomic.Uint64
allowBreak bool
}
func (r *sniffing) Read(b []byte) (int, error) {
@ -30,8 +31,12 @@ func (r *sniffing) Write(b []byte) (int, error) {
} else {
resolver.InsertHostByIP(r.metadata.DstIP, header.Domain())
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")
} else {
r.metadata.Host = header.Domain()
}
}
}
@ -45,10 +50,11 @@ func (r *sniffing) Close() error {
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{
Conn: conn,
metadata: metadata,
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)
return NewSniffing(t, metadata)
return NewSniffing(t, metadata, rule)
}
type udpTracker struct {

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"path/filepath"
"runtime"
"strconv"
@ -132,15 +133,15 @@ func process() {
}
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 {
// 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.Host = ""
if ip.To4() != nil {
if ip.Is4() {
metadata.AddrType = C.AtypIPv4
} else {
metadata.AddrType = C.AtypIPv6
@ -155,11 +156,11 @@ func preHandleMetadata(metadata *C.Metadata) error {
metadata.AddrType = C.AtypDomainName
metadata.DNSMode = C.DNSMapping
if resolver.FakeIPEnabled() {
metadata.DstIP = nil
metadata.DstIP = netip.Addr{}
metadata.DNSMode = C.DNSFakeIP
} else if node := resolver.DefaultHosts.Search(host); node != nil {
// redir-host should lookup the hosts
metadata.DstIP = node.Data.AsSlice()
metadata.DstIP = node.Data
}
} else if resolver.IsFakeIP(metadata.DstIP) {
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
@ -308,7 +309,7 @@ func handleTCPConn(connCtx C.ConnContext) {
defer cancel()
if MitmOutbound != nil && metadata.Type != C.MITM {
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) {
_ = remoteConn.Close()
}(remoteConn)
@ -355,7 +356,7 @@ func handleTCPConn(connCtx C.ConnContext) {
}
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) {
@ -365,7 +366,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
var resolved bool
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
metadata.DstIP = node.Data.AsSlice()
metadata.DstIP = node.Data
resolved = true
}
@ -419,7 +420,7 @@ func matchScript(metadata *C.Metadata) (C.Proxy, error) {
defer configMux.RUnlock()
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
metadata.DstIP = node.Data.AsSlice()
metadata.DstIP = node.Data
}
adapter, err := S.CallPyMainFunction(metadata)