Skip to content

Commit

Permalink
feat: support external-doh-server
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Jul 22, 2024
1 parent 4eb13a7 commit de61e81
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 8 deletions.
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type Controller struct {
ExternalControllerTLS string `json:"-"`
ExternalControllerUnix string `json:"-"`
ExternalUI string `json:"-"`
ExternalDohServer string `json:"-"`
Secret string `json:"-"`
}

Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions hub/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions hub/route/doh.go
Original file line number Diff line number Diff line change
@@ -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)
}
16 changes: 10 additions & 6 deletions hub/route/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{"*"},
Expand Down Expand Up @@ -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
}
Expand All @@ -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},
},
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand Down

0 comments on commit de61e81

Please sign in to comment.