Feature: add dns query json api

This commit is contained in:
gVisor bot 2023-01-16 15:20:39 +08:00
parent 2e77a3beda
commit a4a9886077
5 changed files with 88 additions and 13 deletions

View file

@ -11,6 +11,8 @@ import (
"time" "time"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
"github.com/miekg/dns"
) )
var ( var (
@ -44,6 +46,7 @@ type Resolver interface {
ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error)
ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error)
ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error)
ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error)
} }
// LookupIPv4WithResolver same as LookupIPv4, but with a resolver // LookupIPv4WithResolver same as LookupIPv4, but with a resolver

View file

@ -104,7 +104,6 @@ func pointerOrDefault(p *int, def int) int {
if p != nil { if p != nil {
return *p return *p
} }
return def return def
} }
@ -210,7 +209,7 @@ func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicS
func patchConfigs(w http.ResponseWriter, r *http.Request) { func patchConfigs(w http.ResponseWriter, r *http.Request) {
general := &configSchema{} general := &configSchema{}
if err := render.DecodeJSON(r.Body, general); err != nil { if err := render.DecodeJSON(r.Body, &general); err != nil {
render.Status(r, http.StatusBadRequest) render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest) render.JSON(w, r, ErrBadRequest)
return return
@ -266,13 +265,11 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
render.NoContent(w, r) render.NoContent(w, r)
} }
type updateConfigRequest struct { func updateConfigs(w http.ResponseWriter, r *http.Request) {
req := struct {
Path string `json:"path"` Path string `json:"path"`
Payload string `json:"payload"` Payload string `json:"payload"`
} }{}
func updateConfigs(w http.ResponseWriter, r *http.Request) {
req := updateConfigRequest{}
if err := render.DecodeJSON(r.Body, &req); err != nil { if err := render.DecodeJSON(r.Body, &req); err != nil {
render.Status(r, http.StatusBadRequest) render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest) render.JSON(w, r, ErrBadRequest)

76
hub/route/dns.go Normal file
View file

@ -0,0 +1,76 @@
package route
import (
"context"
"math"
"net/http"
"github.com/Dreamacro/clash/component/resolver"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"github.com/miekg/dns"
"github.com/samber/lo"
)
func dnsRouter() http.Handler {
r := chi.NewRouter()
r.Get("/query", queryDNS)
return r
}
func queryDNS(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
qTypeStr, _ := lo.Coalesce(r.URL.Query().Get("type"), "A")
qType, exist := dns.StringToType[qTypeStr]
if !exist {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, newError("invalid query type"))
return
}
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
defer cancel()
msg := dns.Msg{}
msg.SetQuestion(dns.Fqdn(name), qType)
resp, err := resolver.DefaultResolver.ExchangeContext(ctx, &msg)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(err.Error()))
return
}
responseData := render.M{
"Status": resp.Rcode,
"Question": resp.Question,
"TC": resp.Truncated,
"RD": resp.RecursionDesired,
"RA": resp.RecursionAvailable,
"AD": resp.AuthenticatedData,
"CD": resp.CheckingDisabled,
}
rr2Json := func(rr dns.RR, _ int) render.M {
header := rr.Header()
return render.M{
"name": header.Name,
"type": header.Rrtype,
"TTL": header.Ttl,
"data": lo.Substring(rr.String(), len(header.String()), math.MaxUint),
}
}
if len(resp.Answer) > 0 {
responseData["Answer"] = lo.Map(resp.Answer, rr2Json)
}
if len(resp.Ns) > 0 {
responseData["Authority"] = lo.Map(resp.Ns, rr2Json)
}
if len(resp.Extra) > 0 {
responseData["Additional"] = lo.Map(resp.Extra, rr2Json)
}
render.JSON(w, r, responseData)
}

View file

@ -70,12 +70,10 @@ func getProxy(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, proxy) render.JSON(w, r, proxy)
} }
type UpdateProxyRequest struct {
Name string `json:"name"`
}
func updateProxy(w http.ResponseWriter, r *http.Request) { func updateProxy(w http.ResponseWriter, r *http.Request) {
req := UpdateProxyRequest{} req := struct {
Name string `json:"name"`
}{}
if err := render.DecodeJSON(r.Body, &req); err != nil { if err := render.DecodeJSON(r.Body, &req); err != nil {
render.Status(r, http.StatusBadRequest) render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest) render.JSON(w, r, ErrBadRequest)

View file

@ -73,6 +73,7 @@ func Start(addr string, tlsAddr string, secret string,
r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/cache", cacheRouter()) r.Mount("/cache", cacheRouter())
r.Mount("/dns", dnsRouter())
}) })
if uiPath != "" { if uiPath != "" {