mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 20:52:15 +08:00
feat: support external-doh-server
This commit is contained in:
parent
4eb13a73bf
commit
de61e81ff7
@ -96,6 +96,7 @@ type Controller struct {
|
|||||||
ExternalControllerTLS string `json:"-"`
|
ExternalControllerTLS string `json:"-"`
|
||||||
ExternalControllerUnix string `json:"-"`
|
ExternalControllerUnix string `json:"-"`
|
||||||
ExternalUI string `json:"-"`
|
ExternalUI string `json:"-"`
|
||||||
|
ExternalDohServer string `json:"-"`
|
||||||
Secret string `json:"-"`
|
Secret string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,6 +323,7 @@ type RawConfig struct {
|
|||||||
ExternalUI string `yaml:"external-ui"`
|
ExternalUI string `yaml:"external-ui"`
|
||||||
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
|
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
|
||||||
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
|
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
|
||||||
|
ExternalDohServer string `yaml:"external-doh-server"`
|
||||||
Secret string `yaml:"secret"`
|
Secret string `yaml:"secret"`
|
||||||
Interface string `yaml:"interface-name"`
|
Interface string `yaml:"interface-name"`
|
||||||
RoutingMark int `yaml:"routing-mark"`
|
RoutingMark int `yaml:"routing-mark"`
|
||||||
@ -697,6 +699,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
Secret: cfg.Secret,
|
Secret: cfg.Secret,
|
||||||
ExternalControllerUnix: cfg.ExternalControllerUnix,
|
ExternalControllerUnix: cfg.ExternalControllerUnix,
|
||||||
ExternalControllerTLS: cfg.ExternalControllerTLS,
|
ExternalControllerTLS: cfg.ExternalControllerTLS,
|
||||||
|
ExternalDohServer: cfg.ExternalDohServer,
|
||||||
},
|
},
|
||||||
UnifiedDelay: cfg.UnifiedDelay,
|
UnifiedDelay: cfg.UnifiedDelay,
|
||||||
Mode: cfg.Mode,
|
Mode: cfg.Mode,
|
||||||
|
@ -70,6 +70,10 @@ external-ui: /path/to/ui/folder/
|
|||||||
external-ui-name: xd
|
external-ui-name: xd
|
||||||
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
|
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
|
||||||
|
|
||||||
|
# 在RESTful API端口上开启DOH服务器
|
||||||
|
# !!!该URL不会验证secret, 如果开启请自行保证安全问题 !!!
|
||||||
|
external-doh-server: /dns-query
|
||||||
|
|
||||||
# interface-name: en0 # 设置出口网卡
|
# interface-name: en0 # 设置出口网卡
|
||||||
|
|
||||||
# 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint
|
# 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint
|
||||||
|
@ -50,11 +50,12 @@ func Parse(options ...Option) error {
|
|||||||
|
|
||||||
if cfg.General.ExternalController != "" {
|
if cfg.General.ExternalController != "" {
|
||||||
go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS,
|
go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS,
|
||||||
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG)
|
cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.ExternalDohServer,
|
||||||
|
cfg.General.LogLevel == log.DEBUG)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.General.ExternalControllerUnix != "" {
|
if cfg.General.ExternalControllerUnix != "" {
|
||||||
go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.LogLevel == log.DEBUG)
|
go route.StartUnix(cfg.General.ExternalControllerUnix, cfg.General.ExternalDohServer, cfg.General.LogLevel == log.DEBUG)
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.ApplyConfig(cfg, true)
|
executor.ApplyConfig(cfg, true)
|
||||||
|
67
hub/route/doh.go
Normal file
67
hub/route/doh.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
|
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dohRouter() http.Handler {
|
||||||
|
return http.HandlerFunc(dohHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dohHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if resolver.DefaultResolver == nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError("DNS section is disabled"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Header.Get("Accept") != "application/dns-message" {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError("invalid accept header"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var dnsData []byte
|
||||||
|
var err error
|
||||||
|
switch r.Method {
|
||||||
|
case "GET":
|
||||||
|
dnsData, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get("dns"))
|
||||||
|
case "POST":
|
||||||
|
if r.Header.Get("Content-Type") != "application/dns-message" {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError("invalid content-type"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dnsData, err = io.ReadAll(r.Body)
|
||||||
|
_ = r.Body.Close()
|
||||||
|
default:
|
||||||
|
render.Status(r, http.StatusMethodNotAllowed)
|
||||||
|
render.JSON(w, r, newError("method not allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
dnsData, err = resolver.RelayDnsPacket(ctx, dnsData, dnsData)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.Data(w, r, dnsData)
|
||||||
|
}
|
@ -50,7 +50,7 @@ func SetUIPath(path string) {
|
|||||||
uiPath = C.Path.Resolve(path)
|
uiPath = C.Path.Resolve(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func router(isDebug bool, withAuth bool) *chi.Mux {
|
func router(isDebug bool, withAuth bool, dohServer string) *chi.Mux {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
corsM := cors.New(cors.Options{
|
corsM := cors.New(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
@ -104,11 +104,15 @@ func router(isDebug bool, withAuth bool) *chi.Mux {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if len(dohServer) > 0 && dohServer[0] == '/' {
|
||||||
|
r.Mount(dohServer, dohRouter())
|
||||||
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(addr string, tlsAddr string, secret string,
|
func Start(addr string, tlsAddr string, secret string,
|
||||||
certificate, privateKey string, isDebug bool) {
|
certificate, privateKey string, dohServer string, isDebug bool) {
|
||||||
if serverAddr != "" {
|
if serverAddr != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -133,7 +137,7 @@ func Start(addr string, tlsAddr string, secret string,
|
|||||||
serverAddr = l.Addr().String()
|
serverAddr = l.Addr().String()
|
||||||
log.Infoln("RESTful API tls listening at: %s", serverAddr)
|
log.Infoln("RESTful API tls listening at: %s", serverAddr)
|
||||||
tlsServe := &http.Server{
|
tlsServe := &http.Server{
|
||||||
Handler: router(isDebug, true),
|
Handler: router(isDebug, true, dohServer),
|
||||||
TLSConfig: &tls.Config{
|
TLSConfig: &tls.Config{
|
||||||
Certificates: []tls.Certificate{c},
|
Certificates: []tls.Certificate{c},
|
||||||
},
|
},
|
||||||
@ -152,13 +156,13 @@ func Start(addr string, tlsAddr string, secret string,
|
|||||||
serverAddr = l.Addr().String()
|
serverAddr = l.Addr().String()
|
||||||
log.Infoln("RESTful API listening at: %s", serverAddr)
|
log.Infoln("RESTful API listening at: %s", serverAddr)
|
||||||
|
|
||||||
if err = http.Serve(l, router(isDebug, true)); err != nil {
|
if err = http.Serve(l, router(isDebug, true, dohServer)); err != nil {
|
||||||
log.Errorln("External controller serve error: %s", err)
|
log.Errorln("External controller serve error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartUnix(addr string, isDebug bool) {
|
func StartUnix(addr string, dohServer string, isDebug bool) {
|
||||||
addr = C.Path.Resolve(addr)
|
addr = C.Path.Resolve(addr)
|
||||||
|
|
||||||
dir := filepath.Dir(addr)
|
dir := filepath.Dir(addr)
|
||||||
@ -186,7 +190,7 @@ func StartUnix(addr string, isDebug bool) {
|
|||||||
serverAddr = l.Addr().String()
|
serverAddr = l.Addr().String()
|
||||||
log.Infoln("RESTful API unix listening at: %s", serverAddr)
|
log.Infoln("RESTful API unix listening at: %s", serverAddr)
|
||||||
|
|
||||||
if err = http.Serve(l, router(isDebug, false)); err != nil {
|
if err = http.Serve(l, router(isDebug, false, dohServer)); err != nil {
|
||||||
log.Errorln("External controller unix serve error: %s", err)
|
log.Errorln("External controller unix serve error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user