github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/clashapi/dns.go (about)

     1  package clashapi
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  
     7  	"github.com/sagernet/sing-box/adapter"
     8  	C "github.com/sagernet/sing-box/constant"
     9  	"github.com/sagernet/sing/common"
    10  
    11  	"github.com/go-chi/chi/v5"
    12  	"github.com/go-chi/render"
    13  	"github.com/miekg/dns"
    14  )
    15  
    16  func dnsRouter(router adapter.Router) http.Handler {
    17  	r := chi.NewRouter()
    18  	r.Get("/query", queryDNS(router))
    19  	return r
    20  }
    21  
    22  func queryDNS(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
    23  	return func(w http.ResponseWriter, r *http.Request) {
    24  		name := r.URL.Query().Get("name")
    25  		qTypeStr := r.URL.Query().Get("type")
    26  		if qTypeStr == "" {
    27  			qTypeStr = "A"
    28  		}
    29  
    30  		qType, exist := dns.StringToType[qTypeStr]
    31  		if !exist {
    32  			render.Status(r, http.StatusBadRequest)
    33  			render.JSON(w, r, newError("invalid query type"))
    34  			return
    35  		}
    36  
    37  		ctx, cancel := context.WithTimeout(context.Background(), C.DNSTimeout)
    38  		defer cancel()
    39  
    40  		msg := dns.Msg{}
    41  		msg.SetQuestion(dns.Fqdn(name), qType)
    42  		resp, err := router.Exchange(ctx, &msg)
    43  		if err != nil {
    44  			render.Status(r, http.StatusInternalServerError)
    45  			render.JSON(w, r, newError(err.Error()))
    46  			return
    47  		}
    48  
    49  		responseData := render.M{
    50  			"Status":   resp.Rcode,
    51  			"Question": resp.Question,
    52  			"Server":   "internal",
    53  			"TC":       resp.Truncated,
    54  			"RD":       resp.RecursionDesired,
    55  			"RA":       resp.RecursionAvailable,
    56  			"AD":       resp.AuthenticatedData,
    57  			"CD":       resp.CheckingDisabled,
    58  		}
    59  
    60  		rr2Json := func(rr dns.RR) render.M {
    61  			header := rr.Header()
    62  			return render.M{
    63  				"name": header.Name,
    64  				"type": header.Rrtype,
    65  				"TTL":  header.Ttl,
    66  				"data": rr.String()[len(header.String()):],
    67  			}
    68  		}
    69  
    70  		if len(resp.Answer) > 0 {
    71  			responseData["Answer"] = common.Map(resp.Answer, rr2Json)
    72  		}
    73  		if len(resp.Ns) > 0 {
    74  			responseData["Authority"] = common.Map(resp.Ns, rr2Json)
    75  		}
    76  		if len(resp.Extra) > 0 {
    77  			responseData["Additional"] = common.Map(resp.Extra, rr2Json)
    78  		}
    79  
    80  		render.JSON(w, r, responseData)
    81  	}
    82  }