github.com/yaling888/clash@v1.53.0/hub/route/dns.go (about) 1 package route 2 3 import ( 4 "context" 5 "math" 6 "net/http" 7 "strconv" 8 "strings" 9 10 "github.com/go-chi/chi/v5" 11 "github.com/go-chi/render" 12 "github.com/miekg/dns" 13 "github.com/samber/lo" 14 15 "github.com/yaling888/clash/common/util" 16 "github.com/yaling888/clash/component/resolver" 17 ) 18 19 func dnsRouter() http.Handler { 20 r := chi.NewRouter() 21 r.Get("/query", queryDNS) 22 return r 23 } 24 25 func queryDNS(w http.ResponseWriter, r *http.Request) { 26 if resolver.DefaultResolver == nil { 27 render.Status(r, http.StatusInternalServerError) 28 render.JSON(w, r, newError("DNS section is disabled")) 29 return 30 } 31 32 var ( 33 name = r.URL.Query().Get("name") 34 proxy = r.URL.Query().Get("proxy") 35 qTypeStr = util.EmptyOr(r.URL.Query().Get("type"), "A") 36 cacheStr = util.EmptyOr(r.URL.Query().Get("cache"), "1") 37 ) 38 39 qType, exist := dns.StringToType[strings.ToUpper(qTypeStr)] 40 if !exist { 41 render.Status(r, http.StatusBadRequest) 42 render.JSON(w, r, newError("invalid query type")) 43 return 44 } 45 46 var ( 47 resp *dns.Msg 48 source string 49 msg = dns.Msg{} 50 cache = true 51 ) 52 53 c, err := strconv.ParseBool(cacheStr) 54 if err == nil { 55 cache = c 56 } 57 58 msg.SetQuestion(dns.Fqdn(name), qType) 59 60 ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) 61 defer cancel() 62 63 if proxy != "" { 64 ctx = resolver.WithProxy(ctx, proxy) 65 } 66 67 if cache { 68 resp, source, err = resolver.DefaultResolver.ExchangeContext(ctx, &msg) 69 } else { 70 resp, source, err = resolver.DefaultResolver.ExchangeContextWithoutCache(ctx, &msg) 71 } 72 if err != nil { 73 render.Status(r, http.StatusInternalServerError) 74 render.JSON(w, r, newError(err.Error())) 75 return 76 } 77 78 responseData := render.M{ 79 "Server": source, 80 "Cache": cache, 81 "Status": resp.Rcode, 82 "Question": resp.Question, 83 "TC": resp.Truncated, 84 "RD": resp.RecursionDesired, 85 "RA": resp.RecursionAvailable, 86 "AD": resp.AuthenticatedData, 87 "CD": resp.CheckingDisabled, 88 } 89 90 rr2Json := func(rr dns.RR, _ int) render.M { 91 header := rr.Header() 92 return render.M{ 93 "name": header.Name, 94 "type": header.Rrtype, 95 "TTL": header.Ttl, 96 "data": lo.Substring(rr.String(), len(header.String()), math.MaxUint), 97 } 98 } 99 100 if len(resp.Answer) > 0 { 101 responseData["Answer"] = lo.Map(resp.Answer, rr2Json) 102 } 103 if len(resp.Ns) > 0 { 104 responseData["Authority"] = lo.Map(resp.Ns, rr2Json) 105 } 106 if len(resp.Extra) > 0 { 107 responseData["Additional"] = lo.Map(resp.Extra, rr2Json) 108 } 109 110 render.JSON(w, r, responseData) 111 }