89 lines
2.7 KiB
Go
89 lines
2.7 KiB
Go
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})
|
|
}
|