chore: rebuild udp dns resolve
Some checks failed
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Failing after 1s
Test / test (1.21, ubuntu-latest) (push) Failing after 1s
Test / test (1.22, ubuntu-latest) (push) Failing after 1s
Test / test (1.23, ubuntu-latest) (push) Failing after 1s
Test / test (1.24, ubuntu-latest) (push) Failing after 1s
Trigger CMFA Update / trigger-CMFA-update (push) Failing after 1s

The DNS resolution of the overall UDP part has been delayed to the connection initiation stage. During the rule matching process, it will only be triggered when the IP rule without no-resolve is matched.

For direct and wireguard outbound, the same logic as the TCP part will be followed, that is, when direct-nameserver (or DNS configured by wireguard) exists, the result of the matching process will be discarded and the domain name will be re-resolved. This re-resolution logic is only effective for fakeip.

For reject and DNS outbound, no resolution is required.

For other outbound, resolution will still be performed when the connection is initiated, and the domain name will not be sent directly to the remote server at present.
This commit is contained in:
wwqgtxx 2025-05-27 10:45:26 +08:00
parent 12e3952b74
commit a1c7881229
24 changed files with 246 additions and 190 deletions

View File

@ -2,7 +2,6 @@ package outbound
import ( import (
"context" "context"
"errors"
"net" "net"
"strconv" "strconv"
"time" "time"
@ -10,7 +9,6 @@ import (
CN "github.com/metacubex/mihomo/common/net" CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/anytls" "github.com/metacubex/mihomo/transport/anytls"
"github.com/metacubex/mihomo/transport/vmess" "github.com/metacubex/mihomo/transport/vmess"
@ -53,6 +51,10 @@ func (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Con
} }
func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
if err = t.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
// create tcp // create tcp
c, err := t.client.CreateProxy(ctx, uot.RequestDestination(2)) c, err := t.client.CreateProxy(ctx, uot.RequestDestination(2))
if err != nil { if err != nil {
@ -60,13 +62,6 @@ func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
} }
// create uot on tcp // create uot on tcp
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
}
destination := M.SocksaddrFromNet(metadata.UDPAddr()) destination := M.SocksaddrFromNet(metadata.UDPAddr())
return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil
} }

View File

