mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-19 00:30:58 +00:00
Merge branch 'Alpha' into Meta
This commit is contained in:
commit
4277dc6eab
92 changed files with 1696 additions and 948 deletions
69
.github/workflows/build.yml
vendored
69
.github/workflows/build.yml
vendored
|
@ -18,7 +18,7 @@ on:
|
|||
branches:
|
||||
- Alpha
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
|
@ -206,6 +206,10 @@ jobs:
|
|||
sudo apt-get install dpkg
|
||||
if [ "${{matrix.jobs.abi}}" = "1" ]; then
|
||||
ARCH=loongarch64
|
||||
elif [ "${{matrix.jobs.goarm}}" = "7" ]; then
|
||||
ARCH=armhf
|
||||
elif [ "${{matrix.jobs.goarch}}" = "arm" ]; then
|
||||
ARCH=armel
|
||||
else
|
||||
ARCH=${{matrix.jobs.goarch}}
|
||||
fi
|
||||
|
@ -270,7 +274,7 @@ jobs:
|
|||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}
|
||||
name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}"
|
||||
path: |
|
||||
mihomo*.gz
|
||||
mihomo*.deb
|
||||
|
@ -415,35 +419,35 @@ jobs:
|
|||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Set Docker tags and labels based on trigger
|
||||
id: set-meta
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "tags=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
echo "labels=org.opencontainers.image.version=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV
|
||||
echo "labels=${{ steps.meta.outputs.labels }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
id: meta_alpha
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}
|
||||
tags: ${{ env.tags }}
|
||||
labels: ${{ env.labels }}
|
||||
|
||||
|
||||
images: '${{ env.REGISTRY }}/${{ github.repository }}'
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
|
||||
id: meta_release
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: '${{ env.REGISTRY }}/${{ github.repository }}'
|
||||
tags: |
|
||||
${{ github.event.inputs.version }}
|
||||
flavor: |
|
||||
latest=true
|
||||
labels: org.opencontainers.image.version=${{ github.event.inputs.version }}
|
||||
|
||||
- name: Show files
|
||||
run: |
|
||||
ls .
|
||||
ls bin/
|
||||
|
||||
|
||||
- name: login to docker REGISTRY
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
@ -454,7 +458,7 @@ jobs:
|
|||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
|
@ -465,5 +469,20 @@ jobs:
|
|||
linux/amd64
|
||||
linux/arm64
|
||||
linux/arm/v7
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ${{ steps.meta_alpha.outputs.tags }}
|
||||
labels: ${{ steps.meta_alpha.outputs.labels }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: |
|
||||
linux/386
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
linux/arm/v7
|
||||
tags: ${{ steps.meta_release.outputs.tags }}
|
||||
labels: ${{ steps.meta_release.outputs.labels }}
|
|
@ -3,22 +3,10 @@ package inbound
|
|||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
var (
|
||||
lc = tfo.ListenConfig{
|
||||
DisableTFO: true,
|
||||
}
|
||||
)
|
||||
|
||||
func SetTfo(open bool) {
|
||||
lc.DisableTFO = !open
|
||||
}
|
||||
|
||||
func SetMPTCP(open bool) {
|
||||
setMultiPathTCP(&lc.ListenConfig, open)
|
||||
setMultiPathTCP(getListenConfig(), open)
|
||||
}
|
||||
|
||||
func ListenContext(ctx context.Context, network, address string) (net.Listener, error) {
|
||||
|
|
23
adapter/inbound/listen_unix.go
Normal file
23
adapter/inbound/listen_unix.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
//go:build unix
|
||||
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
var (
|
||||
lc = tfo.ListenConfig{
|
||||
DisableTFO: true,
|
||||
}
|
||||
)
|
||||
|
||||
func SetTfo(open bool) {
|
||||
lc.DisableTFO = !open
|
||||
}
|
||||
|
||||
func getListenConfig() *net.ListenConfig {
|
||||
return &lc.ListenConfig
|
||||
}
|
15
adapter/inbound/listen_windows.go
Normal file
15
adapter/inbound/listen_windows.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
lc = net.ListenConfig{}
|
||||
)
|
||||
|
||||
func SetTfo(open bool) {}
|
||||
|
||||
func getListenConfig() *net.ListenConfig {
|
||||
return &lc
|
||||
}
|
|
@ -3,15 +3,19 @@ package outbound
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/loopback"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
)
|
||||
|
||||
var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR"))
|
||||
|
||||
type Direct struct {
|
||||
*Base
|
||||
loopBack *loopback.Detector
|
||||
|
@ -24,8 +28,10 @@ type DirectOption struct {
|
|||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
if err := d.loopBack.CheckConn(metadata); err != nil {
|
||||
return nil, err
|
||||
if !features.CMFA && !DisableLoopBackDetector {
|
||||
if err := d.loopBack.CheckConn(metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
|
||||
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
||||
|
@ -38,8 +44,10 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
|||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
||||
return nil, err
|
||||
if !features.CMFA && !DisableLoopBackDetector {
|
||||
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
||||
if !metadata.Resolved() {
|
||||
|
@ -49,13 +57,17 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
|||
}
|
||||
metadata.DstIP = ip
|
||||
}
|
||||
pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||
pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", metadata.AddrPort())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
|
||||
}
|
||||
|
||||
func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool {
|
||||
return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver
|
||||
}
|
||||
|
||||
func NewDirectWithOption(option DirectOption) *Direct {
|
||||
return &Direct{
|
||||
Base: &Base{
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
|
||||
"github.com/metacubex/sing-quic/hysteria2"
|
||||
|
||||
"github.com/metacubex/randv2"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -165,7 +165,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
|
|||
})
|
||||
if len(serverAddress) > 0 {
|
||||
clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) {
|
||||
return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion))
|
||||
return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[randv2.IntN(len(serverAddress))], C.NewDNSPrefer(option.IPVersion))
|
||||
}
|
||||
|
||||
if option.HopInterval == 0 {
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
@ -180,10 +180,10 @@ func NewSsh(option SshOption) (*Ssh, error) {
|
|||
}
|
||||
|
||||
version := "SSH-2.0-OpenSSH_"
|
||||
if fastrand.Intn(2) == 0 {
|
||||
version += "7." + strconv.Itoa(fastrand.Intn(10))
|
||||
if randv2.IntN(2) == 0 {
|
||||
version += "7." + strconv.Itoa(randv2.IntN(10))
|
||||
} else {
|
||||
version += "8." + strconv.Itoa(fastrand.Intn(9))
|
||||
version += "8." + strconv.Itoa(randv2.IntN(9))
|
||||
}
|
||||
config.ClientVersion = version
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package outbound
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/transport/gun"
|
||||
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
||||
"github.com/metacubex/mihomo/transport/trojan"
|
||||
)
|
||||
|
||||
|
@ -29,6 +31,8 @@ type Trojan struct {
|
|||
transport *gun.TransportWrap
|
||||
|
||||
realityConfig *tlsC.RealityConfig
|
||||
|
||||
ssCipher core.Cipher
|
||||
}
|
||||
|
||||
type TrojanOption struct {
|
||||
|
@ -46,9 +50,17 @@ type TrojanOption struct {
|
|||
RealityOpts RealityOptions `proxy:"reality-opts,omitempty"`
|
||||
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
||||
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
|
||||
SSOpts TrojanSSOption `proxy:"ss-opts,omitempty"`
|
||||
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
|
||||
}
|
||||
|
||||
// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
|
||||
type TrojanSSOption struct {
|
||||
Enabled bool `proxy:"enabled,omitempty"`
|
||||
Method string `proxy:"method,omitempty"`
|
||||
Password string `proxy:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) {
|
||||
if t.option.Network == "ws" {
|
||||
host, port, _ := net.SplitHostPort(t.addr)
|
||||
|
@ -95,6 +107,10 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.
|
|||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||
}
|
||||
|
||||
if t.ssCipher != nil {
|
||||
c = t.ssCipher.StreamConn(c)
|
||||
}
|
||||
|
||||
if metadata.NetWork == C.UDP {
|
||||
err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
|
||||
return c, err
|
||||
|
@ -112,6 +128,10 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if t.ssCipher != nil {
|
||||
c = t.ssCipher.StreamConn(c)
|
||||
}
|
||||
|
||||
if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
|
@ -161,6 +181,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
|||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
if t.ssCipher != nil {
|
||||
c = t.ssCipher.StreamConn(c)
|
||||
}
|
||||
|
||||
err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -193,6 +218,10 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
|
|||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||
}
|
||||
|
||||
if t.ssCipher != nil {
|
||||
c = t.ssCipher.StreamConn(c)
|
||||
}
|
||||
|
||||
err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -257,6 +286,20 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
|||
}
|
||||
tOption.Reality = t.realityConfig
|
||||
|
||||
if option.SSOpts.Enabled {
|
||||
if option.SSOpts.Password == "" {
|
||||
return nil, errors.New("empty password")
|
||||
}
|
||||
if option.SSOpts.Method == "" {
|
||||
option.SSOpts.Method = "AES-128-GCM"
|
||||
}
|
||||
ciph, err := core.PickCipher(option.SSOpts.Method, nil, option.SSOpts.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.ssCipher = ciph
|
||||
}
|
||||
|
||||
if option.Network == "grpc" {
|
||||
dialFn := func(network, addr string) (net.Conn, error) {
|
||||
var err error
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
|
@ -48,6 +49,10 @@ type WireGuard struct {
|
|||
connectAddr M.Socksaddr
|
||||
localPrefixes []netip.Prefix
|
||||
|
||||
serverAddrMap map[M.Socksaddr]netip.AddrPort
|
||||
serverAddrTime atomic.TypedValue[time.Time]
|
||||
serverAddrMutex sync.Mutex
|
||||
|
||||
closeCh chan struct{} // for test
|
||||
}
|
||||
|
||||
|
@ -67,6 +72,8 @@ type WireGuardOption struct {
|
|||
|
||||
RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"`
|
||||
Dns []string `proxy:"dns,omitempty"`
|
||||
|
||||
RefreshServerIPInterval int `proxy:"refresh-server-ip-interval,omitempty"`
|
||||
}
|
||||
|
||||
type WireGuardPeerOption struct {
|
||||
|
@ -287,6 +294,15 @@ func (w *WireGuard) resolve(ctx context.Context, address M.Socksaddr) (netip.Add
|
|||
}
|
||||
|
||||
func (w *WireGuard) init(ctx context.Context) error {
|
||||
err := w.init0(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.updateServerAddr(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WireGuard) init0(ctx context.Context) error {
|
||||
if w.initOk.Load() {
|
||||
return nil
|
||||
}
|
||||
|
@ -301,41 +317,118 @@ func (w *WireGuard) init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
w.bind.ResetReservedForEndpoint()
|
||||
ipcConf := "private_key=" + w.option.PrivateKey
|
||||
w.serverAddrMap = make(map[M.Socksaddr]netip.AddrPort)
|
||||
ipcConf, err := w.genIpcConf(ctx, false)
|
||||
if err != nil {
|
||||
// !!! do not set initErr here !!!
|
||||
// let us can retry domain resolve in next time
|
||||
return err
|
||||
}
|
||||
|
||||
if debug.Enabled {
|
||||
log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf))
|
||||
}
|
||||
err = w.device.IpcSet(ipcConf)
|
||||
if err != nil {
|
||||
w.initErr = E.Cause(err, "setup wireguard")
|
||||
return w.initErr
|
||||
}
|
||||
w.serverAddrTime.Store(time.Now())
|
||||
|
||||
err = w.tunDevice.Start()
|
||||
if err != nil {
|
||||
w.initErr = err
|
||||
return w.initErr
|
||||
}
|
||||
|
||||
w.initOk.Store(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WireGuard) updateServerAddr(ctx context.Context) {
|
||||
if w.option.RefreshServerIPInterval != 0 && time.Since(w.serverAddrTime.Load()) > time.Second*time.Duration(w.option.RefreshServerIPInterval) {
|
||||
if w.serverAddrMutex.TryLock() {
|
||||
defer w.serverAddrMutex.Unlock()
|
||||
ipcConf, err := w.genIpcConf(ctx, true)
|
||||
if err != nil {
|
||||
log.Warnln("[WG](%s)UpdateServerAddr failed to generate wireguard ipc conf: %s", w.option.Name, err)
|
||||
return
|
||||
}
|
||||
err = w.device.IpcSet(ipcConf)
|
||||
if err != nil {
|
||||
log.Warnln("[WG](%s)UpdateServerAddr failed to update wireguard ipc conf: %s", w.option.Name, err)
|
||||
return
|
||||
}
|
||||
w.serverAddrTime.Store(time.Now())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WireGuard) genIpcConf(ctx context.Context, updateOnly bool) (string, error) {
|
||||
ipcConf := ""
|
||||
if !updateOnly {
|
||||
ipcConf += "private_key=" + w.option.PrivateKey + "\n"
|
||||
}
|
||||
if len(w.option.Peers) > 0 {
|
||||
for i, peer := range w.option.Peers {
|
||||
destination, err := w.resolve(ctx, peer.Addr())
|
||||
peerAddr := peer.Addr()
|
||||
destination, err := w.resolve(ctx, peerAddr)
|
||||
if err != nil {
|
||||
// !!! do not set initErr here !!!
|
||||
// let us can retry domain resolve in next time
|
||||
return E.Cause(err, "resolve endpoint domain for peer ", i)
|
||||
return "", E.Cause(err, "resolve endpoint domain for peer ", i)
|
||||
}
|
||||
ipcConf += "\npublic_key=" + peer.PublicKey
|
||||
ipcConf += "\nendpoint=" + destination.String()
|
||||
if peer.PreSharedKey != "" {
|
||||
ipcConf += "\npreshared_key=" + peer.PreSharedKey
|
||||
if w.serverAddrMap[peerAddr] != destination {
|
||||
w.serverAddrMap[peerAddr] = destination
|
||||
} else if updateOnly {
|
||||
continue
|
||||
}
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
ipcConf += "\nallowed_ip=" + allowedIP
|
||||
|
||||
if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true
|
||||
w.bind.SetConnectAddr(destination)
|
||||
}
|
||||
ipcConf += "public_key=" + peer.PublicKey + "\n"
|
||||
if updateOnly {
|
||||
ipcConf += "update_only=true\n"
|
||||
}
|
||||
ipcConf += "endpoint=" + destination.String() + "\n"
|
||||
if len(peer.Reserved) > 0 {
|
||||
var reserved [3]uint8
|
||||
copy(reserved[:], w.option.Reserved)
|
||||
w.bind.SetReservedForEndpoint(destination, reserved)
|
||||
}
|
||||
if updateOnly {
|
||||
continue
|
||||
}
|
||||
if peer.PreSharedKey != "" {
|
||||
ipcConf += "preshared_key=" + peer.PreSharedKey + "\n"
|
||||
}
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
ipcConf += "allowed_ip=" + allowedIP + "\n"
|
||||
}
|
||||
if w.option.PersistentKeepalive != 0 {
|
||||
ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ipcConf += "\npublic_key=" + w.option.PublicKey
|
||||
destination, err := w.resolve(ctx, w.connectAddr)
|
||||
if err != nil {
|
||||
// !!! do not set initErr here !!!
|
||||
// let us can retry domain resolve in next time
|
||||
return E.Cause(err, "resolve endpoint domain")
|
||||
return "", E.Cause(err, "resolve endpoint domain")
|
||||
}
|
||||
if w.serverAddrMap[w.connectAddr] != destination {
|
||||
w.serverAddrMap[w.connectAddr] = destination
|
||||
} else if updateOnly {
|
||||
return "", nil
|
||||
}
|
||||
w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true
|
||||
ipcConf += "public_key=" + w.option.PublicKey + "\n"
|
||||
if updateOnly {
|
||||
ipcConf += "update_only=true\n"
|
||||
}
|
||||
ipcConf += "endpoint=" + destination.String() + "\n"
|
||||
if updateOnly {
|
||||
return ipcConf, nil
|
||||
}
|
||||
w.bind.SetConnectAddr(destination)
|
||||
ipcConf += "\nendpoint=" + destination.String()
|
||||
if w.option.PreSharedKey != "" {
|
||||
ipcConf += "\npreshared_key=" + w.option.PreSharedKey
|
||||
ipcConf += "preshared_key=" + w.option.PreSharedKey + "\n"
|
||||
}
|
||||
var has4, has6 bool
|
||||
for _, address := range w.localPrefixes {
|
||||
|
@ -346,34 +439,17 @@ func (w *WireGuard) init(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
if has4 {
|
||||
ipcConf += "\nallowed_ip=0.0.0.0/0"
|
||||
ipcConf += "allowed_ip=0.0.0.0/0\n"
|
||||
}
|
||||
if has6 {
|
||||
ipcConf += "\nallowed_ip=::/0"
|
||||
ipcConf += "allowed_ip=::/0\n"
|
||||
}
|
||||
|
||||
if w.option.PersistentKeepalive != 0 {
|
||||
ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive)
|
||||
}
|
||||
}
|
||||
|
||||
if w.option.PersistentKeepalive != 0 {
|
||||
ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", w.option.PersistentKeepalive)
|
||||
}
|
||||
|
||||
if debug.Enabled {
|
||||
log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf))
|
||||
}
|
||||
err := w.device.IpcSet(ipcConf)
|
||||
if err != nil {
|
||||
w.initErr = E.Cause(err, "setup wireguard")
|
||||
return w.initErr
|
||||
}
|
||||
|
||||
err = w.tunDevice.Start()
|
||||
if err != nil {
|
||||
w.initErr = err
|
||||
return w.initErr
|
||||
}
|
||||
|
||||
w.initOk.Store(true)
|
||||
return nil
|
||||
return ipcConf, nil
|
||||
}
|
||||
|
||||
func closeWireGuard(w *WireGuard) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
type Relay struct {
|
||||
|
@ -149,6 +150,7 @@ func (r *Relay) Addr() string {
|
|||
}
|
||||
|
||||
func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
|
||||
log.Warnln("The group [%s] with relay type is deprecated, please using dialer-proxy instead", option.Name)
|
||||
return &Relay{
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
outbound.BaseOption{
|
||||
|
|
|
@ -28,7 +28,10 @@ type healthCheckSchema struct {
|
|||
}
|
||||
|
||||
type OverrideSchema struct {
|
||||
TFO *bool `provider:"tfo,omitempty"`
|
||||
MPTcp *bool `provider:"mptcp,omitempty"`
|
||||
UDP *bool `provider:"udp,omitempty"`
|
||||
UDPOverTCP *bool `provider:"udp-over-tcp,omitempty"`
|
||||
Up *string `provider:"up,omitempty"`
|
||||
Down *string `provider:"down,omitempty"`
|
||||
DialerProxy *string `provider:"dialer-proxy,omitempty"`
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -373,37 +374,23 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
|
|||
mapping["dialer-proxy"] = dialerProxy
|
||||
}
|
||||
|
||||
if override.UDP != nil {
|
||||
mapping["udp"] = *override.UDP
|
||||
}
|
||||
if override.Up != nil {
|
||||
mapping["up"] = *override.Up
|
||||
}
|
||||
if override.Down != nil {
|
||||
mapping["down"] = *override.Down
|
||||
}
|
||||
if override.DialerProxy != nil {
|
||||
mapping["dialer-proxy"] = *override.DialerProxy
|
||||
}
|
||||
if override.SkipCertVerify != nil {
|
||||
mapping["skip-cert-verify"] = *override.SkipCertVerify
|
||||
}
|
||||
if override.Interface != nil {
|
||||
mapping["interface-name"] = *override.Interface
|
||||
}
|
||||
if override.RoutingMark != nil {
|
||||
mapping["routing-mark"] = *override.RoutingMark
|
||||
}
|
||||
if override.IPVersion != nil {
|
||||
mapping["ip-version"] = *override.IPVersion
|
||||
}
|
||||
if override.AdditionalPrefix != nil {
|
||||
name := mapping["name"].(string)
|
||||
mapping["name"] = *override.AdditionalPrefix + name
|
||||
}
|
||||
if override.AdditionalSuffix != nil {
|
||||
name := mapping["name"].(string)
|
||||
mapping["name"] = name + *override.AdditionalSuffix
|
||||
val := reflect.ValueOf(override)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := val.Field(i)
|
||||
if field.IsNil() {
|
||||
continue
|
||||
}
|
||||
fieldName := strings.Split(val.Type().Field(i).Tag.Get("provider"), ",")[0]
|
||||
switch fieldName {
|
||||
case "additional-prefix":
|
||||
name := mapping["name"].(string)
|
||||
mapping["name"] = *field.Interface().(*string) + name
|
||||
case "additional-suffix":
|
||||
name := mapping["name"].(string)
|
||||
mapping["name"] = name + *field.Interface().(*string)
|
||||
default:
|
||||
mapping[fieldName] = field.Elem().Interface()
|
||||
}
|
||||
}
|
||||
|
||||
proxy, err := adapter.ParseProxy(mapping)
|
||||
|
|
|
@ -333,7 +333,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||
case "ws", "httpupgrade":
|
||||
headers := make(map[string]any)
|
||||
wsOpts := make(map[string]any)
|
||||
wsOpts["path"] = []string{"/"}
|
||||
wsOpts["path"] = "/"
|
||||
if host, ok := values["host"]; ok && host != "" {
|
||||
headers["Host"] = host.(string)
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/metacubex/sing-shadowsocks/shadowimpl"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
var hostsSuffix = []string{
|
||||
|
@ -302,11 +302,11 @@ func RandHost() string {
|
|||
prefix += string(buf[6:8]) + "-"
|
||||
prefix += string(buf[len(buf)-8:])
|
||||
|
||||
return prefix + hostsSuffix[fastrand.Intn(hostsLen)]
|
||||
return prefix + hostsSuffix[randv2.IntN(hostsLen)]
|
||||
}
|
||||
|
||||
func RandUserAgent() string {
|
||||
return userAgents[fastrand.Intn(uaLen)]
|
||||
return userAgents[randv2.IntN(uaLen)]
|
||||
}
|
||||
|
||||
func SetUserAgent(header http.Header) {
|
||||
|
|
|
@ -3,8 +3,8 @@ package pool
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
func TestAllocGet(t *testing.T) {
|
||||
|
@ -43,6 +43,6 @@ func TestAllocPutThenGet(t *testing.T) {
|
|||
|
||||
func BenchmarkMSB(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
msb(fastrand.Int())
|
||||
msb(randv2.Int())
|
||||
}
|
||||
}
|
||||
|
|
50
common/utils/callback.go
Normal file
50
common/utils/callback.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
)
|
||||
|
||||
type Callback[T any] struct {
|
||||
list list.List[func(T)]
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCallback[T any]() *Callback[T] {
|
||||
return &Callback[T]{}
|
||||
}
|
||||
|
||||
func (c *Callback[T]) Register(item func(T)) io.Closer {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
element := c.list.PushBack(item)
|
||||
return &callbackCloser[T]{
|
||||
element: element,
|
||||
callback: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Callback[T]) Emit(item T) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
for element := c.list.Front(); element != nil; element = element.Next() {
|
||||
go element.Value(item)
|
||||
}
|
||||
}
|
||||
|
||||
type callbackCloser[T any] struct {
|
||||
element *list.Element[func(T)]
|
||||
callback *Callback[T]
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (c *callbackCloser[T]) Close() error {
|
||||
c.once.Do(func() {
|
||||
c.callback.mutex.Lock()
|
||||
defer c.callback.mutex.Unlock()
|
||||
c.callback.list.Remove(c.element)
|
||||
})
|
||||
return nil
|
||||
}
|
|
@ -2,19 +2,39 @@ package utils
|
|||
|
||||
import (
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type fastRandReader struct{}
|
||||
type unsafeRandReader struct{}
|
||||
|
||||
func (r fastRandReader) Read(p []byte) (int, error) {
|
||||
return fastrand.Read(p)
|
||||
func (r unsafeRandReader) Read(p []byte) (n int, err error) {
|
||||
// modify from https://github.com/golang/go/blob/587c3847da81aa7cfc3b3db2677c8586c94df13a/src/runtime/rand.go#L70-L89
|
||||
// Inspired by wyrand.
|
||||
n = len(p)
|
||||
v := randv2.Uint64()
|
||||
for len(p) > 0 {
|
||||
v ^= 0xa0761d6478bd642f
|
||||
v *= 0xe7037ed1a0b428db
|
||||
size := 8
|
||||
if len(p) < 8 {
|
||||
size = len(p)
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
p[i] ^= byte(v >> (8 * i))
|
||||
}
|
||||
p = p[size:]
|
||||
v = v>>32 | v<<32
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{}))
|
||||
var UnsafeRandReader = unsafeRandReader{}
|
||||
|
||||
var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(UnsafeRandReader))
|
||||
|
||||
func NewUUIDV1() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV1() // fastrand.Read wouldn't cause error, so ignore err is safe
|
||||
u, _ := UnsafeUUIDGenerator.NewV1() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
|
@ -23,7 +43,7 @@ func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID {
|
|||
}
|
||||
|
||||
func NewUUIDV4() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV4() // fastrand.Read wouldn't cause error, so ignore err is safe
|
||||
u, _ := UnsafeUUIDGenerator.NewV4() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
|
@ -32,12 +52,12 @@ func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID {
|
|||
}
|
||||
|
||||
func NewUUIDV6() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV6() // fastrand.Read wouldn't cause error, so ignore err is safe
|
||||
u, _ := UnsafeUUIDGenerator.NewV6() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
func NewUUIDV7() uuid.UUID {
|
||||
u, _ := UnsafeUUIDGenerator.NewV7() // fastrand.Read wouldn't cause error, so ignore err is safe
|
||||
u, _ := UnsafeUUIDGenerator.NewV7() // unsafeRandReader wouldn't cause error, so ignore err is safe
|
||||
return u
|
||||
}
|
||||
|
||||
|
|
|
@ -43,12 +43,12 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool {
|
|||
}
|
||||
|
||||
func (set *IpCidrSet) IsContain(ip netip.Addr) bool {
|
||||
return set.toIPSet().Contains(ip.WithZone(""))
|
||||
return set.ToIPSet().Contains(ip.WithZone(""))
|
||||
}
|
||||
|
||||
func (set *IpCidrSet) Merge() error {
|
||||
var b netipx.IPSetBuilder
|
||||
b.AddSet(set.toIPSet())
|
||||
b.AddSet(set.ToIPSet())
|
||||
i, err := b.IPSet()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -57,7 +57,9 @@ func (set *IpCidrSet) Merge() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (set *IpCidrSet) toIPSet() *netipx.IPSet {
|
||||
// ToIPSet not safe convert to *netipx.IPSet
|
||||
// be careful, must be used after Merge
|
||||
func (set *IpCidrSet) ToIPSet() *netipx.IPSet {
|
||||
return (*netipx.IPSet)(unsafe.Pointer(set))
|
||||
}
|
||||
|
||||
|
|
|
@ -378,12 +378,12 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C
|
|||
}
|
||||
|
||||
func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||
opt := WithOption(d.Opt)
|
||||
opt := d.Opt // make a copy
|
||||
if rAddrPort.Addr().Unmap().IsLoopback() {
|
||||
// avoid "The requested address is not valid in its context."
|
||||
opt = WithInterface("")
|
||||
WithInterface("")(&opt)
|
||||
}
|
||||
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, opt)
|
||||
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(opt))
|
||||
}
|
||||
|
||||
func NewDialer(options ...Option) Dialer {
|
||||
|
|
|
@ -5,12 +5,8 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
var DisableTFO = false
|
||||
|
||||
type tfoConn struct {
|
||||
net.Conn
|
||||
closed bool
|
||||
|
@ -124,16 +120,3 @@ func (c *tfoConn) ReaderReplaceable() bool {
|
|||
func (c *tfoConn) WriterReplaceable() bool {
|
||||
return c.Conn != nil
|
||||
}
|
||||
|
||||
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout)
|
||||
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
|
||||
return &tfoConn{
|
||||
dialed: make(chan bool, 1),
|
||||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, network, address, earlyData)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
25
component/dialer/tfo_unix.go
Normal file
25
component/dialer/tfo_unix.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
//go:build unix
|
||||
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
const DisableTFO = false
|
||||
|
||||
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout)
|
||||
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
|
||||
return &tfoConn{
|
||||
dialed: make(chan bool, 1),
|
||||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, network, address, earlyData)
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
package dialer
|
||||
|
||||
import "github.com/metacubex/mihomo/constant/features"
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// According to MSDN, this option is available since Windows 10, 1607
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx
|
||||
if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) {
|
||||
DisableTFO = true
|
||||
}
|
||||
const DisableTFO = true
|
||||
|
||||
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, address)
|
||||
}
|
||||
|
|
|
@ -11,8 +11,9 @@ import (
|
|||
|
||||
type Interface struct {
|
||||
Index int
|
||||
MTU int
|
||||
Name string
|
||||
Addrs []netip.Prefix
|
||||
Addresses []netip.Prefix
|
||||
HardwareAddr net.HardwareAddr
|
||||
}
|
||||
|
||||
|
@ -61,8 +62,9 @@ func Interfaces() (map[string]*Interface, error) {
|
|||
|
||||
r[iface.Name] = &Interface{
|
||||
Index: iface.Index,
|
||||
MTU: iface.MTU,
|
||||
Name: iface.Name,
|
||||
Addrs: ipNets,
|
||||
Addresses: ipNets,
|
||||
HardwareAddr: iface.HardwareAddr,
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +94,7 @@ func IsLocalIp(ip netip.Addr) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
for _, addr := range iface.Addrs {
|
||||
for _, addr := range iface.Addresses {
|
||||
if addr.Contains(ip) {
|
||||
return true, nil
|
||||
}
|
||||
|
@ -120,7 +122,7 @@ func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, erro
|
|||
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 {
|
||||
for _, addr := range iface.Addresses {
|
||||
if !accept(addr) {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -2,19 +2,23 @@ package process
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/mdlayher/netlink"
|
||||
tun "github.com/metacubex/sing-tun"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -59,6 +63,19 @@ type inetDiagResponse struct {
|
|||
INode uint32
|
||||
}
|
||||
|
||||
type MyCallback struct{}
|
||||
|
||||
var (
|
||||
packageManager tun.PackageManager
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func (cb *MyCallback) OnPackagesUpdated(packageCount int, sharedCount int) {}
|
||||
|
||||
func (cb *MyCallback) NewError(ctx context.Context, err error) {
|
||||
log.Warnln("%s", err)
|
||||
}
|
||||
|
||||
func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {
|
||||
uid, inode, err := resolveSocketByNetlink(network, ip, srcPort)
|
||||
if err != nil {
|
||||
|
@ -162,12 +179,7 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {
|
|||
}
|
||||
if runtime.GOOS == "android" {
|
||||
if bytes.Equal(buffer[:n], socket) {
|
||||
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return splitCmdline(cmdline), nil
|
||||
return findPackageName(uid), nil
|
||||
}
|
||||
} else {
|
||||
if bytes.Equal(buffer[:n], socket) {
|
||||
|
@ -181,17 +193,28 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {
|
|||
return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
|
||||
}
|
||||
|
||||
func splitCmdline(cmdline []byte) string {
|
||||
cmdline = bytes.Trim(cmdline, " ")
|
||||
|
||||
idx := bytes.IndexFunc(cmdline, func(r rune) bool {
|
||||
return unicode.IsControl(r) || unicode.IsSpace(r) || r == ':'
|
||||
func findPackageName(uid uint32) string {
|
||||
once.Do(func() {
|
||||
callback := &MyCallback{}
|
||||
var err error
|
||||
packageManager, err = tun.NewPackageManager(callback)
|
||||
if err != nil {
|
||||
log.Warnln("%s", err)
|
||||
}
|
||||
err = packageManager.Start()
|
||||
if err != nil {
|
||||
log.Warnln("%s", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if idx == -1 {
|
||||
return filepath.Base(string(cmdline))
|
||||
if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||
return sharedPackage
|
||||
}
|
||||
return filepath.Base(string(cmdline[:idx]))
|
||||
if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded {
|
||||
return packageName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isPid(s string) bool {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/resolver/hosts"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -125,5 +125,5 @@ func (hv HostValue) RandIP() (netip.Addr, error) {
|
|||
if hv.IsDomain {
|
||||
return netip.Addr{}, errors.New("value type is error")
|
||||
}
|
||||
return hv.IPs[fastrand.Intn(len(hv.IPs))], nil
|
||||
return hv.IPs[randv2.IntN(len(hv.IPs))], nil
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -93,7 +93,7 @@ func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (neti
|
|||
} else if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host)
|
||||
}
|
||||
return ips[fastrand.Intn(len(ips))], nil
|
||||
return ips[randv2.IntN(len(ips))], nil
|
||||
}
|
||||
|
||||
// ResolveIPv4 with a host, return ipv4
|
||||
|
@ -149,7 +149,7 @@ func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (neti
|
|||
} else if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host)
|
||||
}
|
||||
return ips[fastrand.Intn(len(ips))], nil
|
||||
return ips[randv2.IntN(len(ips))], nil
|
||||
}
|
||||
|
||||
func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) {
|
||||
|
@ -200,9 +200,9 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.
|
|||
}
|
||||
ipv4s, ipv6s := SortationAddr(ips)
|
||||
if len(ipv4s) > 0 {
|
||||
return ipv4s[fastrand.Intn(len(ipv4s))], nil
|
||||
return ipv4s[randv2.IntN(len(ipv4s))], nil
|
||||
}
|
||||
return ipv6s[fastrand.Intn(len(ipv6s))], nil
|
||||
return ipv6s[randv2.IntN(len(ipv6s))], nil
|
||||
}
|
||||
|
||||
// ResolveIP with a host, return ip and priority return TypeA
|
||||
|
|
|
@ -116,14 +116,13 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
|
|||
}
|
||||
|
||||
func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
|
||||
// show log early, since the following code may mutate `metadata.Host`
|
||||
log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]",
|
||||
metadata.NetWork,
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
metadata.Host, host)
|
||||
metadata.SniffHost = host
|
||||
if overrideDest {
|
||||
log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]",
|
||||
metadata.NetWork,
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
metadata.Host, host)
|
||||
metadata.Host = host
|
||||
}
|
||||
metadata.DNSMode = C.DNSNormal
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
|
||||
"github.com/metacubex/randv2"
|
||||
utls "github.com/metacubex/utls"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/net/http2"
|
||||
|
@ -138,13 +138,13 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C
|
|||
return
|
||||
}
|
||||
request.Header.Set("User-Agent", fingerprint.Client)
|
||||
request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)})
|
||||
request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", randv2.IntN(32)+30)})
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//_, _ = io.Copy(io.Discard, response.Body)
|
||||
time.Sleep(time.Duration(5+fastrand.Int63n(10)) * time.Second)
|
||||
time.Sleep(time.Duration(5+randv2.IntN(10)) * time.Second)
|
||||
response.Body.Close()
|
||||
client.CloseIdleConnections()
|
||||
}
|
||||
|
|
|
@ -98,11 +98,13 @@ func updateGeoDatabases() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateGeoDatabases(updateNotification chan struct{}) error {
|
||||
var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip")
|
||||
|
||||
func UpdateGeoDatabases() error {
|
||||
log.Infoln("[GEO] Start updating GEO database")
|
||||
|
||||
if UpdatingGeo.Load() {
|
||||
return errors.New("GEO database is updating, skip")
|
||||
return ErrGetDatabaseUpdateSkip
|
||||
}
|
||||
|
||||
UpdatingGeo.Store(true)
|
||||
|
@ -115,7 +117,6 @@ func UpdateGeoDatabases(updateNotification chan struct{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
updateNotification <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -136,17 +137,16 @@ func getUpdateTime() (err error, time time.Time) {
|
|||
return nil, fileInfo.ModTime()
|
||||
}
|
||||
|
||||
func RegisterGeoUpdater(updateNotification chan struct{}) {
|
||||
func RegisterGeoUpdater(onSuccess func()) {
|
||||
if C.GeoUpdateInterval <= 0 {
|
||||
log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval)
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval)
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
|
||||
defer ticker.Stop()
|
||||
|
||||
err, lastUpdate := getUpdateTime()
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] Get GEO database update time error: %s", err.Error())
|
||||
|
@ -156,15 +156,20 @@ func RegisterGeoUpdater(updateNotification chan struct{}) {
|
|||
log.Infoln("[GEO] last update time %s", lastUpdate)
|
||||
if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) {
|
||||
log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour)
|
||||
if err := UpdateGeoDatabases(updateNotification); err != nil {
|
||||
if err := UpdateGeoDatabases(); err != nil {
|
||||
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
|
||||
return
|
||||
} else {
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
for range ticker.C {
|
||||
if err := UpdateGeoDatabases(updateNotification); err != nil {
|
||||
log.Infoln("[GEO] updating database every %d hours", C.GeoUpdateInterval)
|
||||
if err := UpdateGeoDatabases(); err != nil {
|
||||
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
|
||||
} else {
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
174
config/config.go
174
config/config.go
|
@ -212,6 +212,7 @@ type RawDNS struct {
|
|||
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
||||
UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"`
|
||||
RespectRules bool `yaml:"respect-rules" json:"respect-rules"`
|
||||
NameServer []string `yaml:"nameserver" json:"nameserver"`
|
||||
Fallback []string `yaml:"fallback" json:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
||||
|
@ -245,31 +246,39 @@ type RawTun struct {
|
|||
DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
|
||||
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
||||
AutoDetectInterface bool `yaml:"auto-detect-interface"`
|
||||
RedirectToTun []string `yaml:"-" json:"-"`
|
||||
|
||||
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
GSO bool `yaml:"gso" json:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
|
||||
//Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
|
||||
IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
|
||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
|
||||
RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
|
||||
RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
|
||||
RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
|
||||
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
|
||||
Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"`
|
||||
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"`
|
||||
Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"`
|
||||
Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"`
|
||||
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
TableIndex int `yaml:"table-index" json:"table-index"`
|
||||
}
|
||||
|
||||
type RawTuicServer struct {
|
||||
|
@ -563,13 +572,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
}
|
||||
config.RuleProviders = ruleProviders
|
||||
|
||||
subRules, err := parseSubRules(rawCfg, proxies)
|
||||
subRules, err := parseSubRules(rawCfg, proxies, ruleProviders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.SubRules = subRules
|
||||
|
||||
rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules")
|
||||
rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -665,7 +674,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
}
|
||||
|
||||
cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun
|
||||
return &General{
|
||||
Inbound: Inbound{
|
||||
Port: cfg.Port,
|
||||
|
@ -716,8 +724,11 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
|||
groupsConfig := cfg.ProxyGroup
|
||||
providersConfig := cfg.ProxyProvider
|
||||
|
||||
var proxyList []string
|
||||
var AllProxies []string
|
||||
var (
|
||||
proxyList []string
|
||||
AllProxies []string
|
||||
hasGlobal bool
|
||||
)
|
||||
proxiesList := list.New()
|
||||
groupsList := list.New()
|
||||
|
||||
|
@ -750,6 +761,9 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
|||
if !existName {
|
||||
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
|
||||
}
|
||||
if groupName == "GLOBAL" {
|
||||
hasGlobal = true
|
||||
}
|
||||
proxyList = append(proxyList, groupName)
|
||||
groupsList.PushBack(mapping)
|
||||
}
|
||||
|
@ -801,13 +815,15 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
|||
pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
|
||||
providersMap[provider.ReservedName] = pd
|
||||
|
||||
global := outboundgroup.NewSelector(
|
||||
&outboundgroup.GroupCommonOption{
|
||||
Name: "GLOBAL",
|
||||
},
|
||||
[]providerTypes.ProxyProvider{pd},
|
||||
)
|
||||
proxies["GLOBAL"] = adapter.NewProxy(global)
|
||||
if !hasGlobal {
|
||||
global := outboundgroup.NewSelector(
|
||||
&outboundgroup.GroupCommonOption{
|
||||
Name: "GLOBAL",
|
||||
},
|
||||
[]providerTypes.ProxyProvider{pd},
|
||||
)
|
||||
proxies["GLOBAL"] = adapter.NewProxy(global)
|
||||
}
|
||||
ProxiesList = proxiesList
|
||||
GroupsList = groupsList
|
||||
if ParsingProxiesCallback != nil {
|
||||
|
@ -836,6 +852,7 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err
|
|||
}
|
||||
|
||||
func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) {
|
||||
RP.SetTunnel(T.Tunnel)
|
||||
ruleProviders = map[string]providerTypes.RuleProvider{}
|
||||
// parse rule provider
|
||||
for name, mapping := range cfg.RuleProvider {
|
||||
|
@ -845,12 +862,11 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.
|
|||
}
|
||||
|
||||
ruleProviders[name] = rp
|
||||
RP.SetRuleProvider(rp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) {
|
||||
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) {
|
||||
subRules = map[string][]C.Rule{}
|
||||
for name := range cfg.SubRules {
|
||||
subRules[name] = make([]C.Rule, 0)
|
||||
|
@ -860,7 +876,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[str
|
|||
return nil, fmt.Errorf("sub-rule name is empty")
|
||||
}
|
||||
var rules []C.Rule
|
||||
rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name))
|
||||
rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -913,7 +929,7 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {
|
||||
func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {
|
||||
var rules []C.Rule
|
||||
|
||||
// parse rules
|
||||
|
@ -962,6 +978,12 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
|
|||
return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error())
|
||||
}
|
||||
|
||||
for _, name := range parsed.ProviderNames() {
|
||||
if _, ok := ruleProviders[name]; !ok {
|
||||
return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name)
|
||||
}
|
||||
}
|
||||
|
||||
rules = append(rules, parsed)
|
||||
}
|
||||
|
||||
|
@ -1031,10 +1053,20 @@ func hostWithDefaultPort(host string, defPort string) (string, error) {
|
|||
return net.JoinHostPort(hostname, port), nil
|
||||
}
|
||||
|
||||
func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) {
|
||||
func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) {
|
||||
var nameservers []dns.NameServer
|
||||
|
||||
for idx, server := range servers {
|
||||
if strings.HasPrefix(server, "dhcp://") {
|
||||
nameservers = append(
|
||||
nameservers,
|
||||
dns.NameServer{
|
||||
Net: "dhcp",
|
||||
Addr: server[len("dhcp://"):],
|
||||
},
|
||||
)
|
||||
continue
|
||||
}
|
||||
server = parsePureDNSServer(server)
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
|
@ -1077,9 +1109,6 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||
}
|
||||
}
|
||||
}
|
||||
case "dhcp":
|
||||
addr = u.Host
|
||||
dnsNetType = "dhcp" // UDP from DHCP
|
||||
case "quic":
|
||||
addr, err = hostWithDefaultPort(u.Host, "853")
|
||||
dnsNetType = "quic" // DNS over QUIC
|
||||
|
@ -1106,6 +1135,10 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
||||
}
|
||||
|
||||
if respectRules && len(proxyName) == 0 {
|
||||
proxyName = dns.RespectRules
|
||||
}
|
||||
|
||||
nameservers = append(
|
||||
nameservers,
|
||||
dns.NameServer{
|
||||
|
@ -1122,7 +1155,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||
|
||||
func init() {
|
||||
dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
|
||||
return parseNameServer(servers, false)
|
||||
return parseNameServer(servers, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1181,8 @@ func parsePureDNSServer(server string) string {
|
|||
}
|
||||
}
|
||||
}
|
||||
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
|
||||
|
||||
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
|
||||
policy := orderedmap.New[string, []dns.NameServer]()
|
||||
updatedPolicy := orderedmap.New[string, any]()
|
||||
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
||||
|
@ -1194,7 +1228,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameservers, err := parseNameServer(servers, preferH3)
|
||||
nameservers, err := parseNameServer(servers, respectRules, preferH3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1288,6 +1322,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
|
||||
}
|
||||
|
||||
if cfg.RespectRules && len(cfg.ProxyServerNameserver) == 0 {
|
||||
return nil, fmt.Errorf("if “respect-rules” is turned on, “proxy-server-nameserver” cannot be empty")
|
||||
}
|
||||
|
||||
dnsCfg := &DNS{
|
||||
Enable: cfg.Enable,
|
||||
Listen: cfg.Listen,
|
||||
|
@ -1302,26 +1340,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||
},
|
||||
}
|
||||
var err error
|
||||
if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.PreferH3); err != nil {
|
||||
if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.PreferH3); err != nil {
|
||||
if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil {
|
||||
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, cfg.PreferH3); err != nil {
|
||||
if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.DefaultNameserver) == 0 {
|
||||
return nil, errors.New("default nameserver should have at least one nameserver")
|
||||
}
|
||||
if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, cfg.PreferH3); err != nil {
|
||||
if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check default nameserver is pure ip addr
|
||||
|
@ -1438,31 +1476,39 @@ func parseTun(rawTun RawTun, general *General) error {
|
|||
DNSHijack: rawTun.DNSHijack,
|
||||
AutoRoute: rawTun.AutoRoute,
|
||||
AutoDetectInterface: rawTun.AutoDetectInterface,
|
||||
RedirectToTun: rawTun.RedirectToTun,
|
||||
|
||||
MTU: rawTun.MTU,
|
||||
GSO: rawTun.GSO,
|
||||
GSOMaxSize: rawTun.GSOMaxSize,
|
||||
Inet4Address: []netip.Prefix{tunAddressPrefix},
|
||||
Inet6Address: rawTun.Inet6Address,
|
||||
StrictRoute: rawTun.StrictRoute,
|
||||
MTU: rawTun.MTU,
|
||||
GSO: rawTun.GSO,
|
||||
GSOMaxSize: rawTun.GSOMaxSize,
|
||||
Inet4Address: []netip.Prefix{tunAddressPrefix},
|
||||
Inet6Address: rawTun.Inet6Address,
|
||||
IPRoute2TableIndex: rawTun.IPRoute2TableIndex,
|
||||
IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex,
|
||||
AutoRedirect: rawTun.AutoRedirect,
|
||||
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
|
||||
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
|
||||
StrictRoute: rawTun.StrictRoute,
|
||||
RouteAddress: rawTun.RouteAddress,
|
||||
RouteAddressSet: rawTun.RouteAddressSet,
|
||||
RouteExcludeAddress: rawTun.RouteExcludeAddress,
|
||||
RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet,
|
||||
IncludeInterface: rawTun.IncludeInterface,
|
||||
ExcludeInterface: rawTun.ExcludeInterface,
|
||||
IncludeUID: rawTun.IncludeUID,
|
||||
IncludeUIDRange: rawTun.IncludeUIDRange,
|
||||
ExcludeUID: rawTun.ExcludeUID,
|
||||
ExcludeUIDRange: rawTun.ExcludeUIDRange,
|
||||
IncludeAndroidUser: rawTun.IncludeAndroidUser,
|
||||
IncludePackage: rawTun.IncludePackage,
|
||||
ExcludePackage: rawTun.ExcludePackage,
|
||||
EndpointIndependentNat: rawTun.EndpointIndependentNat,
|
||||
UDPTimeout: rawTun.UDPTimeout,
|
||||
FileDescriptor: rawTun.FileDescriptor,
|
||||
|
||||
Inet4RouteAddress: rawTun.Inet4RouteAddress,
|
||||
Inet6RouteAddress: rawTun.Inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress,
|
||||
IncludeInterface: rawTun.IncludeInterface,
|
||||
ExcludeInterface: rawTun.ExcludeInterface,
|
||||
IncludeUID: rawTun.IncludeUID,
|
||||
IncludeUIDRange: rawTun.IncludeUIDRange,
|
||||
ExcludeUID: rawTun.ExcludeUID,
|
||||
ExcludeUIDRange: rawTun.ExcludeUIDRange,
|
||||
IncludeAndroidUser: rawTun.IncludeAndroidUser,
|
||||
IncludePackage: rawTun.IncludePackage,
|
||||
ExcludePackage: rawTun.ExcludePackage,
|
||||
EndpointIndependentNat: rawTun.EndpointIndependentNat,
|
||||
UDPTimeout: rawTun.UDPTimeout,
|
||||
FileDescriptor: rawTun.FileDescriptor,
|
||||
TableIndex: rawTun.TableIndex,
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -6,5 +6,5 @@ func init() {
|
|||
version := windows.RtlGetVersion()
|
||||
WindowsMajorVersion = version.MajorVersion
|
||||
WindowsMinorVersion = version.MinorVersion
|
||||
WindowsBuildNumber = version.MinorVersion
|
||||
WindowsBuildNumber = version.BuildNumber
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ type RuleProvider interface {
|
|||
Match(*constant.Metadata) bool
|
||||
ShouldResolveIP() bool
|
||||
ShouldFindProcess() bool
|
||||
AsRule(adaptor string) constant.Rule
|
||||
Strategy() any
|
||||
}
|
||||
|
||||
// Rule Behavior
|
||||
|
@ -127,3 +127,9 @@ func (rf RuleFormat) String() string {
|
|||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type Tunnel interface {
|
||||
Providers() map[string]ProxyProvider
|
||||
RuleProviders() map[string]RuleProvider
|
||||
RuleUpdateCallback() *utils.Callback[RuleProvider]
|
||||
}
|
||||
|
|
|
@ -116,4 +116,5 @@ type Rule interface {
|
|||
Payload() string
|
||||
ShouldResolveIP() bool
|
||||
ShouldFindProcess() bool
|
||||
ProviderNames() []string
|
||||
}
|
||||
|
|
|
@ -5,28 +5,20 @@ import (
|
|||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
*D.Client
|
||||
r *Resolver
|
||||
port string
|
||||
host string
|
||||
iface string
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
addr string
|
||||
port string
|
||||
host string
|
||||
dialer *dnsDialer
|
||||
addr string
|
||||
}
|
||||
|
||||
var _ dnsClient = (*client)(nil)
|
||||
|
@ -49,38 +41,13 @@ func (c *client) Address() string {
|
|||
}
|
||||
|
||||
func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
||||
var (
|
||||
ip netip.Addr
|
||||
err error
|
||||
)
|
||||
if c.r == nil {
|
||||
// a default ip dns
|
||||
if ip, err = netip.ParseAddr(c.host); err != nil {
|
||||
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
|
||||
}
|
||||
} else {
|
||||
ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
|
||||
} else if len(ips) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host)
|
||||
}
|
||||
ip = ips[fastrand.Intn(len(ips))]
|
||||
}
|
||||
|
||||
network := "udp"
|
||||
if strings.HasPrefix(c.Client.Net, "tcp") {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
var options []dialer.Option
|
||||
if c.iface != "" {
|
||||
options = append(options, dialer.WithInterface(c.iface))
|
||||
}
|
||||
|
||||
dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)
|
||||
addr := net.JoinHostPort(ip.String(), c.port)
|
||||
conn, err := dialHandler(ctx, network, addr)
|
||||
addr := net.JoinHostPort(c.host, c.port)
|
||||
conn, err := c.dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -115,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
|||
tcpClient.Net = "tcp"
|
||||
network = "tcp"
|
||||
log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String())
|
||||
dConn.Conn, err = dialHandler(ctx, network, addr)
|
||||
dConn.Conn, err = c.dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
ch <- result{msg, err}
|
||||
return
|
||||
|
|
11
dns/dialer.go
Normal file
11
dns/dialer.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package dns
|
||||
|
||||
// export functions from tunnel module
|
||||
|
||||
import "github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
const RespectRules = tunnel.DnsRespectRules
|
||||
|
||||
type dnsDialer = tunnel.DNSDialer
|
||||
|
||||
var newDNSDialer = tunnel.NewDNSDialer
|
35
dns/doh.go
35
dns/doh.go
|
@ -62,10 +62,8 @@ type dnsOverHTTPS struct {
|
|||
quicConfig *quic.Config
|
||||
quicConfigGuard sync.Mutex
|
||||
url *url.URL
|
||||
r *Resolver
|
||||
httpVersions []C.HTTPVersion
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
dialer *dnsDialer
|
||||
addr string
|
||||
}
|
||||
|
||||
|
@ -85,11 +83,9 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
|
|||
}
|
||||
|
||||
doh := &dnsOverHTTPS{
|
||||
url: u,
|
||||
addr: u.String(),
|
||||
r: r,
|
||||
proxyAdapter: proxyAdapter,
|
||||
proxyName: proxyName,
|
||||
url: u,
|
||||
addr: u.String(),
|
||||
dialer: newDNSDialer(r, proxyAdapter, proxyName),
|
||||
quicConfig: &quic.Config{
|
||||
KeepAlivePeriod: QUICKeepAlivePeriod,
|
||||
TokenStore: newQUICTokenStore(),
|
||||
|
@ -388,13 +384,12 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
|
|||
nextProtos = append(nextProtos, string(v))
|
||||
}
|
||||
tlsConfig.NextProtos = nextProtos
|
||||
dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName)
|
||||
|
||||
if slices.Contains(doh.httpVersions, C.HTTPVersion3) {
|
||||
// First, we attempt to create an HTTP3 transport. If the probe QUIC
|
||||
// connection is established successfully, we'll be using HTTP3 for this
|
||||
// upstream.
|
||||
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
|
||||
transportH3, err := doh.createTransportH3(ctx, tlsConfig)
|
||||
if err == nil {
|
||||
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
|
||||
return transportH3, nil
|
||||
|
@ -410,7 +405,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
|
|||
transport := &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableCompression: true,
|
||||
DialContext: dialContext,
|
||||
DialContext: doh.dialer.DialContext,
|
||||
IdleConnTimeout: transportDefaultIdleConnTimeout,
|
||||
MaxConnsPerHost: dohMaxConnsPerHost,
|
||||
MaxIdleConns: dohMaxIdleConns,
|
||||
|
@ -490,13 +485,12 @@ func (h *http3Transport) Close() (err error) {
|
|||
func (doh *dnsOverHTTPS) createTransportH3(
|
||||
ctx context.Context,
|
||||
tlsConfig *tls.Config,
|
||||
dialContext dialHandler,
|
||||
) (roundTripper http.RoundTripper, err error) {
|
||||
if !doh.supportsH3() {
|
||||
return nil, errors.New("HTTP3 support is not enabled")
|
||||
}
|
||||
|
||||
addr, err := doh.probeH3(ctx, tlsConfig, dialContext)
|
||||
addr, err := doh.probeH3(ctx, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -534,7 +528,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
|
|||
IP: net.ParseIP(ip),
|
||||
Port: portInt,
|
||||
}
|
||||
conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r)
|
||||
conn, err := doh.dialer.ListenPacket(ctx, "udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -557,12 +551,11 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
|
|||
func (doh *dnsOverHTTPS) probeH3(
|
||||
ctx context.Context,
|
||||
tlsConfig *tls.Config,
|
||||
dialContext dialHandler,
|
||||
) (addr string, err error) {
|
||||
// We're using bootstrapped address instead of what's passed to the function
|
||||
// it does not create an actual connection, but it helps us determine
|
||||
// what IP is actually reachable (when there are v4/v6 addresses).
|
||||
rawConn, err := dialContext(ctx, "udp", doh.url.Host)
|
||||
rawConn, err := doh.dialer.DialContext(ctx, "udp", doh.url.Host)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to dial: %w", err)
|
||||
}
|
||||
|
@ -592,7 +585,7 @@ func (doh *dnsOverHTTPS) probeH3(
|
|||
chQuic := make(chan error, 1)
|
||||
chTLS := make(chan error, 1)
|
||||
go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic)
|
||||
go doh.probeTLS(ctx, dialContext, probeTLSCfg, chTLS)
|
||||
go doh.probeTLS(ctx, probeTLSCfg, chTLS)
|
||||
|
||||
select {
|
||||
case quicErr := <-chQuic:
|
||||
|
@ -635,10 +628,10 @@ func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *
|
|||
|
||||
// probeTLS attempts to establish a TLS connection to the specified address. We
|
||||
// run probeQUIC and probeTLS in parallel and see which one is faster.
|
||||
func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, dialContext dialHandler, tlsConfig *tls.Config, ch chan error) {
|
||||
func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, tlsConfig *tls.Config, ch chan error) {
|
||||
startTime := time.Now()
|
||||
|
||||
conn, err := doh.tlsDial(ctx, dialContext, "tcp", tlsConfig)
|
||||
conn, err := doh.tlsDial(ctx, "tcp", tlsConfig)
|
||||
if err != nil {
|
||||
ch <- fmt.Errorf("opening TLS connection: %w", err)
|
||||
return
|
||||
|
@ -694,10 +687,10 @@ func isHTTP3(client *http.Client) (ok bool) {
|
|||
|
||||
// tlsDial is basically the same as tls.DialWithDialer, but we will call our own
|
||||
// dialContext function to get connection.
|
||||
func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) {
|
||||
func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, network string, config *tls.Config) (*tls.Conn, error) {
|
||||
// We're using bootstrapped address instead of what's passed
|
||||
// to the function.
|
||||
rawConn, err := dialContext(ctx, network, doh.url.Host)
|
||||
rawConn, err := doh.dialer.DialContext(ctx, network, doh.url.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
16
dns/doq.go
16
dns/doq.go
|
@ -60,10 +60,8 @@ type dnsOverQUIC struct {
|
|||
bytesPool *sync.Pool
|
||||
bytesPoolGuard sync.Mutex
|
||||
|
||||
addr string
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
r *Resolver
|
||||
addr string
|
||||
dialer *dnsDialer
|
||||
}
|
||||
|
||||
// type check
|
||||
|
@ -72,10 +70,8 @@ var _ dnsClient = (*dnsOverQUIC)(nil)
|
|||
// newDoQ returns the DNS-over-QUIC Upstream.
|
||||
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
|
||||
doq := &dnsOverQUIC{
|
||||
addr: addr,
|
||||
proxyAdapter: proxyAdapter,
|
||||
proxyName: proxyName,
|
||||
r: resolver,
|
||||
addr: addr,
|
||||
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
||||
quicConfig: &quic.Config{
|
||||
KeepAlivePeriod: QUICKeepAlivePeriod,
|
||||
TokenStore: newQUICTokenStore(),
|
||||
|
@ -300,7 +296,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
|||
// we're using bootstrapped address instead of what's passed to the function
|
||||
// it does not create an actual connection, but it helps us determine
|
||||
// what IP is actually reachable (when there're v4/v6 addresses).
|
||||
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr)
|
||||
rawConn, err := doq.dialer.DialContext(ctx, "udp", doq.addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
|
||||
}
|
||||
|
@ -315,7 +311,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
|||
|
||||
p, err := strconv.Atoi(port)
|
||||
udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}
|
||||
udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r)
|
||||
udp, err := doq.dialer.ListenPacket(ctx, "udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -37,14 +37,17 @@ func (p geositePolicy) Match(domain string) []dnsClient {
|
|||
}
|
||||
|
||||
type domainSetPolicy struct {
|
||||
domainSetProvider provider.RuleProvider
|
||||
dnsClients []dnsClient
|
||||
tunnel provider.Tunnel
|
||||
name string
|
||||
dnsClients []dnsClient
|
||||
}
|
||||
|
||||
func (p domainSetPolicy) Match(domain string) []dnsClient {
|
||||
metadata := &C.Metadata{Host: domain}
|
||||
if ok := p.domainSetProvider.Match(metadata); ok {
|
||||
return p.dnsClients
|
||||
if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok {
|
||||
metadata := &C.Metadata{Host: domain}
|
||||
if ok := ruleProvider.Match(metadata); ok {
|
||||
return p.dnsClients
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -414,7 +414,7 @@ type Config struct {
|
|||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
Policy *orderedmap.OrderedMap[string, []NameServer]
|
||||
RuleProviders map[string]provider.RuleProvider
|
||||
Tunnel provider.Tunnel
|
||||
CacheAlgorithm string
|
||||
}
|
||||
|
||||
|
@ -502,11 +502,12 @@ func NewResolver(config Config) *Resolver {
|
|||
key := temp[1]
|
||||
switch prefix {
|
||||
case "rule-set":
|
||||
if p, ok := config.RuleProviders[key]; ok {
|
||||
if _, ok := config.Tunnel.RuleProviders()[key]; ok {
|
||||
log.Debugln("Adding rule-set policy: %s ", key)
|
||||
insertPolicy(domainSetPolicy{
|
||||
domainSetProvider: p,
|
||||
dnsClients: cacheTransform(nameserver),
|
||||
tunnel: config.Tunnel,
|
||||
name: key,
|
||||
dnsClients: cacheTransform(nameserver),
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
|
|
132
dns/util.go
132
dns/util.go
|
@ -7,18 +7,14 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/nnip"
|
||||
"github.com/metacubex/mihomo/common/picker"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
"github.com/samber/lo"
|
||||
|
@ -120,6 +116,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
|||
continue
|
||||
}
|
||||
|
||||
var options []dialer.Option
|
||||
if s.Interface != "" {
|
||||
options = append(options, dialer.WithInterface(s.Interface))
|
||||
}
|
||||
|
||||
host, port, _ := net.SplitHostPort(s.Addr)
|
||||
ret = append(ret, &client{
|
||||
Client: &D.Client{
|
||||
|
@ -130,12 +131,9 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
|||
UDPSize: 4096,
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
port: port,
|
||||
host: host,
|
||||
iface: s.Interface,
|
||||
r: resolver,
|
||||
proxyAdapter: s.ProxyAdapter,
|
||||
proxyName: s.ProxyName,
|
||||
port: port,
|
||||
host: host,
|
||||
dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName, options...),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
|
@ -175,120 +173,6 @@ func msgToDomain(msg *D.Msg) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
|
||||
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if len(proxyName) == 0 && proxyAdapter == nil {
|
||||
opts = append(opts, dialer.WithResolver(r))
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
} else {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uintPort, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if proxyAdapter == nil {
|
||||
var ok bool
|
||||
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||
if !ok {
|
||||
opts = append(opts, dialer.WithInterface(proxyName))
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(network, "tcp") {
|
||||
// tcp can resolve host by remote
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Host: host,
|
||||
DstPort: uint16(uintPort),
|
||||
}
|
||||
if proxyAdapter != nil {
|
||||
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata.Host = ""
|
||||
metadata.DstIP = dstIP
|
||||
}
|
||||
return proxyAdapter.DialContext(ctx, metadata, opts...)
|
||||
}
|
||||
opts = append(opts, dialer.WithResolver(r))
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
} else {
|
||||
// udp must resolve host first
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
Host: "",
|
||||
DstIP: dstIP,
|
||||
DstPort: uint16(uintPort),
|
||||
}
|
||||
if proxyAdapter == nil {
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||
}
|
||||
|
||||
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uintPort, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if proxyAdapter == nil {
|
||||
var ok bool
|
||||
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||
if !ok {
|
||||
opts = append(opts, dialer.WithInterface(proxyName))
|
||||
}
|
||||
}
|
||||
|
||||
// udp must resolve host first
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
Host: "",
|
||||
DstIP: dstIP,
|
||||
DstPort: uint16(uintPort),
|
||||
}
|
||||
if proxyAdapter == nil {
|
||||
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||
}
|
||||
|
||||
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||
}
|
||||
|
||||
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
|
||||
cache = true
|
||||
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
|
||||
|
|
|
@ -116,13 +116,25 @@ tun:
|
|||
# mtu: 9000 # 最大传输单元
|
||||
# gso: false # 启用通用分段卸载,仅支持 Linux
|
||||
# gso-max-size: 65536 # 通用分段卸载包的最大大小
|
||||
auto-redirect: false # 自动配置 iptables 以重定向 TCP 连接。仅支持 Linux。带有 auto-redirect 的 auto-route 现在可以在路由器上按预期工作,无需干预。
|
||||
# strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问
|
||||
inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由
|
||||
route-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 不匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。
|
||||
- ruleset-1
|
||||
- ruleset-2
|
||||
route-exclude-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。
|
||||
- ruleset-3
|
||||
- ruleset-4
|
||||
route-address: # 启用 auto-route 时使用自定义路由而不是默认路由
|
||||
- 0.0.0.0/1
|
||||
- 128.0.0.0/1
|
||||
inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由
|
||||
- "::/1"
|
||||
- "8000::/1"
|
||||
# inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法)
|
||||
# - 0.0.0.0/1
|
||||
# - 128.0.0.0/1
|
||||
# inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法)
|
||||
# - "::/1"
|
||||
# - "8000::/1"
|
||||
# endpoint-independent-nat: false # 启用独立于端点的 NAT
|
||||
# include-interface: # 限制被路由的接口。默认不限制,与 `exclude-interface` 冲突
|
||||
# - "lan0"
|
||||
|
@ -209,7 +221,7 @@ tunnels: # one line config
|
|||
dns:
|
||||
cache-algorithm: arc
|
||||
enable: false # 关闭将使用系统 DNS
|
||||
prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试
|
||||
prefer-h3: false # 是否开启 DoH 支持 HTTP/3,将并发尝试
|
||||
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
|
||||
# ipv6: false # false 将返回 AAAA 的空结果
|
||||
# ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms
|
||||
|
@ -227,6 +239,13 @@ dns:
|
|||
|
||||
# use-hosts: true # 查询 hosts
|
||||
|
||||
# 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则
|
||||
# 如果为false(默认值)则这三部分的dns服务器在未特别指定的情况下会直连
|
||||
# 如果为true,将会按照rules的规则匹配链接方式(走代理或直连),如果有特别指定则任然以指定值为准
|
||||
# 仅当proxy-server-nameserver非空时可以开启此选项, 强烈不建议和prefer-h3一起使用
|
||||
# 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver
|
||||
respect-rules: false
|
||||
|
||||
# 配置不使用 fake-ip 的域名
|
||||
# fake-ip-filter:
|
||||
# - '*.lan'
|
||||
|
@ -244,6 +263,7 @@ dns:
|
|||
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
|
||||
- dhcp://en0 # dns from dhcp
|
||||
- quic://dns.adguard.com:784 # DNS over QUIC
|
||||
# - '8.8.8.8#RULES' # 效果同respect-rules,但仅对该服务器生效
|
||||
# - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡
|
||||
|
||||
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置
|
||||
|
@ -611,6 +631,10 @@ proxies: # socks5
|
|||
# - h2
|
||||
# - http/1.1
|
||||
# skip-cert-verify: true
|
||||
# ss-opts: # like trojan-go's `shadowsocks` config
|
||||
# enabled: false
|
||||
# method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305
|
||||
# password: "example"
|
||||
|
||||
- name: trojan-grpc
|
||||
server: server
|
||||
|
@ -716,6 +740,7 @@ proxies: # socks5
|
|||
# dialer-proxy: "ss1"
|
||||
# remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false
|
||||
# dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效
|
||||
# refresh-server-ip-interval: 60 # 重新解析server ip的间隔,单位为秒,默认值为0即仅第一次链接时解析server域名,仅应在server域名对应的IP会发生变化时启用该选项(如家宽ddns)
|
||||
# 如果 peers 不为空,该段落中的 allowed-ips 不可为空;前面段落的 server,port,public-key,pre-shared-key 均会被忽略,但 private-key 会被保留且只能在顶层指定
|
||||
# peers:
|
||||
# - server: 162.159.192.1
|
||||
|
|
45
go.mod
45
go.mod
|
@ -9,50 +9,50 @@ require (
|
|||
github.com/cilium/ebpf v0.12.3
|
||||
github.com/coreos/go-iptables v0.7.0
|
||||
github.com/dlclark/regexp2 v1.11.0
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/chi/v5 v5.0.14
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gobwas/ws v1.4.0
|
||||
github.com/gofrs/uuid/v5 v5.2.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49
|
||||
github.com/klauspost/cpuid/v2 v2.2.7
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6
|
||||
github.com/klauspost/cpuid/v2 v2.2.8
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||
github.com/mdlayher/netlink v1.7.2
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e
|
||||
github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e
|
||||
github.com/metacubex/randv2 v0.2.0
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66
|
||||
github.com/metacubex/utls v1.6.6
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/miekg/dns v1.1.61
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
github.com/openacid/low v0.1.21
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/puzpuzpuz/xsync/v3 v3.1.0
|
||||
github.com/puzpuzpuz/xsync/v3 v3.2.0
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.3.8
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||
github.com/sagernet/sing v0.5.0-alpha.10
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e
|
||||
github.com/samber/lo v1.39.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.4
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||
github.com/zhangyunhao116/fastrand v0.4.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.20.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
golang.org/x/sys v0.21.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lukechampine.com/blake3 v1.3.0
|
||||
)
|
||||
|
@ -92,6 +92,7 @@ require (
|
|||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
|
@ -100,14 +101,14 @@ require (
|
|||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2
|
||||
|
|
92
go.sum
92
go.sum
|
@ -44,8 +44,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
|
||||
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0=
|
||||
github.com/go-chi/chi/v5 v5.0.14/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
|
@ -69,7 +69,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
|||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
|
@ -78,16 +77,16 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
|||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 h1:dh8D8FksyMhD64mRMbUhZHWYJfNoNMCxfVq6eexleMw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
|
@ -104,28 +103,30 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
|
|||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc=
|
||||
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw=
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g=
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk=
|
||||
github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco=
|
||||
github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
|
||||
github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvWDcBDAkIv5kUYIhzHwafDVq635BuybnKqI=
|
||||
github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU=
|
||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 h1:N5tidgg/FRmkgPw/AjRwhLUinKDx/ODCSbvv9xqRoLM=
|
||||
github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e h1:o+zohxPRo45P35fS9u1zfdBgr+L/7S0ObGU6YjbVBIc=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
|
||||
github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8=
|
||||
github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
|
||||
|
@ -148,8 +149,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.2.0 h1:9AzuUeF88YC5bK8u2vEG1Fpvu4wgpM1wfPIExfaaDxQ=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.2.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
|
@ -157,8 +158,10 @@ github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58
|
|||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14=
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
|
@ -169,12 +172,11 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F
|
|||
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE=
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
|
||||
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
|
||||
|
@ -204,14 +206,12 @@ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho
|
|||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zhangyunhao116/fastrand v0.4.0 h1:86QB6Y+GGgLZRFRDCjMmAS28QULwspK9sgL5d1Bx3H4=
|
||||
github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA=
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
|
@ -222,18 +222,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
|||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
|
@ -252,23 +252,21 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -97,7 +97,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||
updateHosts(cfg.Hosts)
|
||||
updateGeneral(cfg.General)
|
||||
updateNTP(cfg.NTP)
|
||||
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6)
|
||||
updateDNS(cfg.DNS, cfg.General.IPv6)
|
||||
updateListeners(cfg.General, cfg.Listeners, force)
|
||||
updateIPTables(cfg)
|
||||
updateTun(cfg.General)
|
||||
|
@ -211,7 +211,7 @@ func updateNTP(c *config.NTP) {
|
|||
}
|
||||
}
|
||||
|
||||
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) {
|
||||
func updateDNS(c *config.DNS, generalIPv6 bool) {
|
||||
if !c.Enable {
|
||||
resolver.DefaultResolver = nil
|
||||
resolver.DefaultHostMapper = nil
|
||||
|
@ -237,7 +237,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
|
|||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
ProxyServer: c.ProxyServerNameserver,
|
||||
RuleProviders: ruleProvider,
|
||||
Tunnel: tunnel.Tunnel,
|
||||
CacheAlgorithm: c.CacheAlgorithm,
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ func updateTun(general *config.General) {
|
|||
return
|
||||
}
|
||||
listener.ReCreateTun(general.Tun, tunnel.Tunnel)
|
||||
listener.ReCreateRedirToTun(general.Tun.RedirectToTun)
|
||||
listener.ReCreateRedirToTun(general.EBpf.RedirectToTun)
|
||||
}
|
||||
|
||||
func updateSniffer(sniffer *config.Sniffer) {
|
||||
|
@ -507,9 +507,7 @@ func updateIPTables(cfg *config.Config) {
|
|||
inboundInterface = iptables.InboundInterface
|
||||
}
|
||||
|
||||
if dialer.DefaultRoutingMark.Load() == 0 {
|
||||
dialer.DefaultRoutingMark.Store(2158)
|
||||
}
|
||||
dialer.DefaultRoutingMark.CompareAndSwap(0, 2158)
|
||||
|
||||
err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port())
|
||||
if err != nil {
|
||||
|
|
|
@ -68,25 +68,34 @@ type tunSchema struct {
|
|||
GSO *bool `yaml:"gso" json:"gso,omitempty"`
|
||||
GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
|
||||
//Inet4Address *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
IPRoute2TableIndex *int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex *int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect *bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
|
||||
RouteAddressSet *[]string `yaml:"route-address-set" json:"route_address_set,omitempty"`
|
||||
RouteExcludeAddress *[]netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
|
||||
RouteExcludeAddressSet *[]string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
|
||||
IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
|
||||
Inet4RouteAddress *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
Inet4RouteExcludeAddress *[]netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"`
|
||||
Inet6RouteExcludeAddress *[]netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"`
|
||||
IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
TableIndex *int `yaml:"table-index" json:"table-index"`
|
||||
}
|
||||
|
||||
type tuicServerSchema struct {
|
||||
|
@ -157,6 +166,36 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
|
|||
if p.Inet6Address != nil {
|
||||
def.Inet6Address = *p.Inet6Address
|
||||
}
|
||||
if p.IPRoute2TableIndex != nil {
|
||||
def.IPRoute2TableIndex = *p.IPRoute2TableIndex
|
||||
}
|
||||
if p.IPRoute2RuleIndex != nil {
|
||||
def.IPRoute2RuleIndex = *p.IPRoute2RuleIndex
|
||||
}
|
||||
if p.AutoRedirect != nil {
|
||||
def.AutoRedirect = *p.AutoRedirect
|
||||
}
|
||||
if p.AutoRedirectInputMark != nil {
|
||||
def.AutoRedirectInputMark = *p.AutoRedirectInputMark
|
||||
}
|
||||
if p.AutoRedirectOutputMark != nil {
|
||||
def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark
|
||||
}
|
||||
if p.StrictRoute != nil {
|
||||
def.StrictRoute = *p.StrictRoute
|
||||
}
|
||||
if p.RouteAddress != nil {
|
||||
def.RouteAddress = *p.RouteAddress
|
||||
}
|
||||
if p.RouteAddressSet != nil {
|
||||
def.RouteAddressSet = *p.RouteAddressSet
|
||||
}
|
||||
if p.RouteExcludeAddress != nil {
|
||||
def.RouteExcludeAddress = *p.RouteExcludeAddress
|
||||
}
|
||||
if p.RouteExcludeAddressSet != nil {
|
||||
def.RouteExcludeAddressSet = *p.RouteExcludeAddressSet
|
||||
}
|
||||
if p.Inet4RouteAddress != nil {
|
||||
def.Inet4RouteAddress = *p.Inet4RouteAddress
|
||||
}
|
||||
|
@ -205,9 +244,6 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
|
|||
if p.FileDescriptor != nil {
|
||||
def.FileDescriptor = *p.FileDescriptor
|
||||
}
|
||||
if p.TableIndex != nil {
|
||||
def.TableIndex = *p.TableIndex
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
@ -364,47 +400,25 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
|
||||
updateNotification := make(chan struct{})
|
||||
errorChannel := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
defer func() {
|
||||
close(updateNotification)
|
||||
close(errorChannel)
|
||||
}()
|
||||
err := updater.UpdateGeoDatabases()
|
||||
if err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
for {
|
||||
select {
|
||||
case <-updateNotification:
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError("Error parsing configuration"))
|
||||
return
|
||||
}
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError("Error parsing configuration"))
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnln("[REST-API] update GEO databases success, applying config")
|
||||
executor.ApplyConfig(cfg, false)
|
||||
return
|
||||
case err := <-errorChannel:
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
log.Warnln("[GEO] update GEO databases success, applying config")
|
||||
|
||||
go func() {
|
||||
err := updater.UpdateGeoDatabases(updateNotification)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
}
|
||||
}()
|
||||
|
||||
<-done
|
||||
executor.ApplyConfig(cfg, false)
|
||||
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
|
|
@ -27,27 +27,36 @@ type Tun struct {
|
|||
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
|
||||
RedirectToTun []string `yaml:"-" json:"-"`
|
||||
|
||||
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
GSO bool `yaml:"gso" json:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
|
||||
Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
GSO bool `yaml:"gso" json:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
|
||||
Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
|
||||
RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
|
||||
RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
|
||||
RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
|
||||
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
|
||||
Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"`
|
||||
Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"`
|
||||
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
TableIndex int `yaml:"table-index" json:"table-index"`
|
||||
}
|
||||
|
|
|
@ -18,29 +18,38 @@ type TunOption struct {
|
|||
AutoRoute bool `inbound:"auto-route,omitempty"`
|
||||
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
|
||||
|
||||
MTU uint32 `inbound:"mtu,omitempty"`
|
||||
GSO bool `inbound:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
||||
Inet4Address []string `inbound:"inet4_address,omitempty"`
|
||||
Inet6Address []string `inbound:"inet6_address,omitempty"`
|
||||
StrictRoute bool `inbound:"strict_route,omitempty"`
|
||||
MTU uint32 `inbound:"mtu,omitempty"`
|
||||
GSO bool `inbound:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
||||
Inet4Address []string `inbound:"inet4_address,omitempty"`
|
||||
Inet6Address []string `inbound:"inet6_address,omitempty"`
|
||||
IPRoute2TableIndex int `inbound:"iproute2-table-index"`
|
||||
IPRoute2RuleIndex int `inbound:"iproute2-rule-index"`
|
||||
AutoRedirect bool `inbound:"auto-redirect"`
|
||||
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"`
|
||||
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"`
|
||||
StrictRoute bool `inbound:"strict_route,omitempty"`
|
||||
RouteAddress []string `inbound:"route-address"`
|
||||
RouteAddressSet []string `inbound:"route-address-set"`
|
||||
RouteExcludeAddress []string `inbound:"route-exclude-address"`
|
||||
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"`
|
||||
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `inbound:"exclude-interface"`
|
||||
IncludeUID []uint32 `inbound:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `inbound:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `inbound:"include_android_user,omitempty"`
|
||||
IncludePackage []string `inbound:"include_package,omitempty"`
|
||||
ExcludePackage []string `inbound:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `inbound:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
||||
|
||||
Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"`
|
||||
Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"`
|
||||
Inet4RouteExcludeAddress []string `inbound:"inet4_route_exclude_address,omitempty"`
|
||||
Inet6RouteExcludeAddress []string `inbound:"inet6_route_exclude_address,omitempty"`
|
||||
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `inbound:"exclude-interface" json:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `inbound:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `inbound:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `inbound:"include_android_user,omitempty"`
|
||||
IncludePackage []string `inbound:"include_package,omitempty"`
|
||||
ExcludePackage []string `inbound:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `inbound:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
||||
TableIndex int `inbound:"table-index,omitempty"`
|
||||
}
|
||||
|
||||
func (o TunOption) Equal(config C.InboundConfig) bool {
|
||||
|
@ -63,6 +72,16 @@ func NewTun(options *TunOption) (*Tun, error) {
|
|||
if !exist {
|
||||
return nil, errors.New("invalid tun stack")
|
||||
}
|
||||
|
||||
routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -91,35 +110,44 @@ func NewTun(options *TunOption) (*Tun, error) {
|
|||
Base: base,
|
||||
config: options,
|
||||
tun: LC.Tun{
|
||||
Enable: true,
|
||||
Device: options.Device,
|
||||
Stack: stack,
|
||||
DNSHijack: options.DNSHijack,
|
||||
AutoRoute: options.AutoRoute,
|
||||
AutoDetectInterface: options.AutoDetectInterface,
|
||||
MTU: options.MTU,
|
||||
GSO: options.GSO,
|
||||
GSOMaxSize: options.GSOMaxSize,
|
||||
Inet4Address: inet4Address,
|
||||
Inet6Address: inet6Address,
|
||||
StrictRoute: options.StrictRoute,
|
||||
Enable: true,
|
||||
Device: options.Device,
|
||||
Stack: stack,
|
||||
DNSHijack: options.DNSHijack,
|
||||
AutoRoute: options.AutoRoute,
|
||||
AutoDetectInterface: options.AutoDetectInterface,
|
||||
MTU: options.MTU,
|
||||
GSO: options.GSO,
|
||||
GSOMaxSize: options.GSOMaxSize,
|
||||
Inet4Address: inet4Address,
|
||||
Inet6Address: inet6Address,
|
||||
IPRoute2TableIndex: options.IPRoute2TableIndex,
|
||||
IPRoute2RuleIndex: options.IPRoute2RuleIndex,
|
||||
AutoRedirect: options.AutoRedirect,
|
||||
AutoRedirectInputMark: options.AutoRedirectInputMark,
|
||||
AutoRedirectOutputMark: options.AutoRedirectOutputMark,
|
||||
StrictRoute: options.StrictRoute,
|
||||
RouteAddress: routeAddress,
|
||||
RouteAddressSet: options.RouteAddressSet,
|
||||
RouteExcludeAddress: routeExcludeAddress,
|
||||
RouteExcludeAddressSet: options.RouteExcludeAddressSet,
|
||||
IncludeInterface: options.IncludeInterface,
|
||||
ExcludeInterface: options.ExcludeInterface,
|
||||
IncludeUID: options.IncludeUID,
|
||||
IncludeUIDRange: options.IncludeUIDRange,
|
||||
ExcludeUID: options.ExcludeUID,
|
||||
ExcludeUIDRange: options.ExcludeUIDRange,
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
EndpointIndependentNat: options.EndpointIndependentNat,
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
FileDescriptor: options.FileDescriptor,
|
||||
|
||||
Inet4RouteAddress: inet4RouteAddress,
|
||||
Inet6RouteAddress: inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||
IncludeInterface: options.IncludeInterface,
|
||||
ExcludeInterface: options.ExcludeInterface,
|
||||
IncludeUID: options.IncludeUID,
|
||||
IncludeUIDRange: options.IncludeUIDRange,
|
||||
ExcludeUID: options.ExcludeUID,
|
||||
ExcludeUIDRange: options.ExcludeUIDRange,
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
EndpointIndependentNat: options.EndpointIndependentNat,
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
FileDescriptor: options.FileDescriptor,
|
||||
TableIndex: options.TableIndex,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -820,11 +820,15 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
|
|||
LastTunConf.MTU != tunConf.MTU ||
|
||||
LastTunConf.GSO != tunConf.GSO ||
|
||||
LastTunConf.GSOMaxSize != tunConf.GSOMaxSize ||
|
||||
LastTunConf.IPRoute2TableIndex != tunConf.IPRoute2TableIndex ||
|
||||
LastTunConf.IPRoute2RuleIndex != tunConf.IPRoute2RuleIndex ||
|
||||
LastTunConf.AutoRedirect != tunConf.AutoRedirect ||
|
||||
LastTunConf.AutoRedirectInputMark != tunConf.AutoRedirectInputMark ||
|
||||
LastTunConf.AutoRedirectOutputMark != tunConf.AutoRedirectOutputMark ||
|
||||
LastTunConf.StrictRoute != tunConf.StrictRoute ||
|
||||
LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat ||
|
||||
LastTunConf.UDPTimeout != tunConf.UDPTimeout ||
|
||||
LastTunConf.FileDescriptor != tunConf.FileDescriptor ||
|
||||
LastTunConf.TableIndex != tunConf.TableIndex {
|
||||
LastTunConf.FileDescriptor != tunConf.FileDescriptor {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -836,6 +840,22 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
|
|||
return tunConf.DNSHijack[i] < tunConf.DNSHijack[j]
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.RouteAddress, func(i, j int) bool {
|
||||
return tunConf.RouteAddress[i].String() < tunConf.RouteAddress[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.RouteAddressSet, func(i, j int) bool {
|
||||
return tunConf.RouteAddressSet[i] < tunConf.RouteAddressSet[j]
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.RouteExcludeAddress, func(i, j int) bool {
|
||||
return tunConf.RouteExcludeAddress[i].String() < tunConf.RouteExcludeAddress[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.RouteExcludeAddressSet, func(i, j int) bool {
|
||||
return tunConf.RouteExcludeAddressSet[i] < tunConf.RouteExcludeAddressSet[j]
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.Inet4Address, func(i, j int) bool {
|
||||
return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String()
|
||||
})
|
||||
|
@ -897,6 +917,10 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
|
|||
})
|
||||
|
||||
if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) ||
|
||||
!slices.Equal(tunConf.RouteAddress, LastTunConf.RouteAddress) ||
|
||||
!slices.Equal(tunConf.RouteAddressSet, LastTunConf.RouteAddressSet) ||
|
||||
!slices.Equal(tunConf.RouteExcludeAddress, LastTunConf.RouteExcludeAddress) ||
|
||||
!slices.Equal(tunConf.RouteExcludeAddressSet, LastTunConf.RouteExcludeAddressSet) ||
|
||||
!slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) ||
|
||||
!slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) ||
|
||||
!slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) ||
|
||||
|
|
|
@ -198,6 +198,12 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) {
|
|||
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
|
||||
handler := *h
|
||||
handler.Type = typ
|
||||
return &handler
|
||||
}
|
||||
|
||||
func ShouldIgnorePacketError(err error) bool {
|
||||
// ignore simple error
|
||||
if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
|
@ -124,3 +125,9 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||
}
|
||||
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
|
||||
handle := *h
|
||||
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
|
||||
return &handle
|
||||
}
|
||||
|
|
70
listener/sing_tun/iface.go
Normal file
70
listener/sing_tun/iface.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package sing_tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/component/iface"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
type defaultInterfaceFinder struct{}
|
||||
|
||||
var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil)
|
||||
|
||||
func (f *defaultInterfaceFinder) Interfaces() []control.Interface {
|
||||
ifaces, err := iface.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
interfaces := make([]control.Interface, 0, len(ifaces))
|
||||
for _, _interface := range ifaces {
|
||||
interfaces = append(interfaces, control.Interface(*_interface))
|
||||
}
|
||||
|
||||
return interfaces
|
||||
}
|
||||
|
||||
var errNoSuchInterface = errors.New("no such network interface")
|
||||
|
||||
func (f *defaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) {
|
||||
ifaces, err := iface.Interfaces()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, netInterface := range ifaces {
|
||||
if netInterface.Name == name {
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
}
|
||||
return 0, errNoSuchInterface
|
||||
}
|
||||
|
||||
func (f *defaultInterfaceFinder) InterfaceNameByIndex(index int) (string, error) {
|
||||
ifaces, err := iface.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, netInterface := range ifaces {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
}
|
||||
return "", errNoSuchInterface
|
||||
}
|
||||
|
||||
func (f *defaultInterfaceFinder) InterfaceByAddr(addr netip.Addr) (*control.Interface, error) {
|
||||
ifaces, err := iface.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, netInterface := range ifaces {
|
||||
for _, prefix := range netInterface.Addresses {
|
||||
if prefix.Contains(addr) {
|
||||
return (*control.Interface)(netInterface), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errNoSuchInterface
|
||||
}
|
3
listener/sing_tun/redirect_linux.go
Normal file
3
listener/sing_tun/redirect_linux.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package sing_tun
|
||||
|
||||
const supportRedirect = true
|
5
listener/sing_tun/redirect_stub.go
Normal file
5
listener/sing_tun/redirect_stub.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !linux
|
||||
|
||||
package sing_tun
|
||||
|
||||
const supportRedirect = false
|
|
@ -3,27 +3,34 @@ package sing_tun
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/iface"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/provider"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
tun "github.com/metacubex/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
|
||||
"go4.org/netipx"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var InterfaceName = "Meta"
|
||||
|
@ -42,10 +49,21 @@ type Listener struct {
|
|||
networkUpdateMonitor tun.NetworkUpdateMonitor
|
||||
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
autoRedirect tun.AutoRedirect
|
||||
autoRedirectOutputMark int32
|
||||
|
||||
ruleUpdateCallbackCloser io.Closer
|
||||
ruleUpdateMutex sync.Mutex
|
||||
routeAddressMap map[string]*netipx.IPSet
|
||||
routeExcludeAddressMap map[string]*netipx.IPSet
|
||||
routeAddressSet []*netipx.IPSet
|
||||
routeExcludeAddressSet []*netipx.IPSet
|
||||
|
||||
dnsServerIp []string
|
||||
}
|
||||
|
||||
var emptyAddressSet = []*netipx.IPSet{{}}
|
||||
|
||||
func CalculateInterfaceName(name string) (tunName string) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
tunName = "utun"
|
||||
|
@ -59,15 +77,25 @@ func CalculateInterfaceName(name string) (tunName string) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
var tunIndex int
|
||||
tunIndex := 0
|
||||
indexArr := make([]int, 0, len(interfaces))
|
||||
for _, netInterface := range interfaces {
|
||||
if strings.HasPrefix(netInterface.Name, tunName) {
|
||||
index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16)
|
||||
if parseErr == nil {
|
||||
tunIndex = int(index) + 1
|
||||
indexArr = append(indexArr, int(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
slices.Sort(indexArr)
|
||||
indexArr = slices.Compact(indexArr)
|
||||
for _, index := range indexArr {
|
||||
if index == tunIndex {
|
||||
tunIndex += 1
|
||||
} else { // indexArr already sorted and distinct, so this tunIndex nobody used
|
||||
break
|
||||
}
|
||||
}
|
||||
tunName = F.ToString(tunName, tunIndex)
|
||||
return
|
||||
}
|
||||
|
@ -99,14 +127,45 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
inbound.WithSpecialRules(""),
|
||||
}
|
||||
}
|
||||
ctx := context.TODO()
|
||||
rpTunnel := tunnel.(provider.Tunnel)
|
||||
if options.GSOMaxSize == 0 {
|
||||
options.GSOMaxSize = 65536
|
||||
}
|
||||
if !supportRedirect {
|
||||
options.AutoRedirect = false
|
||||
}
|
||||
tunName := options.Device
|
||||
if tunName == "" || !checkTunName(tunName) {
|
||||
tunName = CalculateInterfaceName(InterfaceName)
|
||||
options.Device = tunName
|
||||
}
|
||||
routeAddress := options.RouteAddress
|
||||
if len(options.Inet4RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
|
||||
}
|
||||
if len(options.Inet6RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet6RouteAddress...)
|
||||
}
|
||||
inet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
inet6RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
routeExcludeAddress := options.RouteExcludeAddress
|
||||
if len(options.Inet4RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
|
||||
}
|
||||
if len(options.Inet6RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)
|
||||
}
|
||||
inet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
inet6RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
tunMTU := options.MTU
|
||||
if tunMTU == 0 {
|
||||
tunMTU = 9000
|
||||
|
@ -117,9 +176,21 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
} else {
|
||||
udpTimeout = int64(sing.UDPTimeout.Seconds())
|
||||
}
|
||||
tableIndex := options.TableIndex
|
||||
tableIndex := options.IPRoute2TableIndex
|
||||
if tableIndex == 0 {
|
||||
tableIndex = 2022
|
||||
tableIndex = tun.DefaultIPRoute2TableIndex
|
||||
}
|
||||
ruleIndex := options.IPRoute2RuleIndex
|
||||
if ruleIndex == 0 {
|
||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||
}
|
||||
inputMark := options.AutoRedirectInputMark
|
||||
if inputMark == 0 {
|
||||
inputMark = tun.DefaultAutoRedirectInputMark
|
||||
}
|
||||
outputMark := options.AutoRedirectOutputMark
|
||||
if outputMark == 0 {
|
||||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
includeUID := uidToRange(options.IncludeUID)
|
||||
if len(options.IncludeUIDRange) > 0 {
|
||||
|
@ -191,6 +262,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
}
|
||||
}()
|
||||
|
||||
interfaceFinder := DefaultInterfaceFinder
|
||||
|
||||
networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "create NetworkUpdateMonitor")
|
||||
|
@ -225,11 +298,15 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
Inet4Address: options.Inet4Address,
|
||||
Inet6Address: options.Inet6Address,
|
||||
AutoRoute: options.AutoRoute,
|
||||
IPRoute2TableIndex: tableIndex,
|
||||
IPRoute2RuleIndex: ruleIndex,
|
||||
AutoRedirectInputMark: inputMark,
|
||||
AutoRedirectOutputMark: outputMark,
|
||||
StrictRoute: options.StrictRoute,
|
||||
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,
|
||||
Inet4RouteAddress: inet4RouteAddress,
|
||||
Inet6RouteAddress: inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||
IncludeInterface: options.IncludeInterface,
|
||||
ExcludeInterface: options.ExcludeInterface,
|
||||
IncludeUID: includeUID,
|
||||
|
@ -239,7 +316,56 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
ExcludePackage: options.ExcludePackage,
|
||||
FileDescriptor: options.FileDescriptor,
|
||||
InterfaceMonitor: defaultInterfaceMonitor,
|
||||
TableIndex: tableIndex,
|
||||
}
|
||||
|
||||
if options.AutoRedirect {
|
||||
l.routeAddressMap = make(map[string]*netipx.IPSet)
|
||||
l.routeExcludeAddressMap = make(map[string]*netipx.IPSet)
|
||||
|
||||
if !options.AutoRoute {
|
||||
return nil, E.New("`auto-route` is required by `auto-redirect`")
|
||||
}
|
||||
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))
|
||||
l.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
|
||||
TunOptions: &tunOptions,
|
||||
Context: ctx,
|
||||
Handler: handler.TypeMutation(C.REDIR),
|
||||
Logger: log.SingLogger,
|
||||
NetworkMonitor: networkUpdateMonitor,
|
||||
InterfaceFinder: interfaceFinder,
|
||||
TableName: "mihomo",
|
||||
DisableNFTables: dErr == nil && disableNFTables,
|
||||
RouteAddressSet: &l.routeAddressSet,
|
||||
RouteExcludeAddressSet: &l.routeExcludeAddressSet,
|
||||
})
|
||||
if err != nil {
|
||||
err = E.Cause(err, "initialize auto redirect")
|
||||
return
|
||||
}
|
||||
|
||||
var markMode bool
|
||||
for _, routeAddressSet := range options.RouteAddressSet {
|
||||
rp, loaded := rpTunnel.RuleProviders()[routeAddressSet]
|
||||
if !loaded {
|
||||
err = E.New("parse route-address-set: rule-set not found: ", routeAddressSet)
|
||||
return
|
||||
}
|
||||
l.updateRule(rp, false, false)
|
||||
markMode = true
|
||||
}
|
||||
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
|
||||
rp, loaded := rpTunnel.RuleProviders()[routeExcludeAddressSet]
|
||||
if !loaded {
|
||||
err = E.New("parse route-exclude_address-set: rule-set not found: ", routeExcludeAddressSet)
|
||||
return
|
||||
}
|
||||
l.updateRule(rp, true, false)
|
||||
markMode = true
|
||||
}
|
||||
if markMode {
|
||||
tunOptions.AutoRedirectMarkMode = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = l.buildAndroidRules(&tunOptions)
|
||||
|
@ -258,14 +384,14 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
resolver.AddSystemDnsBlacklist(dnsServerIp...)
|
||||
|
||||
stackOptions := tun.StackOptions{
|
||||
Context: context.TODO(),
|
||||
Context: ctx,
|
||||
Tun: tunIf,
|
||||
TunOptions: tunOptions,
|
||||
EndpointIndependentNat: options.EndpointIndependentNat,
|
||||
UDPTimeout: udpTimeout,
|
||||
Handler: handler,
|
||||
Logger: log.SingLogger,
|
||||
InterfaceFinder: control.DefaultInterfaceFinder(),
|
||||
InterfaceFinder: interfaceFinder,
|
||||
EnforceBindInterface: EnforceBindInterface,
|
||||
}
|
||||
|
||||
|
@ -288,13 +414,80 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
}
|
||||
l.tunStack = tunStack
|
||||
|
||||
if l.autoRedirect != nil {
|
||||
if len(l.options.RouteAddressSet) > 0 && len(l.routeAddressSet) == 0 {
|
||||
l.routeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start
|
||||
}
|
||||
if len(l.options.RouteExcludeAddressSet) > 0 && len(l.routeExcludeAddressSet) == 0 {
|
||||
l.routeExcludeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start
|
||||
}
|
||||
err = l.autoRedirect.Start()
|
||||
if err != nil {
|
||||
err = E.Cause(err, "auto redirect")
|
||||
return
|
||||
}
|
||||
if tunOptions.AutoRedirectMarkMode {
|
||||
l.autoRedirectOutputMark = int32(outputMark)
|
||||
dialer.DefaultRoutingMark.Store(l.autoRedirectOutputMark)
|
||||
l.autoRedirect.UpdateRouteAddressSet()
|
||||
l.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback)
|
||||
}
|
||||
}
|
||||
|
||||
//l.openAndroidHotspot(tunOptions)
|
||||
|
||||
l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s",
|
||||
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack)
|
||||
l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s",
|
||||
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack)
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) {
|
||||
name := ruleProvider.Name()
|
||||
if slices.Contains(l.options.RouteAddressSet, name) {
|
||||
l.updateRule(ruleProvider, false, true)
|
||||
return
|
||||
}
|
||||
if slices.Contains(l.options.RouteExcludeAddressSet, name) {
|
||||
l.updateRule(ruleProvider, true, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type toIpCidr interface {
|
||||
ToIpCidr() *netipx.IPSet
|
||||
}
|
||||
|
||||
func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) {
|
||||
l.ruleUpdateMutex.Lock()
|
||||
defer l.ruleUpdateMutex.Unlock()
|
||||
name := ruleProvider.Name()
|
||||
switch rp := ruleProvider.Strategy().(type) {
|
||||
case toIpCidr:
|
||||
if !exclude {
|
||||
ipCidr := rp.ToIpCidr()
|
||||
if ipCidr != nil {
|
||||
l.routeAddressMap[name] = ipCidr
|
||||
} else {
|
||||
delete(l.routeAddressMap, name)
|
||||
}
|
||||
l.routeAddressSet = maps.Values(l.routeAddressMap)
|
||||
} else {
|
||||
ipCidr := rp.ToIpCidr()
|
||||
if ipCidr != nil {
|
||||
l.routeExcludeAddressMap[name] = ipCidr
|
||||
} else {
|
||||
delete(l.routeExcludeAddressMap, name)
|
||||
}
|
||||
l.routeExcludeAddressSet = maps.Values(l.routeExcludeAddressMap)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
if update && l.autoRedirect != nil {
|
||||
l.autoRedirect.UpdateRouteAddressSet()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) FlushDefaultInterface() {
|
||||
if l.options.AutoDetectInterface {
|
||||
for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} {
|
||||
|
@ -336,11 +529,11 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
|
|||
}
|
||||
var start, end uint64
|
||||
var err error
|
||||
start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32)
|
||||
start, err = strconv.ParseUint(uidRange[:subIndex], 0, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range start")
|
||||
}
|
||||
end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32)
|
||||
end, err = strconv.ParseUint(uidRange[subIndex+1:], 0, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range end")
|
||||
}
|
||||
|
@ -352,9 +545,14 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
|
|||
func (l *Listener) Close() error {
|
||||
l.closed = true
|
||||
resolver.RemoveSystemDnsBlacklist(l.dnsServerIp...)
|
||||
if l.autoRedirectOutputMark != 0 {
|
||||
dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0)
|
||||
}
|
||||
return common.Close(
|
||||
l.ruleUpdateCallbackCloser,
|
||||
l.tunStack,
|
||||
l.tunIf,
|
||||
l.autoRedirect,
|
||||
l.defaultInterfaceMonitor,
|
||||
l.networkUpdateMonitor,
|
||||
l.packageManager,
|
||||
|
|
|
@ -105,9 +105,9 @@ func listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, erro
|
|||
buf := pool.Get(pool.UDPBufferSize)
|
||||
br, err := lc.Read(buf)
|
||||
if err != nil {
|
||||
pool.Put(buf)
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
log.Debugln("TProxy local conn listener exit.. rAddr=%s lAddr=%s", rAddr.String(), lAddr.String())
|
||||
pool.Put(buf)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,9 +119,7 @@ func CleanupTProxyIPTables() {
|
|||
|
||||
log.Warnln("Cleanup tproxy linux iptables")
|
||||
|
||||
if int(dialer.DefaultRoutingMark.Load()) == 2158 {
|
||||
dialer.DefaultRoutingMark.Store(0)
|
||||
}
|
||||
dialer.DefaultRoutingMark.CompareAndSwap(2158, 0)
|
||||
|
||||
if _, err := cmd.ExecCmd("iptables -t mangle -L mihomo_divert"); err != nil {
|
||||
return
|
||||
|
|
26
main.go
26
main.go
|
@ -113,23 +113,19 @@ func main() {
|
|||
}
|
||||
|
||||
if C.GeoAutoUpdate {
|
||||
updateNotification := make(chan struct{})
|
||||
go updater.RegisterGeoUpdater(updateNotification)
|
||||
|
||||
go func() {
|
||||
for range updateNotification {
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] update GEO databases failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnln("[GEO] update GEO databases success, applying config")
|
||||
|
||||
executor.ApplyConfig(cfg, false)
|
||||
updater.RegisterGeoUpdater(func() {
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] update GEO databases failed: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
log.Warnln("[GEO] update GEO databases success, applying config")
|
||||
|
||||
executor.ApplyConfig(cfg, false)
|
||||
})
|
||||
}
|
||||
|
||||
defer executor.Shutdown()
|
||||
|
||||
termSign := make(chan os.Signal, 1)
|
||||
|
|
|
@ -20,6 +20,8 @@ func (b *Base) ShouldResolveIP() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (b *Base) ProviderNames() []string { return nil }
|
||||
|
||||
func HasNoResolve(params []string) bool {
|
||||
for _, p := range params {
|
||||
if p == noResolve {
|
||||
|
|
|
@ -2,12 +2,13 @@ package logic
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/rules/common"
|
||||
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
)
|
||||
|
||||
type Logic struct {
|
||||
|
@ -243,7 +244,7 @@ func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Ru
|
|||
for _, rule := range subRules[name] {
|
||||
if m, a := rule.Match(metadata); m {
|
||||
if rule.RuleType() == C.SubRules {
|
||||
matchSubRules(metadata, rule.Adapter(), subRules)
|
||||
return matchSubRules(metadata, rule.Adapter(), subRules)
|
||||
} else {
|
||||
return m, a
|
||||
}
|
||||
|
@ -298,3 +299,10 @@ func (logic *Logic) ShouldResolveIP() bool {
|
|||
func (logic *Logic) ShouldFindProcess() bool {
|
||||
return logic.needProcess
|
||||
}
|
||||
|
||||
func (logic *Logic) ProviderNames() (names []string) {
|
||||
for _, rule := range logic.rules {
|
||||
names = append(names, rule.ProviderNames()...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"github.com/metacubex/mihomo/component/cidr"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
type ipcidrStrategy struct {
|
||||
|
@ -52,6 +54,10 @@ func (i *ipcidrStrategy) FinishInsert() {
|
|||
i.cidrSet.Merge()
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet {
|
||||
return i.cidrSet.ToIPSet()
|
||||
}
|
||||
|
||||
func NewIPCidrStrategy() *ipcidrStrategy {
|
||||
return &ipcidrStrategy{}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ type UpdatableProvider interface {
|
|||
UpdatedAt() time.Time
|
||||
}
|
||||
|
||||
func (f *ruleSetProvider) UpdatedAt() time.Time {
|
||||
return f.Fetcher.UpdatedAt
|
||||
func (rp *ruleSetProvider) UpdatedAt() time.Time {
|
||||
return rp.Fetcher.UpdatedAt
|
||||
}
|
||||
|
||||
func (rp *ruleSetProvider) Close() error {
|
||||
|
|
|
@ -4,23 +4,26 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/component/resource"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
P "github.com/metacubex/mihomo/constant/provider"
|
||||
)
|
||||
|
||||
var (
|
||||
ruleProviders = map[string]P.RuleProvider{}
|
||||
)
|
||||
var tunnel P.Tunnel
|
||||
|
||||
func SetTunnel(t P.Tunnel) {
|
||||
tunnel = t
|
||||
}
|
||||
|
||||
type ruleSetProvider struct {
|
||||
*resource.Fetcher[any]
|
||||
*resource.Fetcher[ruleStrategy]
|
||||
behavior P.RuleBehavior
|
||||
format P.RuleFormat
|
||||
strategy ruleStrategy
|
||||
|
@ -49,16 +52,6 @@ type ruleStrategy interface {
|
|||
FinishInsert()
|
||||
}
|
||||
|
||||
func RuleProviders() map[string]P.RuleProvider {
|
||||
return ruleProviders
|
||||
}
|
||||
|
||||
func SetRuleProvider(ruleProvider P.RuleProvider) {
|
||||
if ruleProvider != nil {
|
||||
ruleProviders[(ruleProvider).Name()] = ruleProvider
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *ruleSetProvider) Type() P.ProviderType {
|
||||
return P.Rule
|
||||
}
|
||||
|
@ -99,8 +92,8 @@ func (rp *ruleSetProvider) ShouldFindProcess() bool {
|
|||
return rp.strategy.ShouldFindProcess()
|
||||
}
|
||||
|
||||
func (rp *ruleSetProvider) AsRule(adaptor string) C.Rule {
|
||||
panic("implement me")
|
||||
func (rp *ruleSetProvider) Strategy() any {
|
||||
return rp.strategy
|
||||
}
|
||||
|
||||
func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) {
|
||||
|
@ -123,13 +116,15 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma
|
|||
format: format,
|
||||
}
|
||||
|
||||
onUpdate := func(elm interface{}) {
|
||||
strategy := elm.(ruleStrategy)
|
||||
onUpdate := func(strategy ruleStrategy) {
|
||||
rp.strategy = strategy
|
||||
tunnel.RuleUpdateCallback().Emit(rp)
|
||||
}
|
||||
|
||||
rp.strategy = newStrategy(behavior, parse)
|
||||
rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse), format) }, onUpdate)
|
||||
rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (ruleStrategy, error) {
|
||||
return rulesParse(bytes, newStrategy(behavior, parse), format)
|
||||
}, onUpdate)
|
||||
|
||||
wrapper := &RuleSetProvider{
|
||||
rp,
|
||||
|
@ -158,7 +153,7 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string,
|
|||
|
||||
var ErrNoPayload = errors.New("file must have a `payload` field")
|
||||
|
||||
func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, error) {
|
||||
func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) {
|
||||
strategy.Reset()
|
||||
|
||||
schema := &RulePayload{}
|
||||
|
@ -176,15 +171,14 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, er
|
|||
line = buf[s : i+1]
|
||||
s = i + 1
|
||||
} else {
|
||||
s = len(buf) // stop loop in next step
|
||||
if firstLineLength == 0 { // no head or only one line body
|
||||
s = len(buf) // stop loop in next step
|
||||
if firstLineLength == 0 && format == P.YamlRule { // no head or only one line body
|
||||
return nil, ErrNoPayload
|
||||
}
|
||||
}
|
||||
var str string
|
||||
switch format {
|
||||
case P.TextRule:
|
||||
firstLineLength = -1 // don't return ErrNoPayload when read last line
|
||||
str = string(line)
|
||||
str = strings.TrimSpace(str)
|
||||
if len(str) == 0 {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
P "github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/rules/common"
|
||||
|
@ -11,13 +10,18 @@ type RuleSet struct {
|
|||
*common.Base
|
||||
ruleProviderName string
|
||||
adapter string
|
||||
ruleProvider P.RuleProvider
|
||||
noResolveIP bool
|
||||
shouldFindProcess bool
|
||||
}
|
||||
|
||||
func (rs *RuleSet) ShouldFindProcess() bool {
|
||||
return rs.shouldFindProcess || rs.getProviders().ShouldFindProcess()
|
||||
if rs.shouldFindProcess {
|
||||
return true
|
||||
}
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.ShouldFindProcess()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rs *RuleSet) RuleType() C.RuleType {
|
||||
|
@ -25,7 +29,10 @@ func (rs *RuleSet) RuleType() C.RuleType {
|
|||
}
|
||||
|
||||
func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) {
|
||||
return rs.getProviders().Match(metadata), rs.adapter
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.Match(metadata), rs.adapter
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (rs *RuleSet) Adapter() string {
|
||||
|
@ -33,31 +40,37 @@ func (rs *RuleSet) Adapter() string {
|
|||
}
|
||||
|
||||
func (rs *RuleSet) Payload() string {
|
||||
return rs.getProviders().Name()
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.Name()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rs *RuleSet) ShouldResolveIP() bool {
|
||||
return !rs.noResolveIP && rs.getProviders().ShouldResolveIP()
|
||||
}
|
||||
func (rs *RuleSet) getProviders() P.RuleProvider {
|
||||
if rs.ruleProvider == nil {
|
||||
rp := RuleProviders()[rs.ruleProviderName]
|
||||
rs.ruleProvider = rp
|
||||
if rs.noResolveIP {
|
||||
return false
|
||||
}
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.ShouldResolveIP()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return rs.ruleProvider
|
||||
func (rs *RuleSet) ProviderNames() []string {
|
||||
return []string{rs.ruleProviderName}
|
||||
}
|
||||
|
||||
func (rs *RuleSet) getProvider() (P.RuleProvider, bool) {
|
||||
pp, ok := tunnel.RuleProviders()[rs.ruleProviderName]
|
||||
return pp, ok
|
||||
}
|
||||
|
||||
func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*RuleSet, error) {
|
||||
rp, ok := RuleProviders()[ruleProviderName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("rule set %s not found", ruleProviderName)
|
||||
}
|
||||
return &RuleSet{
|
||||
rs := &RuleSet{
|
||||
Base: &common.Base{},
|
||||
ruleProviderName: ruleProviderName,
|
||||
adapter: adapter,
|
||||
ruleProvider: rp,
|
||||
noResolveIP: noResolveIP,
|
||||
}, nil
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/metacubex/mihomo/transport/hysteria/obfs"
|
||||
"github.com/metacubex/mihomo/transport/hysteria/utils"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -86,7 +86,7 @@ func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterva
|
|||
serverAddrs: serverAddrs,
|
||||
hopInterval: hopInterval,
|
||||
obfs: obfs,
|
||||
addrIndex: fastrand.Intn(len(serverAddrs)),
|
||||
addrIndex: randv2.IntN(len(serverAddrs)),
|
||||
recvQueue: make(chan *udpPacket, packetQueueSize),
|
||||
closeChan: make(chan struct{}),
|
||||
bufPool: sync.Pool{
|
||||
|
@ -177,7 +177,7 @@ func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Ad
|
|||
_ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize)
|
||||
}
|
||||
go c.recvRoutine(c.currentConn)
|
||||
c.addrIndex = fastrand.Intn(len(c.serverAddrs))
|
||||
c.addrIndex = randv2.IntN(len(c.serverAddrs))
|
||||
}
|
||||
|
||||
func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/transport/hysteria/obfs"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
const udpBufferSize = 65535
|
||||
|
@ -31,7 +31,7 @@ func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChat
|
|||
obfs: obfs,
|
||||
readBuf: make([]byte, udpBufferSize),
|
||||
writeBuf: make([]byte, udpBufferSize),
|
||||
sn: fastrand.Uint32() & 0xFFFF,
|
||||
sn: randv2.Uint32() & 0xFFFF,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/lunixbochs/struc"
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/quic-go/congestion"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -405,7 +405,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error {
|
|||
var errSize *quic.DatagramTooLargeError
|
||||
if errors.As(err, &errSize) {
|
||||
// need to frag
|
||||
msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
|
||||
msg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
|
||||
fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize))
|
||||
for _, fragMsg := range fragMsgs {
|
||||
msgBuf.Reset()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package obfs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
// [salt][obfuscated payload]
|
||||
|
@ -35,7 +34,7 @@ func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int {
|
|||
}
|
||||
|
||||
func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int {
|
||||
_, _ = fastrand.Read(out[:saltLen]) // salt
|
||||
_, _ = rand.Read(out[:saltLen]) // salt
|
||||
// Obfuscate the payload
|
||||
key := sha256.Sum256(append(x.Key, out[:saltLen]...))
|
||||
for i, c := range in {
|
||||
|
|
|
@ -2,6 +2,7 @@ package obfs
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
// HTTPObfs is shadowsocks http simple-obfs implementation
|
||||
|
@ -64,12 +65,12 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) {
|
|||
func (ho *HTTPObfs) Write(b []byte) (int, error) {
|
||||
if ho.firstRequest {
|
||||
randBytes := make([]byte, 16)
|
||||
fastrand.Read(randBytes)
|
||||
rand.Read(randBytes)
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:]))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2))
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", randv2.Int()%54, randv2.Int()%2))
|
||||
req.Header.Set("Upgrade", "websocket")
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Host = ho.host
|
||||
|
|
|
@ -2,14 +2,13 @@ package obfs
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -127,8 +126,8 @@ func NewTLSObfs(conn net.Conn, server string) net.Conn {
|
|||
func makeClientHelloMsg(data []byte, server string) []byte {
|
||||
random := make([]byte, 28)
|
||||
sessionID := make([]byte, 32)
|
||||
fastrand.Read(random)
|
||||
fastrand.Read(sessionID)
|
||||
rand.Read(random)
|
||||
rand.Read(sessionID)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -82,7 +82,7 @@ func (c *httpConn) Write(b []byte) (int, error) {
|
|||
bLength := len(b)
|
||||
headDataLength := bLength
|
||||
if bLength-headLength > 64 {
|
||||
headDataLength = headLength + fastrand.Intn(65)
|
||||
headDataLength = headLength + randv2.IntN(65)
|
||||
}
|
||||
headData := b[:headDataLength]
|
||||
b = b[headDataLength:]
|
||||
|
@ -100,7 +100,7 @@ func (c *httpConn) Write(b []byte) (int, error) {
|
|||
}
|
||||
}
|
||||
hosts := strings.Split(host, ",")
|
||||
host = hosts[fastrand.Intn(len(hosts))]
|
||||
host = hosts[randv2.IntN(len(hosts))]
|
||||
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
|
@ -119,7 +119,7 @@ func (c *httpConn) Write(b []byte) (int, error) {
|
|||
buf.WriteString(body + "\r\n\r\n")
|
||||
} else {
|
||||
buf.WriteString("User-Agent: ")
|
||||
buf.WriteString(userAgent[fastrand.Intn(len(userAgent))])
|
||||
buf.WriteString(userAgent[randv2.IntN(len(userAgent))])
|
||||
buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n")
|
||||
if c.post {
|
||||
packBoundary(buf)
|
||||
|
@ -147,7 +147,7 @@ func packBoundary(buf *bytes.Buffer) {
|
|||
buf.WriteString("Content-Type: multipart/form-data; boundary=")
|
||||
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
for i := 0; i < 32; i++ {
|
||||
buf.WriteByte(set[fastrand.Intn(62)])
|
||||
buf.WriteByte(set[randv2.IntN(62)])
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package obfs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -54,10 +55,10 @@ func (c *randomHeadConn) Write(b []byte) (int, error) {
|
|||
c.buf = append(c.buf, b...)
|
||||
if !c.hasSentHeader {
|
||||
c.hasSentHeader = true
|
||||
dataLength := fastrand.Intn(96) + 4
|
||||
dataLength := randv2.IntN(96) + 4
|
||||
buf := pool.Get(dataLength + 4)
|
||||
defer pool.Put(buf)
|
||||
fastrand.Read(buf[:dataLength])
|
||||
rand.Read(buf[:dataLength])
|
||||
binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength]))
|
||||
_, err := c.Conn.Write(buf)
|
||||
return len(b), err
|
||||
|
|
|
@ -3,6 +3,7 @@ package obfs
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strings"
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/transport/ssr/tools"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -26,7 +27,7 @@ type tls12Ticket struct {
|
|||
|
||||
func newTLS12Ticket(b *Base) Obfs {
|
||||
r := &tls12Ticket{Base: b, authData: &authData{}}
|
||||
fastrand.Read(r.clientID[:])
|
||||
rand.Read(r.clientID[:])
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -91,7 +92,7 @@ func (c *tls12TicketConn) Write(b []byte) (int, error) {
|
|||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
for len(b) > 2048 {
|
||||
size := fastrand.Intn(4096) + 100
|
||||
size := randv2.IntN(4096) + 100
|
||||
if len(b) < size {
|
||||
size = len(b)
|
||||
}
|
||||
|
@ -197,7 +198,7 @@ func packSNIData(buf *bytes.Buffer, u string) {
|
|||
}
|
||||
|
||||
func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) {
|
||||
length := 16 * (fastrand.Intn(17) + 8)
|
||||
length := 16 * (randv2.IntN(17) + 8)
|
||||
buf.Write([]byte{0, 0x23})
|
||||
binary.Write(buf, binary.BigEndian, uint16(length))
|
||||
tools.AppendRandBytes(buf, length)
|
||||
|
@ -222,6 +223,6 @@ func (t *tls12Ticket) getHost() string {
|
|||
host = ""
|
||||
}
|
||||
hosts := strings.Split(host, ",")
|
||||
host = hosts[fastrand.Intn(len(hosts))]
|
||||
host = hosts[randv2.IntN(len(hosts))]
|
||||
return host
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package protocol
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"net"
|
||||
|
@ -13,7 +14,7 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/transport/ssr/tools"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -66,7 +67,7 @@ func (a *authAES128) initUserData() {
|
|||
}
|
||||
if len(a.userKey) == 0 {
|
||||
a.userKey = a.Key
|
||||
fastrand.Read(a.userID[:])
|
||||
rand.Read(a.userID[:])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +201,7 @@ func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength
|
|||
}
|
||||
|
||||
func trapezoidRandom(max int, d float64) int {
|
||||
base := fastrand.Float64()
|
||||
base := randv2.Float64()
|
||||
if d-0 > 1e-6 {
|
||||
a := 1 - d
|
||||
base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d)
|
||||
|
@ -221,10 +222,10 @@ func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int
|
|||
if revLength > -1460 {
|
||||
return trapezoidRandom(revLength+1460, -0.3)
|
||||
}
|
||||
return fastrand.Intn(32)
|
||||
return randv2.IntN(32)
|
||||
}
|
||||
if dataLength > 900 {
|
||||
return fastrand.Intn(revLength)
|
||||
return randv2.IntN(revLength)
|
||||
}
|
||||
return trapezoidRandom(revLength, -0.3)
|
||||
}
|
||||
|
@ -249,7 +250,7 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
|||
copy(macKey, a.iv)
|
||||
copy(macKey[len(a.iv):], a.Key)
|
||||
|
||||
poolBuf.WriteByte(byte(fastrand.Intn(256)))
|
||||
poolBuf.WriteByte(byte(randv2.IntN(256)))
|
||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6])
|
||||
poolBuf.Write(a.userID[:])
|
||||
err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt)
|
||||
|
@ -265,9 +266,9 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
|||
|
||||
func (a *authAES128) getRandDataLengthForPackAuthData(size int) int {
|
||||
if size > 400 {
|
||||
return fastrand.Intn(512)
|
||||
return randv2.IntN(512)
|
||||
}
|
||||
return fastrand.Intn(1024)
|
||||
return randv2.IntN(1024)
|
||||
}
|
||||
|
||||
func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/transport/ssr/tools"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -178,7 +178,7 @@ func (a *authSHA1V4) getRandDataLength(size int) int {
|
|||
return 0
|
||||
}
|
||||
if size > 400 {
|
||||
return fastrand.Intn(256)
|
||||
return randv2.IntN(256)
|
||||
}
|
||||
return fastrand.Intn(512)
|
||||
return randv2.IntN(512)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
@ -13,7 +14,7 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
|
@ -38,8 +39,8 @@ func (a *authData) next() *authData {
|
|||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
if a.connectionID > 0xff000000 || a.connectionID == 0 {
|
||||
fastrand.Read(a.clientID[:])
|
||||
a.connectionID = fastrand.Uint32() & 0xffffff
|
||||
rand.Read(a.clientID[:])
|
||||
a.connectionID = randv2.Uint32() & 0xffffff
|
||||
}
|
||||
a.connectionID++
|
||||
copy(r.clientID[:], a.clientID[:])
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -71,7 +71,7 @@ func getHeadSize(b []byte, defaultValue int) int {
|
|||
|
||||
func getDataLength(b []byte) int {
|
||||
bLength := len(b)
|
||||
dataLength := getHeadSize(b, 30) + fastrand.Intn(32)
|
||||
dataLength := getHeadSize(b, 30) + randv2.IntN(32)
|
||||
if bLength < dataLength {
|
||||
return bLength
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) {
|
|||
quicConn.SetCongestionControl(
|
||||
congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
congestion.GetInitialPacketSize(quicConn.RemoteAddr()),
|
||||
congestion.GetInitialPacketSize(quicConn),
|
||||
false,
|
||||
),
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) {
|
|||
quicConn.SetCongestionControl(
|
||||
congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
congestion.GetInitialPacketSize(quicConn.RemoteAddr()),
|
||||
congestion.GetInitialPacketSize(quicConn),
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) {
|
|||
quicConn.SetCongestionControl(
|
||||
congestion.NewBBRSender(
|
||||
congestion.DefaultClock{},
|
||||
congestion.GetInitialPacketSize(quicConn.RemoteAddr()),
|
||||
congestion.GetInitialPacketSize(quicConn),
|
||||
c.ByteCount(cwnd)*congestion.InitialMaxDatagramSize,
|
||||
congestion.DefaultBBRMaxCongestionWindow*congestion.InitialMaxDatagramSize,
|
||||
),
|
||||
|
@ -49,7 +49,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) {
|
|||
quicConn.SetCongestionControl(
|
||||
congestionv2.NewBbrSender(
|
||||
congestionv2.DefaultClock{},
|
||||
congestionv2.GetInitialPacketSize(quicConn.RemoteAddr()),
|
||||
congestionv2.GetInitialPacketSize(quicConn),
|
||||
c.ByteCount(cwnd),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -5,34 +5,23 @@ package congestion
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/quic-go/congestion"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
const (
|
||||
// InitialMaxDatagramSize is the default maximum packet size used in QUIC for congestion window computations in bytes.
|
||||
InitialMaxDatagramSize = 1252
|
||||
InitialPacketSizeIPv4 = 1252
|
||||
InitialPacketSizeIPv6 = 1232
|
||||
InitialMaxDatagramSize = 1280
|
||||
InitialPacketSize = 1280
|
||||
InitialCongestionWindow = 32
|
||||
DefaultBBRMaxCongestionWindow = 10000
|
||||
)
|
||||
|
||||
func GetInitialPacketSize(addr net.Addr) congestion.ByteCount {
|
||||
maxSize := congestion.ByteCount(1200)
|
||||
// If this is not a UDP address, we don't know anything about the MTU.
|
||||
// Use the minimum size of an Initial packet as the max packet size.
|
||||
if udpAddr, ok := addr.(*net.UDPAddr); ok {
|
||||
if udpAddr.IP.To4() != nil {
|
||||
maxSize = InitialPacketSizeIPv4
|
||||
} else {
|
||||
maxSize = InitialPacketSizeIPv6
|
||||
}
|
||||
}
|
||||
return congestion.ByteCount(maxSize)
|
||||
func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount {
|
||||
return congestion.ByteCount(quicConn.Config().InitialPacketSize)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -727,7 +716,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) {
|
|||
// Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
|
||||
// excluded because in that case increased gain and decreased gain would not
|
||||
// follow each other.
|
||||
b.cycleCurrentOffset = fastrand.Int() % (GainCycleLength - 1)
|
||||
b.cycleCurrentOffset = randv2.Int() % (GainCycleLength - 1)
|
||||
if b.cycleCurrentOffset >= 1 {
|
||||
b.cycleCurrentOffset += 1
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const (
|
|||
cubeCongestionWindowScale = 410
|
||||
cubeFactor congestion.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize
|
||||
// TODO: when re-enabling cubic, make sure to use the actual packet size here
|
||||
maxDatagramSize = congestion.ByteCount(InitialPacketSizeIPv4)
|
||||
maxDatagramSize = congestion.ByteCount(InitialPacketSize)
|
||||
)
|
||||
|
||||
const defaultNumConnections = 1
|
||||
|
|
|
@ -4,12 +4,12 @@ package congestion
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/quic-go/congestion"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
// BbrSender implements BBR congestion control algorithm. BBR aims to estimate
|
||||
|
@ -30,7 +30,7 @@ const (
|
|||
// Constants based on TCP defaults.
|
||||
// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.
|
||||
// Does not inflate the pacing rate.
|
||||
defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSizeIPv4)
|
||||
defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSize)
|
||||
|
||||
// The gain used for the STARTUP, equal to 2/ln(2).
|
||||
defaultHighGain = 2.885
|
||||
|
@ -62,7 +62,7 @@ const (
|
|||
// Flag.
|
||||
defaultStartupFullLossCount = 8
|
||||
quicBbr2DefaultLossThreshold = 0.02
|
||||
maxBbrBurstPackets = 3
|
||||
maxBbrBurstPackets = 10
|
||||
)
|
||||
|
||||
type bbrMode int
|
||||
|
@ -334,6 +334,8 @@ func (b *bbrSender) OnPacketSent(
|
|||
}
|
||||
|
||||
b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable)
|
||||
|
||||
b.maybeAppLimited(bytesInFlight)
|
||||
}
|
||||
|
||||
// CanSend implements the SendAlgorithm interface.
|
||||
|
@ -413,8 +415,6 @@ func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, even
|
|||
// packet in lost_packets.
|
||||
var lastPacketSendState sendTimeState
|
||||
|
||||
b.maybeApplimited(priorInFlight)
|
||||
|
||||
// Update bytesInFlight
|
||||
b.bytesInFlight = priorInFlight
|
||||
for _, p := range ackedPackets {
|
||||
|
@ -541,7 +541,7 @@ func (b *bbrSender) setDrainGain(drainGain float64) {
|
|||
b.drainGain = drainGain
|
||||
}
|
||||
|
||||
// What's the current estimated bandwidth in bytes per second.
|
||||
// Get the current bandwidth estimate. Note that Bandwidth is in bits per second.
|
||||
func (b *bbrSender) bandwidthEstimate() Bandwidth {
|
||||
return b.maxBandwidth.GetBest()
|
||||
}
|
||||
|
@ -620,7 +620,7 @@ func (b *bbrSender) enterProbeBandwidthMode(now time.Time) {
|
|||
// Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
|
||||
// excluded because in that case increased gain and decreased gain would not
|
||||
// follow each other.
|
||||
b.cycleCurrentOffset = int(fastrand.Int31n(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1)
|
||||
b.cycleCurrentOffset = int(randv2.Int32N(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1)
|
||||
if b.cycleCurrentOffset >= 1 {
|
||||
b.cycleCurrentOffset += 1
|
||||
}
|
||||
|
@ -700,14 +700,13 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta
|
|||
}
|
||||
}
|
||||
|
||||
func (b *bbrSender) maybeApplimited(bytesInFlight congestion.ByteCount) {
|
||||
func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) {
|
||||
congestionWindow := b.GetCongestionWindow()
|
||||
if bytesInFlight >= congestionWindow {
|
||||
return
|
||||
}
|
||||
availableBytes := congestionWindow - bytesInFlight
|
||||
drainLimited := b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2
|
||||
if !drainLimited || availableBytes > maxBbrBurstPackets*b.maxDatagramSize {
|
||||
if availableBytes > maxBbrBurstPackets*b.maxDatagramSize {
|
||||
b.sampler.OnAppLimited()
|
||||
}
|
||||
}
|
||||
|
@ -931,16 +930,6 @@ func bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.B
|
|||
return congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second)
|
||||
}
|
||||
|
||||
func GetInitialPacketSize(addr net.Addr) congestion.ByteCount {
|
||||
// If this is not a UDP address, we don't know anything about the MTU.
|
||||
// Use the minimum size of an Initial packet as the max packet size.
|
||||
if udpAddr, ok := addr.(*net.UDPAddr); ok {
|
||||
if udpAddr.IP.To4() != nil {
|
||||
return congestion.InitialPacketSizeIPv4
|
||||
} else {
|
||||
return congestion.InitialPacketSizeIPv6
|
||||
}
|
||||
} else {
|
||||
return congestion.MinInitialPacketSize
|
||||
}
|
||||
func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount {
|
||||
return congestion.ByteCount(quicConn.Config().InitialPacketSize)
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ type Pacer struct {
|
|||
|
||||
func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer {
|
||||
p := &Pacer{
|
||||
budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSizeIPv4,
|
||||
maxDatagramSize: congestion.InitialPacketSizeIPv4,
|
||||
budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSize,
|
||||
maxDatagramSize: congestion.InitialPacketSize,
|
||||
getBandwidth: getBandwidth,
|
||||
}
|
||||
return p
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
"github.com/metacubex/mihomo/transport/tuic/common"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
type ClientOption struct {
|
||||
|
@ -367,7 +367,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met
|
|||
pipe1, pipe2 := N.Pipe()
|
||||
var connId uint32
|
||||
for {
|
||||
connId = fastrand.Uint32()
|
||||
connId = randv2.Uint32()
|
||||
_, loaded := t.udpInputMap.LoadOrStore(connId, pipe1)
|
||||
if !loaded {
|
||||
break
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
"github.com/metacubex/mihomo/transport/tuic/common"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
type ClientOption struct {
|
||||
|
@ -351,7 +351,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met
|
|||
pipe1, pipe2 := N.Pipe()
|
||||
var connId uint16
|
||||
for {
|
||||
connId = uint16(fastrand.Intn(0xFFFF))
|
||||
connId = uint16(randv2.IntN(0xFFFF))
|
||||
_, loaded := t.udpInputMap.LoadOrStore(connId, pipe1)
|
||||
if !loaded {
|
||||
break
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/metacubex/mihomo/transport/tuic/common"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type quicStreamPacketConn struct {
|
||||
|
@ -157,7 +157,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
pktId := uint16(fastrand.Uint32())
|
||||
pktId := uint16(randv2.Uint32())
|
||||
packet := NewPacket(q.connId, pktId, 1, 0, uint16(len(p)), address, p)
|
||||
switch q.udpRelayMode {
|
||||
case common.QUIC:
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -25,9 +25,9 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid
|
|||
if contentLen < 900 {
|
||||
if paddingTLS {
|
||||
//log.Debugln("long padding")
|
||||
paddingLen = fastrand.Int31n(500) + 900 - contentLen
|
||||
paddingLen = randv2.Int32N(500) + 900 - contentLen
|
||||
} else {
|
||||
paddingLen = fastrand.Int31n(256)
|
||||
paddingLen = randv2.Int32N(256)
|
||||
}
|
||||
}
|
||||
if userUUID != nil {
|
||||
|
@ -49,9 +49,9 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding
|
|||
if contentLen < 900 {
|
||||
if paddingTLS {
|
||||
//log.Debugln("long padding")
|
||||
paddingLen = fastrand.Int31n(500) + 900 - contentLen
|
||||
paddingLen = randv2.Int32N(500) + 900 - contentLen
|
||||
} else {
|
||||
paddingLen = fastrand.Int31n(256)
|
||||
paddingLen = randv2.Int32N(256)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
|
@ -72,7 +73,7 @@ func (vc *Conn) sendRequest() error {
|
|||
buf.WriteByte(vc.respV)
|
||||
buf.WriteByte(OptionChunkStream)
|
||||
|
||||
p := fastrand.Intn(16)
|
||||
p := randv2.IntN(16)
|
||||
// P Sec Reserve Cmd
|
||||
buf.WriteByte(byte(p<<4) | byte(vc.security))
|
||||
buf.WriteByte(0)
|
||||
|
@ -90,7 +91,7 @@ func (vc *Conn) sendRequest() error {
|
|||
// padding
|
||||
if p > 0 {
|
||||
padding := make([]byte, p)
|
||||
fastrand.Read(padding)
|
||||
rand.Read(padding)
|
||||
buf.Write(padding)
|
||||
}
|
||||
|
||||
|
@ -196,7 +197,7 @@ func hashTimestamp(t time.Time) []byte {
|
|||
// newConn return a Conn instance
|
||||
func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) {
|
||||
randBytes := make([]byte, 33)
|
||||
fastrand.Read(randBytes)
|
||||
rand.Read(randBytes)
|
||||
reqBodyIV := make([]byte, 16)
|
||||
reqBodyKey := make([]byte, 16)
|
||||
copy(reqBodyIV[:], randBytes[:16])
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
|
@ -27,7 +27,7 @@ type H2Config struct {
|
|||
func (hc *h2Conn) establishConn() error {
|
||||
preader, pwriter := io.Pipe()
|
||||
|
||||
host := hc.cfg.Hosts[fastrand.Intn(len(hc.cfg.Hosts))]
|
||||
host := hc.cfg.Hosts[randv2.IntN(len(hc.cfg.Hosts))]
|
||||
path := hc.cfg.Path
|
||||
// TODO: connect use VMess Host instead of H2 Host
|
||||
req := http.Request{
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type httpConn struct {
|
||||
|
@ -59,10 +59,10 @@ func (hc *httpConn) Write(b []byte) (int, error) {
|
|||
return -1, errors.New("path is empty")
|
||||
}
|
||||
|
||||
path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))]
|
||||
path := hc.cfg.Path[randv2.IntN(len(hc.cfg.Path))]
|
||||
host := hc.cfg.Host
|
||||
if header := hc.cfg.Headers["Host"]; len(header) != 0 {
|
||||
host = header[fastrand.Intn(len(header))]
|
||||
host = header[randv2.IntN(len(header))]
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path)
|
||||
|
@ -71,7 +71,7 @@ func (hc *httpConn) Write(b []byte) (int, error) {
|
|||
return 0, err
|
||||
}
|
||||
for key, list := range hc.cfg.Headers {
|
||||
req.Header.Set(key, list[fastrand.Intn(len(list))])
|
||||
req.Header.Set(key, list[randv2.IntN(len(list))])
|
||||
}
|
||||
req.ContentLength = int64(len(b))
|
||||
if err := req.Write(hc.Conn); err != nil {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/utils"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
// Version of vmess
|
||||
|
@ -78,7 +78,7 @@ type Config struct {
|
|||
|
||||
// StreamConn return a Conn with net.Conn and DstAddr
|
||||
func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
|
||||
r := fastrand.Intn(len(c.user))
|
||||
r := randv2.IntN(len(c.user))
|
||||
return newConn(conn, c.user[r], dst, c.security, c.isAead)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
|
@ -25,7 +26,7 @@ import (
|
|||
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"github.com/metacubex/randv2"
|
||||
)
|
||||
|
||||
type websocketConn struct {
|
||||
|
@ -150,7 +151,7 @@ func (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
|||
}
|
||||
|
||||
if wsc.state.ClientSide() {
|
||||
maskKey := fastrand.Uint32()
|
||||
maskKey := randv2.Uint32()
|
||||
binary.LittleEndian.PutUint32(header[1+payloadBitLength:], maskKey)
|
||||
N.MaskWebSocket(maskKey, data)
|
||||
}
|
||||
|
@ -398,7 +399,7 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig,
|
|||
const nonceKeySize = 16
|
||||
// NOTE: bts does not escape.
|
||||
bts := make([]byte, nonceKeySize)
|
||||
if _, err = fastrand.Read(bts); err != nil {
|
||||
if _, err = rand.Read(bts); err != nil {
|
||||
return nil, fmt.Errorf("rand read error: %w", err)
|
||||
}
|
||||
secKey = base64.StdEncoding.EncodeToString(bts)
|
||||
|
|
186
tunnel/dns_dialer.go
Normal file
186
tunnel/dns_dialer.go
Normal file
|
@ -0,0 +1,186 @@
|
|||
package tunnel
|
||||
|
||||
// WARNING: all function in this file should only be using in dns module
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||
)
|
||||
|
||||
const DnsRespectRules = "RULES"
|
||||
|
||||
type DNSDialer struct {
|
||||
r resolver.Resolver
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
opts []dialer.Option
|
||||
}
|
||||
|
||||
func NewDNSDialer(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) *DNSDialer {
|
||||
return &DNSDialer{r: r, proxyAdapter: proxyAdapter, proxyName: proxyName, opts: opts}
|
||||
}
|
||||
|
||||
func (d *DNSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
r := d.r
|
||||
proxyName := d.proxyName
|
||||
proxyAdapter := d.proxyAdapter
|
||||
opts := d.opts
|
||||
var rule C.Rule
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.INNER,
|
||||
}
|
||||
err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.Contains(network, "tcp") {
|
||||
metadata.NetWork = C.UDP
|
||||
if !metadata.Resolved() {
|
||||
// udp must resolve host first
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata.DstIP = dstIP
|
||||
}
|
||||
}
|
||||
|
||||
if proxyAdapter == nil && len(proxyName) != 0 {
|
||||
if proxyName == DnsRespectRules {
|
||||
if !metadata.Resolved() {
|
||||
// resolve here before resolveMetadata to avoid its inner resolver.ResolveIP
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata.DstIP = dstIP
|
||||
}
|
||||
proxyAdapter, rule, err = resolveMetadata(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
proxyAdapter, ok = Proxies()[proxyName]
|
||||
if !ok {
|
||||
opts = append(opts, dialer.WithInterface(proxyName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if metadata.NetWork == C.TCP {
|
||||
if proxyAdapter == nil {
|
||||
opts = append(opts, dialer.WithResolver(r))
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
}
|
||||
|
||||
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
|
||||
if !metadata.Resolved() {
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata.DstIP = dstIP
|
||||
}
|
||||
metadata.Host = "" // clear host to avoid double resolve in proxy
|
||||
}
|
||||
|
||||
conn, err := proxyAdapter.DialContext(ctx, metadata, opts...)
|
||||
if err != nil {
|
||||
logMetadataErr(metadata, rule, proxyAdapter, err)
|
||||
return nil, err
|
||||
}
|
||||
logMetadata(metadata, rule, conn)
|
||||
|
||||
conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||
|
||||
return conn, nil
|
||||
} else {
|
||||
if proxyAdapter == nil {
|
||||
return dialer.DialContext(ctx, network, metadata.AddrPort().String(), opts...)
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||
}
|
||||
|
||||
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||
if err != nil {
|
||||
logMetadataErr(metadata, rule, proxyAdapter, err)
|
||||
return nil, err
|
||||
}
|
||||
logMetadata(metadata, rule, packetConn)
|
||||
|
||||
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||
|
||||
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (d *DNSDialer) ListenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) {
|
||||
r := d.r
|
||||
proxyAdapter := d.proxyAdapter
|
||||
proxyName := d.proxyName
|
||||
opts := d.opts
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
Type: C.INNER,
|
||||
}
|
||||
err := metadata.SetRemoteAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !metadata.Resolved() {
|
||||
// udp must resolve host first
|
||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata.DstIP = dstIP
|
||||
}
|
||||
|
||||
var rule C.Rule
|
||||
if proxyAdapter == nil {
|
||||
if proxyName == DnsRespectRules {
|
||||
proxyAdapter, rule, err = resolveMetadata(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
proxyAdapter, ok = Proxies()[proxyName]
|
||||
if !ok {
|
||||
opts = append(opts, dialer.WithInterface(proxyName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if proxyAdapter == nil {
|
||||
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", metadata.AddrPort())
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||
}
|
||||
|
||||
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||
if err != nil {
|
||||
logMetadataErr(metadata, rule, proxyAdapter, err)
|
||||
return nil, err
|
||||
}
|
||||
logMetadata(metadata, rule, packetConn)
|
||||
|
||||
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||
|
||||
return packetConn, nil
|
||||
}
|
116
tunnel/tunnel.go
116
tunnel/tunnel.go
|
@ -8,10 +8,12 @@ import (
|
|||
"net/netip"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/loopback"
|
||||
"github.com/metacubex/mihomo/component/nat"
|
||||
P "github.com/metacubex/mihomo/component/process"
|
||||
|
@ -49,11 +51,15 @@ var (
|
|||
findProcessMode P.FindProcessMode
|
||||
|
||||
fakeIPRange netip.Prefix
|
||||
|
||||
ruleUpdateCallback = utils.NewCallback[provider.RuleProvider]()
|
||||
)
|
||||
|
||||
type tunnel struct{}
|
||||
|
||||
var Tunnel C.Tunnel = tunnel{}
|
||||
var Tunnel = tunnel{}
|
||||
var _ C.Tunnel = Tunnel
|
||||
var _ provider.Tunnel = Tunnel
|
||||
|
||||
func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) {
|
||||
connCtx := icontext.NewConnContext(conn, metadata)
|
||||
|
@ -72,6 +78,18 @@ func (t tunnel) NatTable() C.NatTable {
|
|||
return natTable
|
||||
}
|
||||
|
||||
func (t tunnel) Providers() map[string]provider.ProxyProvider {
|
||||
return providers
|
||||
}
|
||||
|
||||
func (t tunnel) RuleProviders() map[string]provider.RuleProvider {
|
||||
return ruleProviders
|
||||
}
|
||||
|
||||
func (t tunnel) RuleUpdateCallback() *utils.Callback[provider.RuleProvider] {
|
||||
return ruleUpdateCallback
|
||||
}
|
||||
|
||||
func OnSuspend() {
|
||||
status.Store(Suspend)
|
||||
}
|
||||
|
@ -386,43 +404,18 @@ func handleUDPConn(packet C.PacketAdapter) {
|
|||
rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) {
|
||||
return proxy.ListenPacketContext(ctx, metadata.Pure())
|
||||
}, func(err error) {
|
||||
if rule == nil {
|
||||
log.Warnln(
|
||||
"[UDP] dial %s %s --> %s error: %s",
|
||||
proxy.Name(),
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
err.Error(),
|
||||
)
|
||||
} else {
|
||||
log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())
|
||||
}
|
||||
logMetadataErr(metadata, rule, proxy, err)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
logMetadata(metadata, rule, rawPc)
|
||||
|
||||
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true)
|
||||
|
||||
switch true {
|
||||
case metadata.SpecialProxy != "":
|
||||
log.Infoln("[UDP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy)
|
||||
case rule != nil:
|
||||
if rule.Payload() != "" {
|
||||
log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), rawPc.Chains().String())
|
||||
if rawPc.Chains().Last() == "REJECT-DROP" {
|
||||
pc.Close()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.Payload(), rawPc.Chains().String())
|
||||
}
|
||||
case mode == Global:
|
||||
log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
case mode == Direct:
|
||||
log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
default:
|
||||
log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
if rawPc.Chains().Last() == "REJECT-DROP" {
|
||||
pc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
oAddrPort := metadata.AddrPort()
|
||||
|
@ -539,48 +532,18 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||
}
|
||||
return
|
||||
}, func(err error) {
|
||||
if rule == nil {
|
||||
log.Warnln(
|
||||
"[TCP] dial %s %s --> %s error: %s",
|
||||
proxy.Name(),
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
err.Error(),
|
||||
)
|
||||
} else {
|
||||
log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())
|
||||
}
|
||||
logMetadataErr(metadata, rule, proxy, err)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
logMetadata(metadata, rule, remoteConn)
|
||||
|
||||
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true)
|
||||
defer func(remoteConn C.Conn) {
|
||||
_ = remoteConn.Close()
|
||||
}(remoteConn)
|
||||
|
||||
switch true {
|
||||
case metadata.SpecialProxy != "":
|
||||
log.Infoln("[TCP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy)
|
||||
case rule != nil:
|
||||
if rule.Payload() != "" {
|
||||
log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String())
|
||||
} else {
|
||||
log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String())
|
||||
}
|
||||
case mode == Global:
|
||||
log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
case mode == Direct:
|
||||
log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
default:
|
||||
log.Infoln(
|
||||
"[TCP] %s --> %s doesn't match any rule using DIRECT",
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
)
|
||||
}
|
||||
|
||||
_ = conn.SetReadDeadline(time.Now()) // stop unfinished peek
|
||||
peekMutex.Lock()
|
||||
defer peekMutex.Unlock()
|
||||
|
@ -588,6 +551,33 @@ func handleTCPConn(connCtx C.ConnContext) {
|
|||
handleSocket(conn, remoteConn)
|
||||
}
|
||||
|
||||
func logMetadataErr(metadata *C.Metadata, rule C.Rule, proxy C.ProxyAdapter, err error) {
|
||||
if rule == nil {
|
||||
log.Warnln("[%s] dial %s %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())
|
||||
} else {
|
||||
log.Warnln("[%s] dial %s (match %s/%s) %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) {
|
||||
switch {
|
||||
case metadata.SpecialProxy != "":
|
||||
log.Infoln("[%s] %s --> %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy)
|
||||
case rule != nil:
|
||||
if rule.Payload() != "" {
|
||||
log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String())
|
||||
} else {
|
||||
log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String())
|
||||
}
|
||||
case mode == Global:
|
||||
log.Infoln("[%s] %s --> %s using GLOBAL", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
case mode == Direct:
|
||||
log.Infoln("[%s] %s --> %s using DIRECT", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress())
|
||||
default:
|
||||
log.Infoln("[%s] %s --> %s doesn't match any rule using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().Last())
|
||||
}
|
||||
}
|
||||
|
||||
func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
|
||||
return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue