feat: support external-doh-server
Some checks are pending
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run

This commit is contained in:
wwqgtxx 2024-07-23 00:01:41 +08:00
parent 4eb13a73bf
commit de61e81ff7
5 changed files with 87 additions and 8 deletions

View File

@ -96,6 +96,7 @@ type Controller struct {
ExternalControllerTLS string `json:"-"`
ExternalControllerUnix string `json:"-"`
ExternalUI string `json:"-"`
ExternalDohServer string `json:"-"`
Secret string `json:"-"`
}
@ -322,6 +323,7 @@ type RawConfig struct {
ExternalUI string `yaml:"external-ui"`
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
ExternalDohServer string `yaml:"external-doh-server"`
Secret string `yaml:"secret"`
Interface string `yaml:"interface-name"`
RoutingMark int `yaml:"routing-mark"`
@ -697,6 +699,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
Secret: cfg.Secret,
ExternalControllerUnix: cfg.ExternalControllerUnix,
ExternalControllerTLS: cfg.ExternalControllerTLS,
ExternalDohServer: cfg.ExternalDohServer,
},
UnifiedDelay: cfg.UnifiedDelay,
Mode: cfg.Mode,

View File

@ -70,6 +70,10 @@ external-ui: /path/to/ui/folder/
external-ui-name: xd
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 # 设置出口网卡
# 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint

View File

@ -50,11 +50,12 @@ func Parse(options ...Option) error {
if cfg.General.ExternalController != "" {
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 != "" {
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)

67
hub/route/doh.go Normal file
View 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)
}

View File

@ -50,7 +50,7 @@ func SetUIPath(path string) {
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()
corsM := cors.New(cors.Options{
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
}
func Start(addr string, tlsAddr string, secret string,
certificate, privateKey string, isDebug bool) {
certificate, privateKey string, dohServer string, isDebug bool) {
if serverAddr != "" {
return
}
@ -133,7 +137,7 @@ func Start(addr string, tlsAddr string, secret string,
serverAddr = l.Addr().String()
log.Infoln("RESTful API tls listening at: %s", serverAddr)
tlsServe := &http.Server{
Handler: router(isDebug, true),
Handler: router(isDebug, true, dohServer),
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{c},
},
@ -152,13 +156,13 @@ func Start(addr string, tlsAddr string, secret string,
serverAddr = l.Addr().String()
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)
}
}
func StartUnix(addr string, isDebug bool) {
func StartUnix(addr string, dohServer string, isDebug bool) {
addr = C.Path.Resolve(addr)
dir := filepath.Dir(addr)
@ -186,7 +190,7 @@ func StartUnix(addr string, isDebug bool) {
serverAddr = l.Addr().String()
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)
}
}