@ -3,6 +3,7 @@ package outbound
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net" "net"
"runtime" "runtime"
"sync" "sync"
@ -11,6 +12,7 @@ import (
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
@ -18,6 +20,7 @@ import (
type ProxyAdapter interface { type ProxyAdapter interface {
C.ProxyAdapter C.ProxyAdapter
DialOptions() []dialer.Option DialOptions() []dialer.Option
ResolveUDP(ctx context.Context, metadata *C.Metadata) error
} }
type Base struct { type Base struct {
@ -159,6 +162,17 @@ func (b *Base) DialOptions() (opts []dialer.Option) {
return opts return opts
} }
func (b *Base) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return fmt.Errorf("can't resolve ip: %w", err)
}
metadata.DstIP = ip
}
return nil
}
func (b *Base) Close() error { func (b *Base) Close() error {
return nil return nil
} }
@ -258,6 +272,11 @@ type packetConn struct {
adapterName string adapterName string
connID string connID string
adapterAddr string adapterAddr string
resolveUDP func(ctx context.Context, metadata *C.Metadata) error
}
func (c *packetConn) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
return c.resolveUDP(ctx, metadata)
} }
func (c *packetConn) RemoteDestination() string { func (c *packetConn) RemoteDestination() string {
@ -296,12 +315,12 @@ func (c *packetConn) AddRef(ref any) {
c.EnhancePacketConn = N.NewRefPacketConn(c.EnhancePacketConn, ref) // add ref for autoCloseProxyAdapter c.EnhancePacketConn = N.NewRefPacketConn(c.EnhancePacketConn, ref) // add ref for autoCloseProxyAdapter
} }
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { func newPacketConn(pc net.PacketConn, a ProxyAdapter) C.PacketConn {
epc := N.NewEnhancePacketConn(pc) epc := N.NewEnhancePacketConn(pc)
if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly
} }
return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), a.Addr()} return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP}
} }
type AddRef interface { type AddRef interface {

View File

@ -2,7 +2,8 @@ package outbound
import ( import (
"context" "context"
"errors" "fmt"
"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"
@ -38,13 +39,8 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
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 if err := d.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
pc, err := dialer.NewDialer(d.DialOptions()...).ListenPacket(ctx, "udp", "", metadata.AddrPort()) pc, err := dialer.NewDialer(d.DialOptions()...).ListenPacket(ctx, "udp", "", metadata.AddrPort())
if err != nil { if err != nil {
@ -53,6 +49,17 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
} }
func (d *Direct) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if (!metadata.Resolved() || resolver.DirectHostResolver != resolver.DefaultResolver) && metadata.Host != "" {
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)
if err != nil {
return fmt.Errorf("can't resolve ip: %w", err)
}
metadata.DstIP = ip
}
return nil
}
func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool { func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool {
return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver
} }

View File

@ -3,6 +3,7 @@ package outbound
import ( import (
"context" "context"
"net" "net"
"net/netip"
"time" "time"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
@ -31,6 +32,9 @@ func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, er
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) { func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort()) log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort())
if err := d.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -41,6 +45,13 @@ func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.
}, d), nil }, d), nil
} }
func (d *Dns) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if !metadata.Resolved() {
metadata.DstIP = netip.AddrFrom4([4]byte{127, 0, 0, 2})
}
return nil
}
type dnsPacket struct { type dnsPacket struct {
data []byte data []byte
put func() put func()

View File

@ -60,6 +60,9 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata) (C.Con
} }
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) { func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
if err := h.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
udpConn, err := h.client.DialUDP(h.genHdc(ctx)) udpConn, err := h.client.DialUDP(h.genHdc(ctx))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -77,6 +77,9 @@ func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.
} }
func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
if err = h.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
pc, err := h.client.ListenPacket(ctx) pc, err := h.client.ListenPacket(ctx)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -54,6 +54,9 @@ func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn,
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
if err = m.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
if err := m.ensureClientIsRunning(); err != nil { if err := m.ensureClientIsRunning(); err != nil {
return nil, err return nil, err
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"io" "io"
"net" "net"
"net/netip"
"time" "time"
"github.com/metacubex/mihomo/common/buf" "github.com/metacubex/mihomo/common/buf"
@ -29,9 +30,19 @@ func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn,
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) { func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
if err := r.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
return newPacketConn(&nopPacketConn{}, r), nil return newPacketConn(&nopPacketConn{}, r), nil
} }
func (r *Reject) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if !metadata.Resolved() {
metadata.DstIP = netip.IPv4Unspecified()
}
return nil
}
func NewRejectWithOption(option RejectOption) *Reject { func NewRejectWithOption(option RejectOption) *Reject {
return &Reject{ return &Reject{
Base: &Base{ Base: &Base{

View File

@ -2,7 +2,6 @@ package outbound
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@ -11,7 +10,6 @@ import (
"github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/common/structure"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
gost "github.com/metacubex/mihomo/transport/gost-plugin" gost "github.com/metacubex/mihomo/transport/gost-plugin"
"github.com/metacubex/mihomo/transport/restls" "github.com/metacubex/mihomo/transport/restls"
@ -202,6 +200,9 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
return nil, err return nil, err
} }
} }
if err = ss.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
addr, err := resolveUDPAddr(ctx, "udp", ss.addr, ss.prefer) addr, err := resolveUDPAddr(ctx, "udp", ss.addr, ss.prefer)
if err != nil { if err != nil {
return nil, err return nil, err
@ -230,15 +231,9 @@ func (ss *ShadowSocks) ProxyInfo() C.ProxyInfo {
// ListenPacketOnStreamConn implements C.ProxyAdapter // ListenPacketOnStreamConn implements C.ProxyAdapter
func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
if ss.option.UDPOverTCP { if ss.option.UDPOverTCP {
// ss uot use stream-oriented udp with a special address, so we need a net.UDPAddr if err = ss.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
destination := M.SocksaddrFromNet(metadata.UDPAddr()) destination := M.SocksaddrFromNet(metadata.UDPAddr())
if ss.option.UDPOverTCPVersion == uot.LegacyVersion { if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
return newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil return newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil

View File

@ -105,6 +105,9 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di
return nil, err return nil, err
} }
} }
if err = ssr.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
addr, err := resolveUDPAddr(ctx, "udp", ssr.addr, ssr.prefer) addr, err := resolveUDPAddr(ctx, "udp", ssr.addr, ssr.prefer)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -2,12 +2,10 @@ package outbound
import ( import (
"context" "context"
"errors"
CN "github.com/metacubex/mihomo/common/net" CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
@ -53,16 +51,9 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
if s.onlyTcp { if s.onlyTcp {
return s.ProxyAdapter.ListenPacketContext(ctx, metadata) return s.ProxyAdapter.ListenPacketContext(ctx, metadata)
} }
if err = s.ProxyAdapter.ResolveUDP(ctx, metadata); err != nil {
// sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr return nil, err
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr())) pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -127,6 +127,9 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
return nil, err return nil, err
} }
} }
if err = s.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
c, err := dialer.DialContext(ctx, "tcp", s.addr) c, err := dialer.DialContext(ctx, "tcp", s.addr)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -109,6 +109,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
return nil, err return nil, err
} }
} }
if err = ss.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
c, err := cDialer.DialContext(ctx, "tcp", ss.addr) c, err := cDialer.DialContext(ctx, "tcp", ss.addr)
if err != nil { if err != nil {
err = fmt.Errorf("%s connect error: %w", ss.addr, err) err = fmt.Errorf("%s connect error: %w", ss.addr, err)

View File

@ -219,6 +219,10 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
if err = t.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
var c net.Conn var c net.Conn
// grpc transport // grpc transport
@ -250,6 +254,9 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
return nil, err return nil, err
} }
} }
if err = t.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
c, err := dialer.DialContext(ctx, "tcp", t.addr) c, err := dialer.DialContext(ctx, "tcp", t.addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err) return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
@ -271,12 +278,6 @@ func (t *Trojan) SupportWithDialer() C.NetWork {
return C.ALLNet return C.ALLNet
} }
// ListenPacketOnStreamConn implements C.ProxyAdapter
func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
pc := trojan.NewPacketConn(c)
return newPacketConn(pc, t), err
}
// SupportUOT implements C.ProxyAdapter // SupportUOT implements C.ProxyAdapter
func (t *Trojan) SupportUOT() bool { func (t *Trojan) SupportUOT() bool {
return true return true

View File

@ -3,7 +3,6 @@ package outbound
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"math" "math"
"net" "net"
@ -14,7 +13,6 @@ import (
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls" tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/tuic" "github.com/metacubex/mihomo/transport/tuic"
@ -91,6 +89,10 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_
// ListenPacketWithDialer implements C.ProxyAdapter // ListenPacketWithDialer implements C.ProxyAdapter
func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
if err = t.ResolveUDP(ctx, metadata); err != nil {
return nil, err
}
if t.option.UDPOverStream { if t.option.UDPOverStream {
uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion)) uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
uotMetadata := *metadata uotMetadata := *metadata
@ -102,13 +104,6 @@ func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, meta
} }
// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr // tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
}
destination := M.SocksaddrFromNet(metadata.UDPAddr()) destination := M.SocksaddrFromNet(metadata.UDPAddr())
if t.option.UDPOverStreamVersion == uot.LegacyVersion { if t.option.UDPOverStreamVersion == uot.LegacyVersion {

View File

@ -19,7 +19,6 @@ import (
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls" tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/gun" "github.com/metacubex/mihomo/transport/gun"
@ -277,13 +276,8 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
var c net.Conn var c net.Conn
// gun transport // gun transport
@ -315,13 +309,8 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
} }
} }
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
c, err := dialer.DialContext(ctx, "tcp", v.addr) c, err := dialer.DialContext(ctx, "tcp", v.addr)
@ -347,13 +336,8 @@ func (v *Vless) SupportWithDialer() C.NetWork {
// ListenPacketOnStreamConn implements C.ProxyAdapter // ListenPacketOnStreamConn implements C.ProxyAdapter
func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
if v.option.XUDP { if v.option.XUDP {

View File

@ -17,7 +17,6 @@ import (
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls" tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/ntp" "github.com/metacubex/mihomo/ntp"
@ -330,13 +329,8 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) { func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
var c net.Conn var c net.Conn
// gun transport // gun transport
@ -367,13 +361,8 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
} }
} }
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
c, err := dialer.DialContext(ctx, "tcp", v.addr) c, err := dialer.DialContext(ctx, "tcp", v.addr)
@ -413,13 +402,8 @@ func (v *Vmess) Close() error {
// ListenPacketOnStreamConn implements C.ProxyAdapter // ListenPacketOnStreamConn implements C.ProxyAdapter
func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr if err = v.ResolveUDP(ctx, metadata); err != nil {
if !metadata.Resolved() { return nil, err
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
if pc, ok := c.(net.PacketConn); ok { if pc, ok := c.(net.PacketConn); ok {

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"net" "net"
"net/netip" "net/netip"
@ -520,16 +519,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
if err = w.init(ctx); err != nil { if err = w.init(ctx); err != nil {
return nil, err return nil, err
} }
if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" { if err = w.ResolveUDP(ctx, metadata); err != nil {
r := resolver.DefaultResolver return nil, err
if w.resolver != nil {
r = w.resolver
}
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
} }
pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap()) pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
if err != nil { if err != nil {
@ -541,6 +532,21 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
return newPacketConn(pc, w), nil return newPacketConn(pc, w), nil
} }
func (w *WireGuard) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" {
r := resolver.DefaultResolver
if w.resolver != nil {
r = w.resolver
}
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil {
return fmt.Errorf("can't resolve ip: %w", err)
}
metadata.DstIP = ip
}
return nil
}
// IsL3Protocol implements C.ProxyAdapter // IsL3Protocol implements C.ProxyAdapter
func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool { func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool {
return true return true

View File

@ -76,9 +76,9 @@ func (sniffer *QuicSniffer) SniffData(b []byte) (string, error) {
func (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, override bool) constant.PacketSender { func (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, override bool) constant.PacketSender {
return &quicPacketSender{ return &quicPacketSender{
sender: packetSender, PacketSender: packetSender,
chClose: make(chan struct{}), chClose: make(chan struct{}),
override: override, override: override,
} }
} }
@ -91,7 +91,7 @@ type quicPacketSender struct {
result string result string
override bool override bool
sender constant.PacketSender constant.PacketSender
chClose chan struct{} chClose chan struct{}
closed bool closed bool
@ -100,7 +100,7 @@ type quicPacketSender struct {
// Send will send PacketAdapter nonblocking // Send will send PacketAdapter nonblocking
// the implement must call UDPPacket.Drop() inside Send // the implement must call UDPPacket.Drop() inside Send
func (q *quicPacketSender) Send(current constant.PacketAdapter) { func (q *quicPacketSender) Send(current constant.PacketAdapter) {
defer q.sender.Send(current) defer q.PacketSender.Send(current)
q.lock.RLock() q.lock.RLock()
if q.closed { if q.closed {
@ -116,29 +116,24 @@ func (q *quicPacketSender) Send(current constant.PacketAdapter) {
} }
} }
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy // DoSniff wait sniffer recv all fragments and update the domain
func (q *quicPacketSender) Process(conn constant.PacketConn, proxy constant.WriteBackProxy) { func (q *quicPacketSender) DoSniff(metadata *constant.Metadata) error {
q.sender.Process(conn, proxy)
}
// ResolveUDP wait sniffer recv all fragments and update the domain
func (q *quicPacketSender) ResolveUDP(data *constant.Metadata) error {
select { select {
case <-q.chClose: case <-q.chClose:
q.lock.RLock() q.lock.RLock()
replaceDomain(data, q.result, q.override) replaceDomain(metadata, q.result, q.override)
q.lock.RUnlock() q.lock.RUnlock()
break break
case <-time.After(quicWaitConn): case <-time.After(quicWaitConn):
q.close() q.close()
} }
return q.sender.ResolveUDP(data) return q.PacketSender.DoSniff(metadata)
} }
// Close stop the Process loop // Close stop the Process loop
func (q *quicPacketSender) Close() { func (q *quicPacketSender) Close() {
q.sender.Close() q.PacketSender.Close()
q.close() q.close()
} }

View File

@ -12,7 +12,7 @@ import (
) )
type fakeSender struct { type fakeSender struct {
resultCh chan *constant.Metadata constant.PacketSender
} }
var _ constant.PacketSender = (*fakeSender)(nil) var _ constant.PacketSender = (*fakeSender)(nil)
@ -22,18 +22,7 @@ func (e *fakeSender) Send(packet constant.PacketAdapter) {
packet.Drop() packet.Drop()
} }
func (e *fakeSender) Process(constant.PacketConn, constant.WriteBackProxy) { func (e *fakeSender) DoSniff(metadata *constant.Metadata) error { return nil }
panic("not implemented")
}
func (e *fakeSender) ResolveUDP(metadata *constant.Metadata) error {
e.resultCh <- metadata
return nil
}
func (e *fakeSender) Close() {
panic("not implemented")
}
type fakeUDPPacket struct { type fakeUDPPacket struct {
data []byte data []byte
@ -85,16 +74,17 @@ func testQuicSniffer(data []string, async bool) (string, error) {
} }
resultCh := make(chan *constant.Metadata, 1) resultCh := make(chan *constant.Metadata, 1)
emptySender := &fakeSender{resultCh: resultCh} emptySender := &fakeSender{}
sender := q.WrapperSender(emptySender, true) sender := q.WrapperSender(emptySender, true)
go func() { go func() {
meta := constant.Metadata{} meta := constant.Metadata{}
err = sender.ResolveUDP(&meta) err := sender.DoSniff(&meta)
if err != nil { if err != nil {
panic(err) panic(err)
} }
resultCh <- &meta
}() }()
for _, d := range data { for _, d := range data {

View File

@ -92,8 +92,7 @@ type Conn interface {
type PacketConn interface { type PacketConn interface {
N.EnhancePacketConn N.EnhancePacketConn
Connection Connection
// Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed ResolveUDP(ctx context.Context, metadata *Metadata) error
// WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error)
} }
type Dialer interface { type Dialer interface {
@ -319,10 +318,15 @@ type PacketSender interface {
Send(PacketAdapter) Send(PacketAdapter)
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy // Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy
Process(PacketConn, WriteBackProxy) Process(PacketConn, WriteBackProxy)
// ResolveUDP do a local resolve UDP dns blocking if metadata is not resolved
ResolveUDP(*Metadata) error
// Close stop the Process loop // Close stop the Process loop
Close() Close()
// DoSniff will blocking after sniffer work done
DoSniff(*Metadata) error
// AddMapping add a destination NAT record
AddMapping(originMetadata *Metadata, metadata *Metadata)
// RestoreReadFrom restore destination NAT for ReadFrom
// the implement must ensure returned netip.Add is valid (or just return input addr)
RestoreReadFrom(addr netip.Addr) netip.Addr
} }
type NatTable interface { type NatTable interface {

View File

@ -261,6 +261,11 @@ func (m *Metadata) Pure() *Metadata {
return m return m
} }
func (m *Metadata) Clone() *Metadata {
copyM := *m
return &copyM
}
func (m *Metadata) AddrPort() netip.AddrPort { func (m *Metadata) AddrPort() netip.AddrPort {
return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort) return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)
} }

View File

@ -5,11 +5,10 @@ import (
"errors" "errors"
"net" "net"
"net/netip" "net/netip"
"sync"
"time" "time"
"github.com/metacubex/mihomo/common/lru"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
@ -18,7 +17,11 @@ type packetSender struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
ch chan C.PacketAdapter ch chan C.PacketAdapter
cache *lru.LruCache[string, netip.Addr]
// destination NAT mapping
originToTarget map[string]netip.Addr
targetToOrigin map[netip.Addr]netip.Addr
mappingMutex sync.RWMutex
} }
// newPacketSender return a chan based C.PacketSender // newPacketSender return a chan based C.PacketSender
@ -30,10 +33,74 @@ func newPacketSender() C.PacketSender {
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
ch: ch, ch: ch,
cache: lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](senderCapacity)),
originToTarget: make(map[string]netip.Addr),
targetToOrigin: make(map[netip.Addr]netip.Addr),
} }
} }
func (s *packetSender) AddMapping(originMetadata *C.Metadata, metadata *C.Metadata) {
s.mappingMutex.Lock()
defer s.mappingMutex.Unlock()
originKey := originMetadata.String()
originAddr := originMetadata.DstIP
targetAddr := metadata.DstIP
if addr := s.originToTarget[originKey]; !addr.IsValid() { // overwrite only if the record is illegal
s.originToTarget[originKey] = targetAddr
}
if addr := s.targetToOrigin[targetAddr]; !addr.IsValid() { // overwrite only if the record is illegal
s.targetToOrigin[targetAddr] = originAddr
}
}
func (s *packetSender) RestoreReadFrom(addr netip.Addr) netip.Addr {
s.mappingMutex.RLock()
defer s.mappingMutex.RUnlock()
if originAddr := s.targetToOrigin[addr]; originAddr.IsValid() {
return originAddr
}
return addr
}
func (s *packetSender) processPacket(pc C.PacketConn, packet C.PacketAdapter) {
defer packet.Drop()
metadata := packet.Metadata()
var addr *net.UDPAddr
s.mappingMutex.RLock()
targetAddr := s.originToTarget[metadata.String()]
s.mappingMutex.RUnlock()
if targetAddr.IsValid() {
addr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(targetAddr, metadata.DstPort))
}
if addr == nil {
originMetadata := metadata // save origin metadata
metadata = metadata.Clone() // don't modify PacketAdapter's metadata
_ = preHandleMetadata(metadata) // error was pre-checked
metadata = metadata.Pure()
if metadata.Host != "" {
// TODO: ResolveUDP may take a long time to block the Process loop
// but we want keep sequence sending so can't open a new goroutine
if err := pc.ResolveUDP(s.ctx, metadata); err != nil {
log.Warnln("[UDP] Resolve Ip error: %s", err)
return
}
}
if !metadata.DstIP.IsValid() {
log.Warnln("[UDP] Destination ip not valid: %#v", metadata)
return
}
s.AddMapping(originMetadata, metadata)
addr = metadata.UDPAddr()
}
_ = handleUDPToRemote(packet, pc, addr)
}
func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) { func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) {
for { for {
select { select {
@ -43,12 +110,7 @@ func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) {
if proxy != nil { if proxy != nil {
proxy.UpdateWriteBack(packet) proxy.UpdateWriteBack(packet)
} }
if err := s.ResolveUDP(packet.Metadata()); err != nil { s.processPacket(pc, packet)
log.Warnln("[UDP] Resolve Ip error: %s", err)
} else {
_ = handleUDPToRemote(packet, pc, packet.Metadata())
}
packet.Drop()
} }
} }
} }
@ -87,25 +149,9 @@ func (s *packetSender) Close() {
s.dropAll() s.dropAll()
} }
func (s *packetSender) ResolveUDP(metadata *C.Metadata) (err error) { func (s *packetSender) DoSniff(metadata *C.Metadata) error { return nil }
// local resolve UDP dns
if !metadata.Resolved() {
ip, ok := s.cache.Get(metadata.Host)
if !ok {
ip, err = resolver.ResolveIP(s.ctx, metadata.Host)
if err != nil {
return err
}
s.cache.Set(metadata.Host, ip)
}
metadata.DstIP = ip func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, addr *net.UDPAddr) error {
}
return nil
}
func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error {
addr := metadata.UDPAddr()
if addr == nil { if addr == nil {
return errors.New("udp addr invalid") return errors.New("udp addr invalid")
} }
@ -119,7 +165,7 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
return nil return nil
} }
func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, sender C.PacketSender, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) { func handleUDPToLocal(writeBack C.WriteBack, pc C.PacketConn, sender C.PacketSender, key string, oAddrPort netip.AddrPort) {
defer func() { defer func() {
sender.Close() sender.Close()
_ = pc.Close() _ = pc.Close()
@ -146,10 +192,8 @@ func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, sender C.Pa
fromAddrPort := fromUDPAddr.AddrPort() fromAddrPort := fromUDPAddr.AddrPort()
fromAddr := fromAddrPort.Addr().Unmap() fromAddr := fromAddrPort.Addr().Unmap()
// restore fakeip // restore DestinationNAT
if fAddr.IsValid() && (oAddrPort.Addr() == fromAddr) { // oAddrPort was Unmapped fromAddr = sender.RestoreReadFrom(fromAddr).Unmap()
fromAddr = fAddr.Unmap()
}
fromAddrPort = netip.AddrPortFrom(fromAddr, fromAddrPort.Port()) fromAddrPort = netip.AddrPortFrom(fromAddr, fromAddrPort.Port())

View File

@ -366,15 +366,9 @@ func handleUDPConn(packet C.PacketAdapter) {
return return
} }
// make a fAddr if request ip is fakeip if err := preHandleMetadata(metadata.Clone()); err != nil { // precheck without modify metadata
var fAddr netip.Addr
if resolver.IsExistFakeIP(metadata.DstIP) {
fAddr = metadata.DstIP
}
if err := preHandleMetadata(metadata); err != nil {
packet.Drop() packet.Drop()
log.Debugln("[Metadata PreHandle] error: %s", err) log.Warnln("[Metadata PreHandle] error: %s", err)
return return
} }
@ -388,21 +382,27 @@ func handleUDPConn(packet C.PacketAdapter) {
}) })
if !loaded { if !loaded {
dial := func() (C.PacketConn, C.WriteBackProxy, error) { dial := func() (C.PacketConn, C.WriteBackProxy, error) {
if err := sender.ResolveUDP(metadata); err != nil { originMetadata := metadata // save origin metadata
log.Warnln("[UDP] Resolve Ip error: %s", err) metadata = metadata.Clone() // don't modify PacketAdapter's metadata
if err := sender.DoSniff(metadata); err != nil {
log.Warnln("[UDP] DoSniff error: %s", err.Error())
return nil, nil, err return nil, nil, err
} }
_ = preHandleMetadata(metadata) // error was pre-checked
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 nil, nil, err return nil, nil, err
} }
dialMetadata := metadata.Pure()
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout)
defer cancel() defer cancel()
rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) {
return proxy.ListenPacketContext(ctx, metadata.Pure()) return proxy.ListenPacketContext(ctx, dialMetadata)
}, func(err error) { }, func(err error) {
logMetadataErr(metadata, rule, proxy, err) logMetadataErr(metadata, rule, proxy, err)
}) })
@ -413,10 +413,11 @@ func handleUDPConn(packet C.PacketAdapter) {
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true)
oAddrPort := metadata.AddrPort() sender.AddMapping(originMetadata, dialMetadata)
oAddrPort := dialMetadata.AddrPort()
writeBackProxy := nat.NewWriteBackProxy(packet) writeBackProxy := nat.NewWriteBackProxy(packet)
go handleUDPToLocal(writeBackProxy, pc, sender, key, oAddrPort, fAddr) go handleUDPToLocal(writeBackProxy, pc, sender, key, oAddrPort)
return pc, writeBackProxy, nil return pc, writeBackProxy, nil
} }