mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-19 16:30:07 +08:00
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.
170 lines
3.5 KiB
Go
170 lines
3.5 KiB
Go
package outbound
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/netip"
|
|
"time"
|
|
|
|
N "github.com/metacubex/mihomo/common/net"
|
|
"github.com/metacubex/mihomo/common/pool"
|
|
"github.com/metacubex/mihomo/component/resolver"
|
|
C "github.com/metacubex/mihomo/constant"
|
|
"github.com/metacubex/mihomo/log"
|
|
)
|
|
|
|
type Dns struct {
|
|
*Base
|
|
}
|
|
|
|
type DnsOption struct {
|
|
BasicOption
|
|
Name string `proxy:"name"`
|
|
}
|
|
|
|
// DialContext implements C.ProxyAdapter
|
|
func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
|
|
left, right := N.Pipe()
|
|
go resolver.RelayDnsConn(context.Background(), right, 0)
|
|
return NewConn(left, d), nil
|
|
}
|
|
|
|
// ListenPacketContext implements C.ProxyAdapter
|
|
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())
|
|
if err := d.ResolveUDP(ctx, metadata); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
return newPacketConn(&dnsPacketConn{
|
|
response: make(chan dnsPacket, 1),
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
}, 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 {
|
|
data []byte
|
|
put func()
|
|
addr net.Addr
|
|
}
|
|
|
|
// dnsPacketConn implements net.PacketConn
|
|
type dnsPacketConn struct {
|
|
response chan dnsPacket
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func (d *dnsPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
|
|
select {
|
|
case packet := <-d.response:
|
|
return packet.data, packet.put, packet.addr, nil
|
|
case <-d.ctx.Done():
|
|
return nil, nil, nil, net.ErrClosed
|
|
}
|
|
}
|
|
|
|
func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
select {
|
|
case packet := <-d.response:
|
|
n = copy(p, packet.data)
|
|
if packet.put != nil {
|
|
packet.put()
|
|
}
|
|
return n, packet.addr, nil
|
|
case <-d.ctx.Done():
|
|
return 0, nil, net.ErrClosed
|
|
}
|
|
}
|
|
|
|
func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
select {
|
|
case <-d.ctx.Done():
|
|
return 0, net.ErrClosed
|
|
default:
|
|
}
|
|
|
|
if len(p) > resolver.SafeDnsPacketSize {
|
|
// wtf???
|
|
return len(p), nil
|
|
}
|
|
|
|
buf := pool.Get(resolver.SafeDnsPacketSize)
|
|
put := func() { _ = pool.Put(buf) }
|
|
copy(buf, p) // avoid p be changed after WriteTo returned
|
|
|
|
go func() { // don't block the WriteTo function
|
|
ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)
|
|
defer cancel()
|
|
|
|
buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf)
|
|
if err != nil {
|
|
put()
|
|
return
|
|
}
|
|
|
|
packet := dnsPacket{
|
|
data: buf,
|
|
put: put,
|
|
addr: addr,
|
|
}
|
|
select {
|
|
case d.response <- packet:
|
|
break
|
|
case <-d.ctx.Done():
|
|
put()
|
|
}
|
|
}()
|
|
return len(p), nil
|
|
}
|
|
|
|
func (d *dnsPacketConn) Close() error {
|
|
d.cancel()
|
|
return nil
|
|
}
|
|
|
|
func (*dnsPacketConn) LocalAddr() net.Addr {
|
|
return &net.UDPAddr{
|
|
IP: net.IPv4(127, 0, 0, 1),
|
|
Port: 53,
|
|
Zone: "",
|
|
}
|
|
}
|
|
|
|
func (*dnsPacketConn) SetDeadline(t time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func (*dnsPacketConn) SetReadDeadline(t time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func (*dnsPacketConn) SetWriteDeadline(t time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func NewDnsWithOption(option DnsOption) *Dns {
|
|
return &Dns{
|
|
Base: &Base{
|
|
name: option.Name,
|
|
tp: C.Dns,
|
|
udp: true,
|
|
tfo: option.TFO,
|
|
mpTcp: option.MPTCP,
|
|
iface: option.Interface,
|
|
rmark: option.RoutingMark,
|
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
|
},
|
|
}
|
|
}
|