package system

import (
	"encoding/binary"
	"io"
	"net"
	"time"

	D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
	"github.com/kr328/tun2socket/binding"
	"github.com/kr328/tun2socket/redirect"
)

const defaultDnsReadTimeout = time.Second * 30

func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool {
	if targetAddr.Port != 53 {
		return false
	}

	return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP)
}

func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
	go func() {
		answer, err := D.RelayDnsPacket(pkt)
		if err != nil {
			return
		}

		_ = sender(answer, &binding.Endpoint{
			Source: ep.Target,
			Target: ep.Source,
		})
	}()
}

func hijackTCPDns(conn net.Conn) {
	go func() {
		defer func(conn net.Conn) {
			_ = conn.Close()
		}(conn)

		if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
			return
		}

		for {
			var length uint16
			if binary.Read(conn, binary.BigEndian, &length) != nil {
				return
			}

			data := make([]byte, length)

			_, err := io.ReadFull(conn, data)
			if err != nil {
				return
			}

			rb, err := D.RelayDnsPacket(data)
			if err != nil {
				continue
			}

			if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
				return
			}

			if _, err = conn.Write(rb); err != nil {
				return
			}
		}
	}()
}
