github.com/metacubex/mihomo@v1.18.5/hub/route/configs.go (about)

     1  package route
     2  
     3  import (
     4  	"net/http"
     5  	"net/netip"
     6  	"path/filepath"
     7  
     8  	"github.com/metacubex/mihomo/adapter/inbound"
     9  	"github.com/metacubex/mihomo/component/dialer"
    10  	"github.com/metacubex/mihomo/component/resolver"
    11  	"github.com/metacubex/mihomo/component/updater"
    12  	"github.com/metacubex/mihomo/config"
    13  	C "github.com/metacubex/mihomo/constant"
    14  	"github.com/metacubex/mihomo/hub/executor"
    15  	P "github.com/metacubex/mihomo/listener"
    16  	LC "github.com/metacubex/mihomo/listener/config"
    17  	"github.com/metacubex/mihomo/log"
    18  	"github.com/metacubex/mihomo/tunnel"
    19  
    20  	"github.com/go-chi/chi/v5"
    21  	"github.com/go-chi/render"
    22  )
    23  
    24  func configRouter() http.Handler {
    25  	r := chi.NewRouter()
    26  	r.Get("/", getConfigs)
    27  	r.Put("/", updateConfigs)
    28  	r.Post("/geo", updateGeoDatabases)
    29  	r.Patch("/", patchConfigs)
    30  	return r
    31  }
    32  
    33  type configSchema struct {
    34  	Port              *int               `json:"port"`
    35  	SocksPort         *int               `json:"socks-port"`
    36  	RedirPort         *int               `json:"redir-port"`
    37  	TProxyPort        *int               `json:"tproxy-port"`
    38  	MixedPort         *int               `json:"mixed-port"`
    39  	Tun               *tunSchema         `json:"tun"`
    40  	TuicServer        *tuicServerSchema  `json:"tuic-server"`
    41  	ShadowSocksConfig *string            `json:"ss-config"`
    42  	VmessConfig       *string            `json:"vmess-config"`
    43  	TcptunConfig      *string            `json:"tcptun-config"`
    44  	UdptunConfig      *string            `json:"udptun-config"`
    45  	AllowLan          *bool              `json:"allow-lan"`
    46  	SkipAuthPrefixes  *[]netip.Prefix    `json:"skip-auth-prefixes"`
    47  	LanAllowedIPs     *[]netip.Prefix    `json:"lan-allowed-ips"`
    48  	LanDisAllowedIPs  *[]netip.Prefix    `json:"lan-disallowed-ips"`
    49  	BindAddress       *string            `json:"bind-address"`
    50  	Mode              *tunnel.TunnelMode `json:"mode"`
    51  	LogLevel          *log.LogLevel      `json:"log-level"`
    52  	IPv6              *bool              `json:"ipv6"`
    53  	Sniffing          *bool              `json:"sniffing"`
    54  	TcpConcurrent     *bool              `json:"tcp-concurrent"`
    55  	InterfaceName     *string            `json:"interface-name"`
    56  }
    57  
    58  type tunSchema struct {
    59  	Enable              bool        `yaml:"enable" json:"enable"`
    60  	Device              *string     `yaml:"device" json:"device"`
    61  	Stack               *C.TUNStack `yaml:"stack" json:"stack"`
    62  	DNSHijack           *[]string   `yaml:"dns-hijack" json:"dns-hijack"`
    63  	AutoRoute           *bool       `yaml:"auto-route" json:"auto-route"`
    64  	AutoDetectInterface *bool       `yaml:"auto-detect-interface" json:"auto-detect-interface"`
    65  	//RedirectToTun       []string   		  `yaml:"-" json:"-"`
    66  
    67  	MTU        *uint32 `yaml:"mtu" json:"mtu,omitempty"`
    68  	GSO        *bool   `yaml:"gso" json:"gso,omitempty"`
    69  	GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
    70  	//Inet4Address           *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
    71  	Inet6Address             *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
    72  	StrictRoute              *bool           `yaml:"strict-route" json:"strict-route,omitempty"`
    73  	Inet4RouteAddress        *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
    74  	Inet6RouteAddress        *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
    75  	Inet4RouteExcludeAddress *[]netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"`
    76  	Inet6RouteExcludeAddress *[]netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"`
    77  	IncludeInterface         *[]string       `yaml:"include-interface" json:"include-interface,omitempty"`
    78  	ExcludeInterface         *[]string       `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
    79  	IncludeUID               *[]uint32       `yaml:"include-uid" json:"include-uid,omitempty"`
    80  	IncludeUIDRange          *[]string       `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
    81  	ExcludeUID               *[]uint32       `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
    82  	ExcludeUIDRange          *[]string       `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
    83  	IncludeAndroidUser       *[]int          `yaml:"include-android-user" json:"include-android-user,omitempty"`
    84  	IncludePackage           *[]string       `yaml:"include-package" json:"include-package,omitempty"`
    85  	ExcludePackage           *[]string       `yaml:"exclude-package" json:"exclude-package,omitempty"`
    86  	EndpointIndependentNat   *bool           `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
    87  	UDPTimeout               *int64          `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
    88  	FileDescriptor           *int            `yaml:"file-descriptor" json:"file-descriptor"`
    89  	TableIndex               *int            `yaml:"table-index" json:"table-index"`
    90  }
    91  
    92  type tuicServerSchema struct {
    93  	Enable                bool               `yaml:"enable" json:"enable"`
    94  	Listen                *string            `yaml:"listen" json:"listen"`
    95  	Token                 *[]string          `yaml:"token" json:"token"`
    96  	Users                 *map[string]string `yaml:"users" json:"users,omitempty"`
    97  	Certificate           *string            `yaml:"certificate" json:"certificate"`
    98  	PrivateKey            *string            `yaml:"private-key" json:"private-key"`
    99  	CongestionController  *string            `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
   100  	MaxIdleTime           *int               `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
   101  	AuthenticationTimeout *int               `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
   102  	ALPN                  *[]string          `yaml:"alpn" json:"alpn,omitempty"`
   103  	MaxUdpRelayPacketSize *int               `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
   104  	CWND                  *int               `yaml:"cwnd" json:"cwnd,omitempty"`
   105  }
   106  
   107  func getConfigs(w http.ResponseWriter, r *http.Request) {
   108  	general := executor.GetGeneral()
   109  	render.JSON(w, r, general)
   110  }
   111  
   112  func pointerOrDefault(p *int, def int) int {
   113  	if p != nil {
   114  		return *p
   115  	}
   116  	return def
   117  }
   118  
   119  func pointerOrDefaultString(p *string, def string) string {
   120  	if p != nil {
   121  		return *p
   122  	}
   123  
   124  	return def
   125  }
   126  
   127  func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
   128  	if p != nil {
   129  		def.Enable = p.Enable
   130  		if p.Device != nil {
   131  			def.Device = *p.Device
   132  		}
   133  		if p.Stack != nil {
   134  			def.Stack = *p.Stack
   135  		}
   136  		if p.DNSHijack != nil {
   137  			def.DNSHijack = *p.DNSHijack
   138  		}
   139  		if p.AutoRoute != nil {
   140  			def.AutoRoute = *p.AutoRoute
   141  		}
   142  		if p.AutoDetectInterface != nil {
   143  			def.AutoDetectInterface = *p.AutoDetectInterface
   144  		}
   145  		if p.MTU != nil {
   146  			def.MTU = *p.MTU
   147  		}
   148  		if p.GSO != nil {
   149  			def.GSO = *p.GSO
   150  		}
   151  		if p.GSOMaxSize != nil {
   152  			def.GSOMaxSize = *p.GSOMaxSize
   153  		}
   154  		//if p.Inet4Address != nil {
   155  		//	def.Inet4Address = *p.Inet4Address
   156  		//}
   157  		if p.Inet6Address != nil {
   158  			def.Inet6Address = *p.Inet6Address
   159  		}
   160  		if p.Inet4RouteAddress != nil {
   161  			def.Inet4RouteAddress = *p.Inet4RouteAddress
   162  		}
   163  		if p.Inet6RouteAddress != nil {
   164  			def.Inet6RouteAddress = *p.Inet6RouteAddress
   165  		}
   166  		if p.Inet4RouteExcludeAddress != nil {
   167  			def.Inet4RouteExcludeAddress = *p.Inet4RouteExcludeAddress
   168  		}
   169  		if p.Inet6RouteExcludeAddress != nil {
   170  			def.Inet6RouteExcludeAddress = *p.Inet6RouteExcludeAddress
   171  		}
   172  		if p.IncludeInterface != nil {
   173  			def.IncludeInterface = *p.IncludeInterface
   174  		}
   175  		if p.ExcludeInterface != nil {
   176  			def.ExcludeInterface = *p.ExcludeInterface
   177  		}
   178  		if p.IncludeUID != nil {
   179  			def.IncludeUID = *p.IncludeUID
   180  		}
   181  		if p.IncludeUIDRange != nil {
   182  			def.IncludeUIDRange = *p.IncludeUIDRange
   183  		}
   184  		if p.ExcludeUID != nil {
   185  			def.ExcludeUID = *p.ExcludeUID
   186  		}
   187  		if p.ExcludeUIDRange != nil {
   188  			def.ExcludeUIDRange = *p.ExcludeUIDRange
   189  		}
   190  		if p.IncludeAndroidUser != nil {
   191  			def.IncludeAndroidUser = *p.IncludeAndroidUser
   192  		}
   193  		if p.IncludePackage != nil {
   194  			def.IncludePackage = *p.IncludePackage
   195  		}
   196  		if p.ExcludePackage != nil {
   197  			def.ExcludePackage = *p.ExcludePackage
   198  		}
   199  		if p.EndpointIndependentNat != nil {
   200  			def.EndpointIndependentNat = *p.EndpointIndependentNat
   201  		}
   202  		if p.UDPTimeout != nil {
   203  			def.UDPTimeout = *p.UDPTimeout
   204  		}
   205  		if p.FileDescriptor != nil {
   206  			def.FileDescriptor = *p.FileDescriptor
   207  		}
   208  		if p.TableIndex != nil {
   209  			def.TableIndex = *p.TableIndex
   210  		}
   211  	}
   212  	return def
   213  }
   214  
   215  func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicServer {
   216  	if p != nil {
   217  		def.Enable = p.Enable
   218  		if p.Listen != nil {
   219  			def.Listen = *p.Listen
   220  		}
   221  		if p.Token != nil {
   222  			def.Token = *p.Token
   223  		}
   224  		if p.Users != nil {
   225  			def.Users = *p.Users
   226  		}
   227  		if p.Certificate != nil {
   228  			def.Certificate = *p.Certificate
   229  		}
   230  		if p.PrivateKey != nil {
   231  			def.PrivateKey = *p.PrivateKey
   232  		}
   233  		if p.CongestionController != nil {
   234  			def.CongestionController = *p.CongestionController
   235  		}
   236  		if p.MaxIdleTime != nil {
   237  			def.MaxIdleTime = *p.MaxIdleTime
   238  		}
   239  		if p.AuthenticationTimeout != nil {
   240  			def.AuthenticationTimeout = *p.AuthenticationTimeout
   241  		}
   242  		if p.ALPN != nil {
   243  			def.ALPN = *p.ALPN
   244  		}
   245  		if p.MaxUdpRelayPacketSize != nil {
   246  			def.MaxUdpRelayPacketSize = *p.MaxUdpRelayPacketSize
   247  		}
   248  		if p.CWND != nil {
   249  			def.CWND = *p.CWND
   250  		}
   251  	}
   252  	return def
   253  }
   254  
   255  func patchConfigs(w http.ResponseWriter, r *http.Request) {
   256  	general := &configSchema{}
   257  	if err := render.DecodeJSON(r.Body, &general); err != nil {
   258  		render.Status(r, http.StatusBadRequest)
   259  		render.JSON(w, r, ErrBadRequest)
   260  		return
   261  	}
   262  
   263  	if general.AllowLan != nil {
   264  		P.SetAllowLan(*general.AllowLan)
   265  	}
   266  
   267  	if general.SkipAuthPrefixes != nil {
   268  		inbound.SetSkipAuthPrefixes(*general.SkipAuthPrefixes)
   269  	}
   270  
   271  	if general.LanAllowedIPs != nil {
   272  		inbound.SetAllowedIPs(*general.LanAllowedIPs)
   273  	}
   274  
   275  	if general.LanDisAllowedIPs != nil {
   276  		inbound.SetDisAllowedIPs(*general.LanDisAllowedIPs)
   277  	}
   278  
   279  	if general.BindAddress != nil {
   280  		P.SetBindAddress(*general.BindAddress)
   281  	}
   282  
   283  	if general.Sniffing != nil {
   284  		tunnel.SetSniffing(*general.Sniffing)
   285  	}
   286  
   287  	if general.TcpConcurrent != nil {
   288  		dialer.SetTcpConcurrent(*general.TcpConcurrent)
   289  	}
   290  
   291  	if general.InterfaceName != nil {
   292  		dialer.DefaultInterface.Store(*general.InterfaceName)
   293  	}
   294  
   295  	ports := P.GetPorts()
   296  
   297  	P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tunnel.Tunnel)
   298  	P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tunnel.Tunnel)
   299  	P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tunnel.Tunnel)
   300  	P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tunnel.Tunnel)
   301  	P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tunnel.Tunnel)
   302  	P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tunnel.Tunnel)
   303  	P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel)
   304  	P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel)
   305  	P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tunnel.Tunnel)
   306  
   307  	if general.Mode != nil {
   308  		tunnel.SetMode(*general.Mode)
   309  	}
   310  
   311  	if general.LogLevel != nil {
   312  		log.SetLevel(*general.LogLevel)
   313  	}
   314  
   315  	if general.IPv6 != nil {
   316  		resolver.DisableIPv6 = !*general.IPv6
   317  	}
   318  
   319  	render.NoContent(w, r)
   320  }
   321  
   322  func updateConfigs(w http.ResponseWriter, r *http.Request) {
   323  	req := struct {
   324  		Path    string `json:"path"`
   325  		Payload string `json:"payload"`
   326  	}{}
   327  	if err := render.DecodeJSON(r.Body, &req); err != nil {
   328  		render.Status(r, http.StatusBadRequest)
   329  		render.JSON(w, r, ErrBadRequest)
   330  		return
   331  	}
   332  
   333  	force := r.URL.Query().Get("force") == "true"
   334  	var cfg *config.Config
   335  	var err error
   336  
   337  	if req.Payload != "" {
   338  		cfg, err = executor.ParseWithBytes([]byte(req.Payload))
   339  		if err != nil {
   340  			render.Status(r, http.StatusBadRequest)
   341  			render.JSON(w, r, newError(err.Error()))
   342  			return
   343  		}
   344  	} else {
   345  		if req.Path == "" {
   346  			req.Path = C.Path.Config()
   347  		}
   348  		if !filepath.IsAbs(req.Path) {
   349  			render.Status(r, http.StatusBadRequest)
   350  			render.JSON(w, r, newError("path is not a absolute path"))
   351  			return
   352  		}
   353  
   354  		cfg, err = executor.ParseWithPath(req.Path)
   355  		if err != nil {
   356  			render.Status(r, http.StatusBadRequest)
   357  			render.JSON(w, r, newError(err.Error()))
   358  			return
   359  		}
   360  	}
   361  
   362  	executor.ApplyConfig(cfg, force)
   363  	render.NoContent(w, r)
   364  }
   365  
   366  func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
   367  	if updater.UpdatingGeo.Load() {
   368  		render.Status(r, http.StatusBadRequest)
   369  		render.JSON(w, r, newError("updating..."))
   370  		return
   371  	}
   372  
   373  	err := updater.UpdateGeoDatabases()
   374  	if err != nil {
   375  		render.Status(r, http.StatusBadRequest)
   376  		render.JSON(w, r, newError(err.Error()))
   377  		return
   378  	}
   379  
   380  	go func() {
   381  		cfg, err := executor.ParseWithPath(C.Path.Config())
   382  		if err != nil {
   383  			log.Errorln("[REST-API] update GEO databases failed: %v", err)
   384  			return
   385  		}
   386  
   387  		log.Warnln("[REST-API] update GEO databases successful, apply config...")
   388  
   389  		executor.ApplyConfig(cfg, false)
   390  	}()
   391  
   392  	render.NoContent(w, r)
   393  }