github.com/kelleygo/clashcore@v1.0.2/hub/route/dns.go (about)

     1  package route
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"net/http"
     7  
     8  	"github.com/kelleygo/clashcore/component/resolver"
     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  
    16  func dnsRouter() http.Handler {
    17  	r := chi.NewRouter()
    18  	r.Get("/query", queryDNS)
    19  	return r
    20  }
    21  
    22  func queryDNS(w http.ResponseWriter, r *http.Request) {
    23  	if resolver.DefaultResolver == nil {
    24  		render.Status(r, http.StatusInternalServerError)
    25  		render.JSON(w, r, newError("DNS section is disabled"))
    26  		return
    27  	}
    28  
    29  	name := r.URL.Query().Get("name")
    30  	qTypeStr, _ := lo.Coalesce(r.URL.Query().Get("type"), "A")
    31  
    32  	qType, exist := dns.StringToType[qTypeStr]
    33  	if !exist {
    34  		render.Status(r, http.StatusBadRequest)
    35  		render.JSON(w, r, newError("invalid query type"))
    36  		return
    37  	}
    38  
    39  	ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
    40  	defer cancel()
    41  
    42  	msg := dns.Msg{}
    43  	msg.SetQuestion(dns.Fqdn(name), qType)
    44  	resp, err := resolver.DefaultResolver.ExchangeContext(ctx, &msg)
    45  	if err != nil {
    46  		render.Status(r, http.StatusInternalServerError)
    47  		render.JSON(w, r, newError(err.Error()))
    48  		return
    49  	}
    50  
    51  	responseData := render.M{
    52  		"Status":   resp.Rcode,
    53  		"Question": resp.Question,
    54  		"TC":       resp.Truncated,
    55  		"RD":       resp.RecursionDesired,
    56  		"RA":       resp.RecursionAvailable,
    57  		"AD":       resp.AuthenticatedData,
    58  		"CD":       resp.CheckingDisabled,
    59  	}
    60  
    61  	rr2Json := func(rr dns.RR, _ int) render.M {
    62  		header := rr.Header()
    63  		return render.M{
    64  			"name": header.Name,
    65  			"type": header.Rrtype,
    66  			"TTL":  header.Ttl,
    67  			"data": lo.Substring(rr.String(), len(header.String()), math.MaxUint),
    68  		}
    69  	}
    70  
    71  	if len(resp.Answer) > 0 {
    72  		responseData["Answer"] = lo.Map(resp.Answer, rr2Json)
    73  	}
    74  	if len(resp.Ns) > 0 {
    75  		responseData["Authority"] = lo.Map(resp.Ns, rr2Json)
    76  	}
    77  	if len(resp.Extra) > 0 {
    78  		responseData["Additional"] = lo.Map(resp.Extra, rr2Json)
    79  	}
    80  
    81  	render.JSON(w, r, responseData)
    82  }