Feature: add dns query json api

This commit is contained in:
Dreamacro 2023-01-16 15:20:39 +08:00
parent 0489a7391b
commit a26b670420
5 changed files with 100 additions and 26 deletions

View File

@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
"github.com/miekg/dns"
) )
var ( var (
@ -40,6 +42,7 @@ type Resolver interface {
ResolveIP(host string) (ip net.IP, err error) ResolveIP(host string) (ip net.IP, err error)
ResolveIPv4(host string) (ip net.IP, err error) ResolveIPv4(host string) (ip net.IP, err error)
ResolveIPv6(host string) (ip net.IP, err error) ResolveIPv6(host string) (ip net.IP, err error)
ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error)
} }
// LookupIPv4 with a host, return ipv4 list // LookupIPv4 with a host, return ipv4 list

View File

@ -24,19 +24,6 @@ func configRouter() http.Handler {
return r return r
} }
type configSchema struct {
Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
}
func getConfigs(w http.ResponseWriter, r *http.Request) { func getConfigs(w http.ResponseWriter, r *http.Request) {
general := executor.GetGeneral() general := executor.GetGeneral()
render.JSON(w, r, general) render.JSON(w, r, general)
@ -51,8 +38,19 @@ func pointerOrDefault(p *int, def int) int {
} }
func patchConfigs(w http.ResponseWriter, r *http.Request) { func patchConfigs(w http.ResponseWriter, r *http.Request) {
general := &configSchema{} general := struct {
if err := render.DecodeJSON(r.Body, general); err != nil { Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
}{}
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
@ -92,13 +90,11 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
render.NoContent(w, r) render.NoContent(w, r)
} }
type updateConfigRequest struct {
Path string `json:"path"`
Payload string `json:"payload"`
}
func updateConfigs(w http.ResponseWriter, r *http.Request) { func updateConfigs(w http.ResponseWriter, r *http.Request) {
req := updateConfigRequest{} req := struct {
Path string `json:"path"`
Payload string `json:"payload"`
}{}
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

@ -66,12 +66,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

@ -70,6 +70,7 @@ func Start(addr string, secret string) {
r.Mount("/rules", ruleRouter()) r.Mount("/rules", ruleRouter())
r.Mount("/connections", connectionRouter()) r.Mount("/connections", connectionRouter())
r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/dns", dnsRouter())
}) })
if uiPath != "" { if uiPath != "" {