doh.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package route
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "io"
  6. "net/http"
  7. "github.com/metacubex/mihomo/component/resolver"
  8. "github.com/go-chi/render"
  9. )
  10. func dohRouter() http.Handler {
  11. return http.HandlerFunc(dohHandler)
  12. }
  13. func dohHandler(w http.ResponseWriter, r *http.Request) {
  14. if resolver.DefaultResolver == nil {
  15. render.Status(r, http.StatusInternalServerError)
  16. render.PlainText(w, r, "DNS section is disabled")
  17. return
  18. }
  19. var dnsData []byte
  20. var err error
  21. switch r.Method {
  22. case "GET":
  23. dnsData, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get("dns"))
  24. case "POST":
  25. if r.Header.Get("Content-Type") != "application/dns-message" {
  26. render.Status(r, http.StatusInternalServerError)
  27. render.PlainText(w, r, "invalid content-type")
  28. return
  29. }
  30. reader := io.LimitReader(r.Body, 65535) // according to rfc8484, the maximum size of the DNS message is 65535 bytes
  31. dnsData, err = io.ReadAll(reader)
  32. _ = r.Body.Close()
  33. default:
  34. render.Status(r, http.StatusMethodNotAllowed)
  35. render.PlainText(w, r, "method not allowed")
  36. return
  37. }
  38. if err != nil {
  39. render.Status(r, http.StatusInternalServerError)
  40. render.PlainText(w, r, err.Error())
  41. return
  42. }
  43. ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
  44. defer cancel()
  45. dnsData, err = resolver.RelayDnsPacket(ctx, dnsData, dnsData)
  46. if err != nil {
  47. render.Status(r, http.StatusInternalServerError)
  48. render.PlainText(w, r, err.Error())
  49. return
  50. }
  51. w.Header().Set("Content-Type", "application/dns-message")
  52. w.WriteHeader(http.StatusOK)
  53. _, _ = w.Write(dnsData)
  54. }