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}) }