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 }