2
0

dns.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. "github.com/miekg/dns"
  7. )
  8. func StartDNSServer(port int) error {
  9. dns.HandleFunc("ch.at.", handleDNS)
  10. dns.HandleFunc(".", handleDNS)
  11. server := &dns.Server{
  12. Addr: fmt.Sprintf(":%d", port),
  13. Net: "udp",
  14. }
  15. return server.ListenAndServe()
  16. }
  17. func handleDNS(w dns.ResponseWriter, r *dns.Msg) {
  18. if !rateLimitAllow(w.RemoteAddr().String()) {
  19. return
  20. }
  21. if len(r.Question) == 0 {
  22. return
  23. }
  24. m := new(dns.Msg)
  25. m.SetReply(r)
  26. m.Authoritative = true
  27. for _, q := range r.Question {
  28. if q.Qtype != dns.TypeTXT {
  29. continue
  30. }
  31. name := strings.TrimSuffix(strings.TrimSuffix(q.Name, "."), ".ch.at")
  32. prompt := strings.ReplaceAll(name, "-", " ")
  33. // Optimize prompt for DNS constraints
  34. dnsPrompt := "Answer in 500 characters or less, no markdown formatting: " + prompt
  35. // Stream LLM response with hard deadline
  36. ch := make(chan string)
  37. done := make(chan bool)
  38. go func() {
  39. LLM(dnsPrompt, ch)
  40. }()
  41. var response strings.Builder
  42. deadline := time.After(4 * time.Second) // Safe middle ground for DNS clients
  43. channelClosed := false
  44. for {
  45. select {
  46. case chunk, ok := <-ch:
  47. if !ok {
  48. channelClosed = true
  49. goto respond
  50. }
  51. response.WriteString(chunk)
  52. if response.Len() >= 500 {
  53. goto respond
  54. }
  55. case <-deadline:
  56. if response.Len() == 0 {
  57. response.WriteString("Request timed out")
  58. } else if !channelClosed {
  59. response.WriteString("... (incomplete)")
  60. }
  61. goto respond
  62. }
  63. }
  64. respond:
  65. close(done)
  66. finalResponse := response.String()
  67. if len(finalResponse) > 500 {
  68. finalResponse = finalResponse[:497] + "..."
  69. } else if len(finalResponse) == 500 && !channelClosed {
  70. // We hit the exact limit but stream is still going
  71. finalResponse = finalResponse[:497] + "..."
  72. }
  73. // Split response into 255-byte chunks for DNS TXT records
  74. var txtStrings []string
  75. for i := 0; i < len(finalResponse); i += 255 {
  76. end := i + 255
  77. if end > len(finalResponse) {
  78. end = len(finalResponse)
  79. }
  80. txtStrings = append(txtStrings, finalResponse[i:end])
  81. }
  82. txt := &dns.TXT{
  83. Hdr: dns.RR_Header{
  84. Name: q.Name,
  85. Rrtype: dns.TypeTXT,
  86. Class: dns.ClassINET,
  87. Ttl: 60,
  88. },
  89. Txt: txtStrings,
  90. }
  91. m.Answer = append(m.Answer, txt)
  92. }
  93. w.WriteMsg(m)
  94. }