mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 03:32:33 +08:00
Compare commits
7 Commits
00d58f9972
...
70fb352b9c
Author | SHA1 | Date | |
---|---|---|---|
|
70fb352b9c | ||
|
2b4741fbc7 | ||
|
f317baa8de | ||
|
5678131591 | ||
|
10f8ba4434 | ||
|
256e9d8c3d | ||
|
828ba83ef3 |
|
@ -4,14 +4,19 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/loopback"
|
"github.com/metacubex/mihomo/component/loopback"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
"github.com/metacubex/mihomo/constant/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR"))
|
||||||
|
|
||||||
type Direct struct {
|
type Direct struct {
|
||||||
*Base
|
*Base
|
||||||
loopBack *loopback.Detector
|
loopBack *loopback.Detector
|
||||||
|
@ -24,9 +29,11 @@ type DirectOption struct {
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
|
if !features.CMFA && !DisableLoopBackDetector {
|
||||||
if err := d.loopBack.CheckConn(metadata); err != nil {
|
if err := d.loopBack.CheckConn(metadata); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
|
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
|
||||||
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -38,9 +45,11 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
|
if !features.CMFA && !DisableLoopBackDetector {
|
||||||
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)
|
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)
|
||||||
|
|
|
@ -304,13 +304,16 @@ func (w *WireGuard) init(ctx context.Context) error {
|
||||||
ipcConf := "private_key=" + w.option.PrivateKey
|
ipcConf := "private_key=" + w.option.PrivateKey
|
||||||
if len(w.option.Peers) > 0 {
|
if len(w.option.Peers) > 0 {
|
||||||
for i, peer := range w.option.Peers {
|
for i, peer := range w.option.Peers {
|
||||||
|
ipcConf += "\npublic_key=" + peer.PublicKey
|
||||||
destination, err := w.resolve(ctx, peer.Addr())
|
destination, err := w.resolve(ctx, peer.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// !!! do not set initErr here !!!
|
// !!! do not set initErr here !!!
|
||||||
// let us can retry domain resolve in next time
|
// 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
|
if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true
|
||||||
|
w.bind.SetConnectAddr(destination)
|
||||||
|
}
|
||||||
ipcConf += "\nendpoint=" + destination.String()
|
ipcConf += "\nendpoint=" + destination.String()
|
||||||
if peer.PreSharedKey != "" {
|
if peer.PreSharedKey != "" {
|
||||||
ipcConf += "\npreshared_key=" + peer.PreSharedKey
|
ipcConf += "\npreshared_key=" + peer.PreSharedKey
|
||||||
|
@ -332,7 +335,7 @@ func (w *WireGuard) init(ctx context.Context) error {
|
||||||
// let us can retry domain resolve in next time
|
// let us can retry domain resolve in next time
|
||||||
return E.Cause(err, "resolve endpoint domain")
|
return E.Cause(err, "resolve endpoint domain")
|
||||||
}
|
}
|
||||||
w.bind.SetConnectAddr(destination)
|
w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true
|
||||||
ipcConf += "\nendpoint=" + destination.String()
|
ipcConf += "\nendpoint=" + destination.String()
|
||||||
if w.option.PreSharedKey != "" {
|
if w.option.PreSharedKey != "" {
|
||||||
ipcConf += "\npreshared_key=" + w.option.PreSharedKey
|
ipcConf += "\npreshared_key=" + w.option.PreSharedKey
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/outbound"
|
"github.com/metacubex/mihomo/adapter/outbound"
|
||||||
|
@ -134,33 +134,33 @@ func (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func strategyRoundRobin(url string) strategyFn {
|
func strategyRoundRobin(url string) strategyFn {
|
||||||
|
var availableProxies []C.Proxy
|
||||||
idx := 0
|
idx := 0
|
||||||
idxMutex := sync.Mutex{}
|
idxMutex := sync.Mutex{}
|
||||||
|
|
||||||
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
|
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
|
||||||
idxMutex.Lock()
|
idxMutex.Lock()
|
||||||
defer idxMutex.Unlock()
|
defer idxMutex.Unlock()
|
||||||
|
|
||||||
i := 0
|
|
||||||
length := len(proxies)
|
|
||||||
|
|
||||||
if touch {
|
if touch {
|
||||||
defer func() {
|
// check list
|
||||||
idx = (idx + i) % length
|
availableProxies = []C.Proxy{}
|
||||||
}()
|
for _, proxy := range proxies {
|
||||||
}
|
|
||||||
|
|
||||||
for ; i < length; i++ {
|
|
||||||
id := (idx + i) % length
|
|
||||||
proxy := proxies[id]
|
|
||||||
if proxy.AliveForTestUrl(url) {
|
if proxy.AliveForTestUrl(url) {
|
||||||
i++
|
availableProxies = append(availableProxies, proxy)
|
||||||
return proxy
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// fallback
|
||||||
|
if len(availableProxies) == 0 {
|
||||||
return proxies[0]
|
return proxies[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
proxy := availableProxies[idx]
|
||||||
|
// reset idx
|
||||||
|
idx = (idx + 1) % len(availableProxies)
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func strategyConsistentHashing(url string) strategyFn {
|
func strategyConsistentHashing(url string) strategyFn {
|
||||||
maxRetry := 5
|
maxRetry := 5
|
||||||
|
|
|
@ -212,6 +212,7 @@ type RawDNS struct {
|
||||||
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
||||||
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
||||||
UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-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"`
|
NameServer []string `yaml:"nameserver" json:"nameserver"`
|
||||||
Fallback []string `yaml:"fallback" json:"fallback"`
|
Fallback []string `yaml:"fallback" json:"fallback"`
|
||||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
||||||
|
@ -1039,7 +1040,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) {
|
||||||
return net.JoinHostPort(hostname, port), nil
|
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
|
var nameservers []dns.NameServer
|
||||||
|
|
||||||
for idx, server := range servers {
|
for idx, server := range servers {
|
||||||
|
@ -1114,6 +1115,10 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
||||||
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.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 = append(
|
||||||
nameservers,
|
nameservers,
|
||||||
dns.NameServer{
|
dns.NameServer{
|
||||||
|
@ -1130,7 +1135,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
|
dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
|
||||||
return parseNameServer(servers, false)
|
return parseNameServer(servers, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1156,7 +1161,7 @@ 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]()
|
policy := orderedmap.New[string, []dns.NameServer]()
|
||||||
updatedPolicy := orderedmap.New[string, any]()
|
updatedPolicy := orderedmap.New[string, any]()
|
||||||
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
||||||
|
@ -1202,7 +1207,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nameservers, err := parseNameServer(servers, preferH3)
|
nameservers, err := parseNameServer(servers, respectRules, preferH3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1296,6 +1301,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")
|
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{
|
dnsCfg := &DNS{
|
||||||
Enable: cfg.Enable,
|
Enable: cfg.Enable,
|
||||||
Listen: cfg.Listen,
|
Listen: cfg.Listen,
|
||||||
|
@ -1310,26 +1319,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var err error
|
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
|
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
|
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
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.DefaultNameserver) == 0 {
|
if len(cfg.DefaultNameserver) == 0 {
|
||||||
return nil, errors.New("default nameserver should have at least one nameserver")
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
// check default nameserver is pure ip addr
|
// check default nameserver is pure ip addr
|
||||||
|
|
122
dns/util.go
122
dns/util.go
|
@ -7,7 +7,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -19,6 +18,7 @@ import (
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
"github.com/metacubex/mihomo/tunnel"
|
"github.com/metacubex/mihomo/tunnel"
|
||||||
|
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||||
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
@ -175,6 +175,8 @@ func msgToDomain(msg *D.Msg) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RespectRules = "RULES"
|
||||||
|
|
||||||
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
|
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 {
|
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
|
||||||
|
@ -183,54 +185,76 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
|
||||||
opts = append(opts, dialer.WithResolver(r))
|
opts = append(opts, dialer.WithResolver(r))
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
} else {
|
} else {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
metadata := &C.Metadata{
|
||||||
|
NetWork: C.TCP,
|
||||||
|
Type: C.INNER,
|
||||||
|
}
|
||||||
|
err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
uintPort, err := strconv.ParseUint(port, 10, 16)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
metadata.DstIP = dstIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rule C.Rule
|
||||||
if proxyAdapter == nil {
|
if proxyAdapter == nil {
|
||||||
|
if proxyName == RespectRules {
|
||||||
|
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 = tunnel.ResolveMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
var ok bool
|
var ok bool
|
||||||
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||||
if !ok {
|
if !ok {
|
||||||
opts = append(opts, dialer.WithInterface(proxyName))
|
opts = append(opts, dialer.WithInterface(proxyName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(network, "tcp") {
|
if strings.Contains(network, "tcp") {
|
||||||
// tcp can resolve host by remote
|
if proxyAdapter == nil {
|
||||||
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))
|
opts = append(opts, dialer.WithResolver(r))
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
} else {
|
}
|
||||||
// udp must resolve host first
|
|
||||||
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
metadata := &C.Metadata{
|
metadata.DstIP = dstIP
|
||||||
NetWork: C.UDP,
|
|
||||||
Host: "",
|
|
||||||
DstIP: dstIP,
|
|
||||||
DstPort: uint16(uintPort),
|
|
||||||
}
|
}
|
||||||
|
metadata.Host = "" // clear host to avoid double resolve in proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := proxyAdapter.DialContext(ctx, metadata, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
} else {
|
||||||
if proxyAdapter == nil {
|
if proxyAdapter == nil {
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
}
|
}
|
||||||
|
@ -244,6 +268,8 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||||
|
|
||||||
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
|
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,33 +277,38 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
|
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)
|
metadata := &C.Metadata{
|
||||||
|
NetWork: C.UDP,
|
||||||
|
}
|
||||||
|
err := metadata.SetRemoteAddress(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
uintPort, err := strconv.ParseUint(port, 10, 16)
|
if !metadata.Resolved() {
|
||||||
|
// udp must resolve host first
|
||||||
|
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
metadata.DstIP = dstIP
|
||||||
|
}
|
||||||
|
|
||||||
|
var rule C.Rule
|
||||||
if proxyAdapter == nil {
|
if proxyAdapter == nil {
|
||||||
|
if proxyName == RespectRules {
|
||||||
|
proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
var ok bool
|
var ok bool
|
||||||
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||||
if !ok {
|
if !ok {
|
||||||
opts = append(opts, dialer.WithInterface(proxyName))
|
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 {
|
if proxyAdapter == nil {
|
||||||
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||||
}
|
}
|
||||||
|
@ -286,7 +317,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
|
||||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
|
||||||
|
|
||||||
|
return packetConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
|
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
|
||||||
|
|
|
@ -209,7 +209,7 @@ tunnels: # one line config
|
||||||
dns:
|
dns:
|
||||||
cache-algorithm: arc
|
cache-algorithm: arc
|
||||||
enable: false # 关闭将使用系统 DNS
|
enable: false # 关闭将使用系统 DNS
|
||||||
prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试
|
prefer-h3: false # 是否开启 DoH 支持 HTTP/3,将并发尝试
|
||||||
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
|
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
|
||||||
# ipv6: false # false 将返回 AAAA 的空结果
|
# ipv6: false # false 将返回 AAAA 的空结果
|
||||||
# ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms
|
# ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms
|
||||||
|
@ -227,6 +227,13 @@ dns:
|
||||||
|
|
||||||
# use-hosts: true # 查询 hosts
|
# 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 的域名
|
||||||
# fake-ip-filter:
|
# fake-ip-filter:
|
||||||
# - '*.lan'
|
# - '*.lan'
|
||||||
|
@ -244,6 +251,7 @@ dns:
|
||||||
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
|
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
|
||||||
- dhcp://en0 # dns from dhcp
|
- dhcp://en0 # dns from dhcp
|
||||||
- quic://dns.adguard.com:784 # DNS over QUIC
|
- quic://dns.adguard.com:784 # DNS over QUIC
|
||||||
|
# - '8.8.8.8#RULES' # 效果同respect-rules,但仅对该服务器生效
|
||||||
# - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡
|
# - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡
|
||||||
|
|
||||||
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置
|
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置
|
||||||
|
|
|
@ -278,7 +278,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {
|
func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {
|
||||||
if metadata.SpecialProxy != "" {
|
if metadata.SpecialProxy != "" {
|
||||||
var exist bool
|
var exist bool
|
||||||
proxy, exist = proxies[metadata.SpecialProxy]
|
proxy, exist = proxies[metadata.SpecialProxy]
|
||||||
|
@ -375,7 +375,7 @@ func handleUDPConn(packet C.PacketAdapter) {
|
||||||
cond.Broadcast()
|
cond.Broadcast()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
proxy, rule, err := resolveMetadata(metadata)
|
proxy, rule, err := ResolveMetadata(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("[UDP] Parse metadata failed: %s", err.Error())
|
log.Warnln("[UDP] Parse metadata failed: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -486,7 +486,7 @@ func handleTCPConn(connCtx C.ConnContext) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy, rule, err := resolveMetadata(metadata)
|
proxy, rule, err := ResolveMetadata(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("[Metadata] parse failed: %s", err.Error())
|
log.Warnln("[Metadata] parse failed: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue
Block a user