Add stateless DNS hijack
This commit is contained in:
parent
1ba7d79118
commit
79084fa798
5 changed files with 113 additions and 6 deletions
1
flow.go
1
flow.go
|
|
@ -21,6 +21,7 @@ const (
|
||||||
ActionReject
|
ActionReject
|
||||||
ActionDrop
|
ActionDrop
|
||||||
ActionBypass
|
ActionBypass
|
||||||
|
ActionHijackDNS
|
||||||
)
|
)
|
||||||
|
|
||||||
type FlowTracker interface {
|
type FlowTracker interface {
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,13 @@ func (d *ForwardDispatcher) judgeAndInstall(key flowKey, packet *forwardPacket,
|
||||||
case ActionDrop:
|
case ActionDrop:
|
||||||
d.installSimple(key, ActionDrop, packet.protocol, now)
|
d.installSimple(key, ActionDrop, packet.protocol, now)
|
||||||
return true
|
return true
|
||||||
|
case ActionHijackDNS:
|
||||||
|
if packet.protocol == uint8(header.UDPProtocolNumber) {
|
||||||
|
d.hijackDNSPacket(packet)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
d.installSimple(key, ActionAccept, packet.protocol, now)
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
d.installSimple(key, ActionAccept, packet.protocol, now)
|
d.installSimple(key, ActionAccept, packet.protocol, now)
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
89
flow_dns.go
Normal file
89
flow_dns.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun/gtcpip/checksum"
|
||||||
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *ForwardDispatcher) hijackDNSPacket(packet *forwardPacket) {
|
||||||
|
writer := &dnsResponseWriter{
|
||||||
|
writeback: d.writeback,
|
||||||
|
source: packet.source,
|
||||||
|
}
|
||||||
|
d.handler.NewDNSPacket(header.UDP(packet.transport).Payload(), M.SocksaddrFromNetIP(packet.source), M.SocksaddrFromNetIP(packet.destination), writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.PacketWriter = (*dnsResponseWriter)(nil)
|
||||||
|
|
||||||
|
type dnsResponseWriter struct {
|
||||||
|
writeback ForwardWriteback
|
||||||
|
source netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
defer buffer.Release()
|
||||||
|
if !destination.IsIP() {
|
||||||
|
return E.New("invalid destination: ", destination)
|
||||||
|
}
|
||||||
|
sourceAddr := w.source.Addr().Unmap()
|
||||||
|
destinationAddr := destination.Addr.Unmap()
|
||||||
|
headroom := w.writeback.ReturnHeadroom()
|
||||||
|
udpLen := header.UDPMinimumSize + buffer.Len()
|
||||||
|
var (
|
||||||
|
packet []byte
|
||||||
|
udpHdr header.UDP
|
||||||
|
ipHdr header.Network
|
||||||
|
)
|
||||||
|
if sourceAddr.Is4() {
|
||||||
|
if !destinationAddr.Is4() {
|
||||||
|
return E.New("send IPv6 packet to IPv4 connection")
|
||||||
|
}
|
||||||
|
size := header.IPv4MinimumSize + udpLen
|
||||||
|
packet = make([]byte, headroom+size)
|
||||||
|
inet4Hdr := header.IPv4(packet[headroom:])
|
||||||
|
inet4Hdr.Encode(&header.IPv4Fields{
|
||||||
|
TotalLength: uint16(size),
|
||||||
|
TTL: synthesizedTTL,
|
||||||
|
Protocol: uint8(header.UDPProtocolNumber),
|
||||||
|
SrcAddr: destinationAddr,
|
||||||
|
DstAddr: sourceAddr,
|
||||||
|
})
|
||||||
|
udpHdr = header.UDP(inet4Hdr.Payload())
|
||||||
|
ipHdr = inet4Hdr
|
||||||
|
} else {
|
||||||
|
if destinationAddr.Is4() {
|
||||||
|
destinationAddr = netip.AddrFrom16(destinationAddr.As16())
|
||||||
|
}
|
||||||
|
size := header.IPv6MinimumSize + udpLen
|
||||||
|
packet = make([]byte, headroom+size)
|
||||||
|
inet6Hdr := header.IPv6(packet[headroom:])
|
||||||
|
inet6Hdr.Encode(&header.IPv6Fields{
|
||||||
|
PayloadLength: uint16(udpLen),
|
||||||
|
TransportProtocol: header.UDPProtocolNumber,
|
||||||
|
HopLimit: synthesizedTTL,
|
||||||
|
SrcAddr: destinationAddr,
|
||||||
|
DstAddr: sourceAddr,
|
||||||
|
})
|
||||||
|
udpHdr = header.UDP(inet6Hdr.Payload())
|
||||||
|
ipHdr = inet6Hdr
|
||||||
|
}
|
||||||
|
udpHdr.Encode(&header.UDPFields{
|
||||||
|
SrcPort: destination.Port,
|
||||||
|
DstPort: w.source.Port(),
|
||||||
|
Length: uint16(udpLen),
|
||||||
|
})
|
||||||
|
copy(udpHdr.Payload(), buffer.Bytes())
|
||||||
|
udpHdr.SetChecksum(^checksum.Checksum(udpHdr.Payload(), udpHdr.CalculateChecksum(
|
||||||
|
header.PseudoHeaderChecksum(header.UDPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), uint16(udpLen)),
|
||||||
|
)))
|
||||||
|
if inet4Hdr, isInet4 := ipHdr.(header.IPv4); isInet4 {
|
||||||
|
inet4Hdr.SetChecksum(^inet4Hdr.CalculateChecksum())
|
||||||
|
}
|
||||||
|
return w.writeback.WriteReturnPackets([][]byte{packet})
|
||||||
|
}
|
||||||
|
|
@ -71,18 +71,26 @@ func (f *UDPForwarder) PreparePacketConnection(source M.Socksaddr, destination M
|
||||||
firstPacket = append(firstPacket[:len(firstPacket):len(firstPacket)], view.AsSlice()...)
|
firstPacket = append(firstPacket[:len(firstPacket):len(firstPacket)], view.AsSlice()...)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
var sourceNetwork tcpip.NetworkProtocolNumber
|
||||||
|
if source.Addr.Is4() {
|
||||||
|
sourceNetwork = header.IPv4ProtocolNumber
|
||||||
|
} else {
|
||||||
|
sourceNetwork = header.IPv6ProtocolNumber
|
||||||
|
}
|
||||||
switch f.handler.JudgeFlow(uint8(header.UDPProtocolNumber), source.AddrPort(), destination.AddrPort(), firstPacket).Action {
|
switch f.handler.JudgeFlow(uint8(header.UDPProtocolNumber), source.AddrPort(), destination.AddrPort(), firstPacket).Action {
|
||||||
case ActionReject:
|
case ActionReject:
|
||||||
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer))
|
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer))
|
||||||
return false, nil, nil, nil
|
return false, nil, nil, nil
|
||||||
case ActionDrop:
|
case ActionDrop:
|
||||||
return false, nil, nil, nil
|
return false, nil, nil, nil
|
||||||
}
|
case ActionHijackDNS:
|
||||||
var sourceNetwork tcpip.NetworkProtocolNumber
|
f.handler.NewDNSPacket(firstPacket, source, destination, &UDPBackWriter{
|
||||||
if source.Addr.Is4() {
|
stack: f.stack,
|
||||||
sourceNetwork = header.IPv4ProtocolNumber
|
source: AddressFromAddr(source.Addr),
|
||||||
} else {
|
sourcePort: source.Port,
|
||||||
sourceNetwork = header.IPv6ProtocolNumber
|
sourceNetwork: sourceNetwork,
|
||||||
|
})
|
||||||
|
return false, nil, nil, nil
|
||||||
}
|
}
|
||||||
writer := &UDPBackWriter{
|
writer := &UDPBackWriter{
|
||||||
stack: f.stack,
|
stack: f.stack,
|
||||||
|
|
|
||||||
2
tun.go
2
tun.go
|
|
@ -14,12 +14,14 @@ import (
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/ranges"
|
"github.com/sagernet/sing/common/ranges"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) FlowVerdict
|
JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) FlowVerdict
|
||||||
|
NewDNSPacket(payload []byte, source M.Socksaddr, destination M.Socksaddr, writer N.PacketWriter)
|
||||||
N.TCPConnectionHandlerEx
|
N.TCPConnectionHandlerEx
|
||||||
N.UDPConnectionHandlerEx
|
N.UDPConnectionHandlerEx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue