Clash.Meta/hub/route/server.go

525 lines
12 KiB
Go
Raw Normal View History

2018-11-21 13:47:46 +08:00
package route
2018-06-18 11:31:49 +08:00
import (
"bytes"
"crypto/subtle"
"crypto/tls"
2018-06-18 11:31:49 +08:00
"encoding/json"
"net"
2018-06-18 11:31:49 +08:00
"net/http"
2024-04-17 11:12:29 +08:00
"os"
"path/filepath"
"runtime/debug"
2018-10-06 13:15:02 +08:00
"strings"
"syscall"
2018-06-18 11:31:49 +08:00
"time"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/adapter/inbound"
CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/common/utils"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel/statistic"
2023-06-06 10:47:50 +08:00
2021-04-03 14:59:03 +08:00
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
2018-07-19 09:31:53 +08:00
"github.com/go-chi/cors"
2018-06-18 11:31:49 +08:00
"github.com/go-chi/render"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
2018-06-18 11:31:49 +08:00
)
2018-11-21 13:47:46 +08:00
var (
2018-12-20 01:29:13 +08:00
uiPath = ""
httpServer *http.Server
tlsServer *http.Server
unixServer *http.Server
pipeServer *http.Server
2018-11-21 13:47:46 +08:00
)
2018-10-06 13:15:02 +08:00
2018-06-18 11:31:49 +08:00
type Traffic struct {
Up int64 `json:"up"`
Down int64 `json:"down"`
}
2023-04-08 00:55:25 +08:00
type Memory struct {
Inuse uint64 `json:"inuse"`
OSLimit uint64 `json:"oslimit"` // maybe we need it in the future
}
type Config struct {
Addr string
TLSAddr string
UnixAddr string
PipeAddr string
Secret string
Certificate string
PrivateKey string
DohServer string
IsDebug bool
}
func ReCreateServer(cfg *Config) {
go start(cfg)
go startTLS(cfg)
go startUnix(cfg)
if inbound.SupportNamedPipe {
go startPipe(cfg)
}
}
2018-12-20 01:29:13 +08:00
func SetUIPath(path string) {
uiPath = C.Path.Resolve(path)
2018-12-20 01:29:13 +08:00
}
func router(isDebug bool, secret string, dohServer string) *chi.Mux {
2018-06-18 11:31:49 +08:00
r := chi.NewRouter()
2022-04-24 02:07:57 +08:00
corsM := cors.New(cors.Options{
2018-07-19 09:31:53 +08:00
AllowedOrigins: []string{"*"},
2018-12-29 14:11:54 +08:00
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
2018-10-06 13:15:02 +08:00
AllowedHeaders: []string{"Content-Type", "Authorization"},
2018-07-19 09:31:53 +08:00
MaxAge: 300,
})
2023-12-27 16:20:40 +08:00
r.Use(setPrivateNetworkAccess)
2022-04-24 02:07:57 +08:00
r.Use(corsM.Handler)
if isDebug {
r.Mount("/debug", func() http.Handler {
r := chi.NewRouter()
r.Put("/gc", func(w http.ResponseWriter, r *http.Request) {
debug.FreeOSMemory()
})
handler := middleware.Profiler
r.Mount("/", handler())
return r
}())
}
r.Group(func(r chi.Router) {
if secret != "" {
r.Use(authentication(secret))
}
2020-02-21 18:00:19 +08:00
r.Get("/", hello)
r.Get("/logs", getLogs)
r.Get("/traffic", traffic)
2023-04-08 00:55:25 +08:00
r.Get("/memory", memory)
2019-09-13 17:44:30 +08:00
r.Get("/version", version)
r.Mount("/configs", configRouter())
r.Mount("/proxies", proxyRouter())
2022-05-30 21:55:09 +08:00
r.Mount("/group", GroupRouter())
r.Mount("/rules", ruleRouter())
r.Mount("/connections", connectionRouter())
r.Mount("/providers/proxies", proxyProviderRouter())
2021-12-02 22:56:17 +08:00
r.Mount("/providers/rules", ruleProviderRouter())
2023-12-03 14:39:01 +08:00
r.Mount("/cache", cacheRouter())
2023-01-16 15:20:39 +08:00
r.Mount("/dns", dnsRouter())
2023-03-14 22:18:55 +08:00
r.Mount("/restart", restartRouter())
r.Mount("/upgrade", upgradeRouter())
addExternalRouters(r)
})
2018-06-18 11:31:49 +08:00
2018-12-20 01:29:13 +08:00
if uiPath != "" {
r.Group(func(r chi.Router) {
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(uiPath)))
2019-09-08 22:33:52 +08:00
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
fs.ServeHTTP(w, r)
})
2018-12-20 01:29:13 +08:00
})
}
2024-07-23 00:01:41 +08:00
if len(dohServer) > 0 && dohServer[0] == '/' {
r.Mount(dohServer, dohRouter())
}
return r
}
func start(cfg *Config) {
// first stop existing server
if httpServer != nil {
_ = httpServer.Close()
httpServer = nil
}
// handle addr
if len(cfg.Addr) > 0 {
l, err := inbound.Listen("tcp", cfg.Addr)
if err != nil {
log.Errorln("External controller listen error: %s", err)
return
}
log.Infoln("RESTful API listening at: %s", l.Addr().String())
2022-12-03 14:14:15 +08:00
server := &http.Server{
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
}
2024-09-10 16:43:00 +08:00
httpServer = server
if err = server.Serve(l); err != nil {
log.Errorln("External controller serve error: %s", err)
}
}
}
2022-12-03 14:14:15 +08:00
func startTLS(cfg *Config) {
// first stop existing server
if tlsServer != nil {
_ = tlsServer.Close()
tlsServer = nil
}
2018-12-20 01:29:13 +08:00
// handle tlsAddr
if len(cfg.TLSAddr) > 0 {
c, err := CN.ParseCert(cfg.Certificate, cfg.PrivateKey, C.Path)
if err != nil {
log.Errorln("External controller tls listen error: %s", err)
return
}
l, err := inbound.Listen("tcp", cfg.TLSAddr)
if err != nil {
log.Errorln("External controller tls listen error: %s", err)
return
}
log.Infoln("RESTful API tls listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer),
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{c},
},
}
2024-09-10 16:43:00 +08:00
tlsServer = server
if err = server.ServeTLS(l, "", ""); err != nil {
log.Errorln("External controller tls serve error: %s", err)
}
}
}
func startUnix(cfg *Config) {
// first stop existing server
if unixServer != nil {
_ = unixServer.Close()
unixServer = nil
2018-06-18 11:31:49 +08:00
}
// handle addr
if len(cfg.UnixAddr) > 0 {
addr := C.Path.Resolve(cfg.UnixAddr)
2018-06-18 11:31:49 +08:00
dir := filepath.Dir(addr)
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, 0o755); err != nil {
log.Errorln("External controller unix listen error: %s", err)
return
}
}
2024-04-19 17:33:00 +08:00
// https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
//
// Note: As mentioned above in the security section, when a socket binds a socket to a valid pathname address,
// a socket file is created within the filesystem. On Linux, the application is expected to unlink
// (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address.
// The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API)
// should be used to delete the socket file prior to calling bind with the same path.
_ = syscall.Unlink(addr)
l, err := inbound.Listen("unix", addr)
if err != nil {
2024-04-17 11:12:29 +08:00
log.Errorln("External controller unix listen error: %s", err)
return
}
log.Infoln("RESTful API unix listening at: %s", l.Addr().String())
2024-04-17 11:12:29 +08:00
server := &http.Server{
Handler: router(cfg.IsDebug, "", cfg.DohServer),
}
2024-09-10 16:43:00 +08:00
unixServer = server
if err = server.Serve(l); err != nil {
log.Errorln("External controller unix serve error: %s", err)
}
}
}
func startPipe(cfg *Config) {
// first stop existing server
if pipeServer != nil {
_ = pipeServer.Close()
pipeServer = nil
}
// handle addr
if len(cfg.PipeAddr) > 0 {
if !strings.HasPrefix(cfg.PipeAddr, "\\\\.\\pipe\\") { // windows namedpipe must start with "\\.\pipe\"
log.Errorln("External controller pipe listen error: windows namedpipe must start with \"\\\\.\\pipe\\\"")
return
}
l, err := inbound.ListenNamedPipe(cfg.PipeAddr)
if err != nil {
log.Errorln("External controller pipe listen error: %s", err)
return
}
log.Infoln("RESTful API pipe listening at: %s", l.Addr().String())
server := &http.Server{
Handler: router(cfg.IsDebug, "", cfg.DohServer),
}
pipeServer = server
if err = server.Serve(l); err != nil {
log.Errorln("External controller pipe serve error: %s", err)
}
}
}
2023-12-27 16:20:40 +08:00
func setPrivateNetworkAccess(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
w.Header().Add("Access-Control-Allow-Private-Network", "true")
}
next.ServeHTTP(w, r)
})
}
func safeEuqal(a, b string) bool {
2023-06-06 10:47:50 +08:00
aBuf := utils.ImmutableBytesFromString(a)
bBuf := utils.ImmutableBytesFromString(b)
return subtle.ConstantTimeCompare(aBuf, bBuf) == 1
}
func authentication(secret string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Browser websocket not support custom header
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" {
token := r.URL.Query().Get("token")
if !safeEuqal(token, secret) {
render.Status(r, http.StatusUnauthorized)
render.JSON(w, r, ErrUnauthorized)
return
}
next.ServeHTTP(w, r)
return
}
header := r.Header.Get("Authorization")
bearer, token, found := strings.Cut(header, " ")
2018-10-06 13:15:02 +08:00
hasInvalidHeader := bearer != "Bearer"
hasInvalidSecret := !found || !safeEuqal(token, secret)
if hasInvalidHeader || hasInvalidSecret {
2019-09-30 14:13:29 +08:00
render.Status(r, http.StatusUnauthorized)
render.JSON(w, r, ErrUnauthorized)
return
}
next.ServeHTTP(w, r)
2018-10-06 13:15:02 +08:00
}
return http.HandlerFunc(fn)
2018-10-06 13:15:02 +08:00
}
}
2018-12-29 14:11:54 +08:00
func hello(w http.ResponseWriter, r *http.Request) {
2023-11-03 21:01:45 +08:00
render.JSON(w, r, render.M{"hello": "mihomo"})
2018-12-29 14:11:54 +08:00
}
2018-06-18 11:31:49 +08:00
func traffic(w http.ResponseWriter, r *http.Request) {
var wsConn net.Conn
if r.Header.Get("Upgrade") == "websocket" {
var err error
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
if err != nil {
return
}
}
if wsConn == nil {
w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK)
}
2018-06-18 11:31:49 +08:00
tick := time.NewTicker(time.Second)
2020-07-13 00:25:54 +08:00
defer tick.Stop()
t := statistic.DefaultManager
buf := &bytes.Buffer{}
var err error
2018-06-18 11:31:49 +08:00
for range tick.C {
buf.Reset()
2018-06-18 11:31:49 +08:00
up, down := t.Now()
if err := json.NewEncoder(buf).Encode(Traffic{
2018-06-18 11:31:49 +08:00
Up: up,
Down: down,
}); err != nil {
break
}
if wsConn == nil {
_, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush()
} else {
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
}
if err != nil {
break
}
2018-06-18 11:31:49 +08:00
}
}
2023-04-08 00:55:25 +08:00
func memory(w http.ResponseWriter, r *http.Request) {
var wsConn net.Conn
if r.Header.Get("Upgrade") == "websocket" {
2023-04-08 00:55:25 +08:00
var err error
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
2023-04-08 00:55:25 +08:00
if err != nil {
return
}
}
if wsConn == nil {
w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK)
}
tick := time.NewTicker(time.Second)
defer tick.Stop()
t := statistic.DefaultManager
buf := &bytes.Buffer{}
var err error
2023-04-08 01:39:48 +08:00
first := true
2023-04-08 00:55:25 +08:00
for range tick.C {
buf.Reset()
2023-04-08 01:39:48 +08:00
inuse := t.Memory()
// make chat.js begin with zero
// this is shit var,but we need output 0 for first time
if first {
inuse = 0
first = false
}
2023-04-08 00:55:25 +08:00
if err := json.NewEncoder(buf).Encode(Memory{
2023-04-08 01:39:48 +08:00
Inuse: inuse,
2023-04-08 00:55:25 +08:00
OSLimit: 0,
}); err != nil {
break
}
if wsConn == nil {
_, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush()
} else {
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
2023-04-08 00:55:25 +08:00
}
if err != nil {
break
}
}
}
2018-06-22 13:15:20 +08:00
type Log struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
type LogStructuredField struct {
Key string `json:"key"`
Value string `json:"value"`
}
type LogStructured struct {
Time string `json:"time"`
Level string `json:"level"`
Message string `json:"message"`
Fields []LogStructuredField `json:"fields"`
}
2018-06-22 13:15:20 +08:00
2018-06-18 11:31:49 +08:00
func getLogs(w http.ResponseWriter, r *http.Request) {
2018-08-12 19:35:13 +08:00
levelText := r.URL.Query().Get("level")
if levelText == "" {
levelText = "info"
2018-06-22 13:15:20 +08:00
}
formatText := r.URL.Query().Get("format")
isStructured := false
if formatText == "structured" {
isStructured = true
}
2018-11-21 13:47:46 +08:00
level, ok := log.LogLevelMapping[levelText]
2018-06-22 13:15:20 +08:00
if !ok {
2018-12-10 11:33:37 +08:00
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest)
2018-06-22 13:15:20 +08:00
return
}
var wsConn net.Conn
if r.Header.Get("Upgrade") == "websocket" {
var err error
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
if err != nil {
return
}
}
if wsConn == nil {
w.Header().Set("Content-Type", "application/json")
render.Status(r, http.StatusOK)
}
2022-04-25 23:22:58 +08:00
ch := make(chan log.Event, 1024)
2018-11-21 13:47:46 +08:00
sub := log.Subscribe()
defer log.UnSubscribe(sub)
buf := &bytes.Buffer{}
2022-04-25 23:22:58 +08:00
go func() {
for logM := range sub {
2022-04-25 23:22:58 +08:00
select {
case ch <- logM:
2022-04-25 23:22:58 +08:00
default:
}
}
close(ch)
}()
for logM := range ch {
2022-04-24 02:07:57 +08:00
if logM.LogLevel < level {
2018-06-22 13:15:20 +08:00
continue
}
2022-04-25 23:22:58 +08:00
buf.Reset()
2018-06-22 13:15:20 +08:00
if !isStructured {
if err := json.NewEncoder(buf).Encode(Log{
Type: logM.Type(),
Payload: logM.Payload,
}); err != nil {
break
}
} else {
newLevel := logM.Type()
if newLevel == "warning" {
newLevel = "warn"
}
if err := json.NewEncoder(buf).Encode(LogStructured{
Time: time.Now().Format(time.TimeOnly),
Level: newLevel,
Message: logM.Payload,
Fields: []LogStructuredField{},
}); err != nil {
break
}
2018-06-18 11:31:49 +08:00
}
2022-04-25 23:22:58 +08:00
var err error
if wsConn == nil {
_, err = w.Write(buf.Bytes())
w.(http.Flusher).Flush()
} else {
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
}
if err != nil {
break
}
2018-06-18 11:31:49 +08:00
}
}
2019-09-13 17:44:30 +08:00
func version(w http.ResponseWriter, r *http.Request) {
2021-12-03 21:54:45 +08:00
render.JSON(w, r, render.M{"meta": C.Meta, "version": C.Version})
2019-09-13 17:44:30 +08:00
}