mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 11:53:15 +08:00
chore: dns outbound support tcp
This commit is contained in:
parent
04886761a2
commit
fad1a08378
@ -2,10 +2,10 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/common/pool"
|
"github.com/metacubex/mihomo/common/pool"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
@ -24,7 +24,9 @@ type DnsOption struct {
|
|||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
return nil, fmt.Errorf("dns outbound does not support tcp")
|
left, right := N.Pipe()
|
||||||
|
go resolver.RelayDnsConn(context.Background(), right, 0)
|
||||||
|
return NewConn(left, d), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
@ -76,15 +78,29 @@ func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)
|
ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
buf := pool.Get(resolver.SafeDnsPacketSize)
|
buf := pool.Get(resolver.SafeDnsPacketSize)
|
||||||
put := func() { _ = pool.Put(buf) }
|
put := func() { _ = pool.Put(buf) }
|
||||||
buf, err = resolver.RelayDnsPacket(ctx, p, buf)
|
copy(buf, p) // avoid p be changed after WriteTo returned
|
||||||
|
|
||||||
|
go func() { // don't block the WriteTo function
|
||||||
|
buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
put()
|
put()
|
||||||
return 0, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
packet := dnsPacket{
|
packet := dnsPacket{
|
||||||
@ -94,11 +110,12 @@ func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case d.response <- packet:
|
case d.response <- packet:
|
||||||
return len(p), nil
|
break
|
||||||
case <-d.ctx.Done():
|
case <-d.ctx.Done():
|
||||||
put()
|
put()
|
||||||
return 0, net.ErrClosed
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsPacketConn) Close() error {
|
func (d *dnsPacketConn) Close() error {
|
||||||
|
@ -26,6 +26,11 @@ type Conn struct {
|
|||||||
resultCh chan *connReadResult
|
resultCh chan *connReadResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsConn(conn any) bool {
|
||||||
|
_, ok := conn.(*Conn)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func NewConn(conn net.Conn) *Conn {
|
func NewConn(conn net.Conn) *Conn {
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
ExtendedConn: bufio.NewExtendedConn(conn),
|
ExtendedConn: bufio.NewExtendedConn(conn),
|
||||||
|
@ -215,3 +215,8 @@ func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {
|
|||||||
return nil, os.ErrDeadlineExceeded
|
return nil, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsPipe(conn any) bool {
|
||||||
|
_, ok := conn.(*pipe)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
@ -23,6 +23,12 @@ type ExtendedReader = network.ExtendedReader
|
|||||||
var WriteBuffer = bufio.WriteBuffer
|
var WriteBuffer = bufio.WriteBuffer
|
||||||
|
|
||||||
func NewDeadlineConn(conn net.Conn) ExtendedConn {
|
func NewDeadlineConn(conn net.Conn) ExtendedConn {
|
||||||
|
if deadline.IsPipe(conn) || deadline.IsPipe(network.UnwrapReader(conn)) {
|
||||||
|
return NewExtendedConn(conn) // pipe always have correctly deadline implement
|
||||||
|
}
|
||||||
|
if deadline.IsConn(conn) || deadline.IsConn(network.UnwrapReader(conn)) {
|
||||||
|
return NewExtendedConn(conn) // was a *deadline.Conn
|
||||||
|
}
|
||||||
return deadline.NewConn(conn)
|
return deadline.NewConn(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,15 +17,15 @@ const DefaultDnsRelayTimeout = time.Second * 5
|
|||||||
|
|
||||||
const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough
|
const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough
|
||||||
|
|
||||||
func RelayDnsConn(ctx context.Context, conn net.Conn) error {
|
func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) error {
|
||||||
buff := pool.Get(pool.UDPBufferSize)
|
buff := pool.Get(pool.UDPBufferSize)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = pool.Put(buff)
|
_ = pool.Put(buff)
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil {
|
if readTimeout > 0 {
|
||||||
break
|
_ = conn.SetReadDeadline(time.Now().Add(readTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
length := uint16(0)
|
length := uint16(0)
|
||||||
|
@ -37,7 +37,7 @@ func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool {
|
|||||||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||||
log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String())
|
log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String())
|
||||||
return resolver.RelayDnsConn(ctx, conn)
|
return resolver.RelayDnsConn(ctx, conn, resolver.DefaultDnsReadTimeout)
|
||||||
}
|
}
|
||||||
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user