mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-09-12 11:52:33 +00:00
when force is false, if domain in the list, will force replace when force is true, if sniff domain in the list, will skip it
400 lines
9.3 KiB
Go
400 lines
9.3 KiB
Go
package executor
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/Dreamacro/clash/adapter"
|
|
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
|
"github.com/Dreamacro/clash/component/auth"
|
|
"github.com/Dreamacro/clash/component/dialer"
|
|
G "github.com/Dreamacro/clash/component/geodata"
|
|
"github.com/Dreamacro/clash/component/iface"
|
|
"github.com/Dreamacro/clash/component/profile"
|
|
"github.com/Dreamacro/clash/component/profile/cachefile"
|
|
"github.com/Dreamacro/clash/component/resolver"
|
|
SNI "github.com/Dreamacro/clash/component/sniffer"
|
|
"github.com/Dreamacro/clash/component/trie"
|
|
"github.com/Dreamacro/clash/config"
|
|
C "github.com/Dreamacro/clash/constant"
|
|
"github.com/Dreamacro/clash/constant/provider"
|
|
"github.com/Dreamacro/clash/dns"
|
|
P "github.com/Dreamacro/clash/listener"
|
|
authStore "github.com/Dreamacro/clash/listener/auth"
|
|
"github.com/Dreamacro/clash/listener/tproxy"
|
|
"github.com/Dreamacro/clash/log"
|
|
"github.com/Dreamacro/clash/tunnel"
|
|
)
|
|
|
|
var mux sync.Mutex
|
|
|
|
func readConfig(path string) ([]byte, error) {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
return nil, fmt.Errorf("configuration file %s is empty", path)
|
|
}
|
|
|
|
return data, err
|
|
}
|
|
|
|
// Parse config with default config path
|
|
func Parse() (*config.Config, error) {
|
|
return ParseWithPath(C.Path.Config())
|
|
}
|
|
|
|
// ParseWithPath parse config with custom config path
|
|
func ParseWithPath(path string) (*config.Config, error) {
|
|
buf, err := readConfig(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ParseWithBytes(buf)
|
|
}
|
|
|
|
// ParseWithBytes config with buffer
|
|
func ParseWithBytes(buf []byte) (*config.Config, error) {
|
|
return config.Parse(buf)
|
|
}
|
|
|
|
// ApplyConfig dispatch configure to all parts
|
|
func ApplyConfig(cfg *config.Config, force bool) {
|
|
mux.Lock()
|
|
defer mux.Unlock()
|
|
|
|
updateUsers(cfg.Users)
|
|
updateProxies(cfg.Proxies, cfg.Providers)
|
|
updateRules(cfg.Rules, cfg.RuleProviders)
|
|
updateDNS(cfg.DNS, cfg.Tun)
|
|
updateGeneral(cfg.General, force)
|
|
updateIPTables(cfg)
|
|
updateSniffer(cfg.Sniffer)
|
|
updateTun(cfg.Tun, cfg.DNS)
|
|
updateExperimental(cfg)
|
|
updateHosts(cfg.Hosts)
|
|
loadProvider(cfg.RuleProviders, cfg.Providers)
|
|
updateProfile(cfg)
|
|
|
|
log.SetLevel(cfg.General.LogLevel)
|
|
}
|
|
|
|
func GetGeneral() *config.General {
|
|
ports := P.GetPorts()
|
|
var authenticator []string
|
|
if auth := authStore.Authenticator(); auth != nil {
|
|
authenticator = auth.Users()
|
|
}
|
|
|
|
general := &config.General{
|
|
Inbound: config.Inbound{
|
|
Port: ports.Port,
|
|
SocksPort: ports.SocksPort,
|
|
RedirPort: ports.RedirPort,
|
|
TProxyPort: ports.TProxyPort,
|
|
MixedPort: ports.MixedPort,
|
|
Authentication: authenticator,
|
|
AllowLan: P.AllowLan(),
|
|
BindAddress: P.BindAddress(),
|
|
},
|
|
Mode: tunnel.Mode(),
|
|
LogLevel: log.Level(),
|
|
IPv6: !resolver.DisableIPv6,
|
|
GeodataLoader: G.LoaderName(),
|
|
}
|
|
|
|
return general
|
|
}
|
|
|
|
func updateExperimental(c *config.Config) {}
|
|
|
|
func updateDNS(c *config.DNS, t *config.Tun) {
|
|
cfg := dns.Config{
|
|
Main: c.NameServer,
|
|
Fallback: c.Fallback,
|
|
IPv6: c.IPv6,
|
|
EnhancedMode: c.EnhancedMode,
|
|
Pool: c.FakeIPRange,
|
|
Hosts: c.Hosts,
|
|
FallbackFilter: dns.FallbackFilter{
|
|
GeoIP: c.FallbackFilter.GeoIP,
|
|
GeoIPCode: c.FallbackFilter.GeoIPCode,
|
|
IPCIDR: c.FallbackFilter.IPCIDR,
|
|
Domain: c.FallbackFilter.Domain,
|
|
GeoSite: c.FallbackFilter.GeoSite,
|
|
},
|
|
Default: c.DefaultNameserver,
|
|
Policy: c.NameServerPolicy,
|
|
ProxyServer: c.ProxyServerNameserver,
|
|
}
|
|
|
|
r := dns.NewResolver(cfg)
|
|
pr := dns.NewProxyServerHostResolver(r)
|
|
m := dns.NewEnhancer(cfg)
|
|
|
|
// reuse cache of old host mapper
|
|
if old := resolver.DefaultHostMapper; old != nil {
|
|
m.PatchFrom(old.(*dns.ResolverEnhancer))
|
|
}
|
|
|
|
resolver.DefaultResolver = r
|
|
resolver.DefaultHostMapper = m
|
|
|
|
if pr.HasProxyServer() {
|
|
resolver.ProxyServerHostResolver = pr
|
|
}
|
|
|
|
if t.Enable {
|
|
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
|
}
|
|
|
|
if c.Enable {
|
|
dns.ReCreateServer(c.Listen, r, m)
|
|
} else {
|
|
if !t.Enable {
|
|
resolver.DefaultResolver = nil
|
|
resolver.DefaultHostMapper = nil
|
|
resolver.DefaultLocalServer = nil
|
|
resolver.ProxyServerHostResolver = nil
|
|
}
|
|
dns.ReCreateServer("", nil, nil)
|
|
}
|
|
}
|
|
|
|
func updateHosts(tree *trie.DomainTrie[netip.Addr]) {
|
|
resolver.DefaultHosts = tree
|
|
}
|
|
|
|
func updateProxies(proxies map[string]C.Proxy, providers map[string]provider.ProxyProvider) {
|
|
tunnel.UpdateProxies(proxies, providers)
|
|
}
|
|
|
|
func updateRules(rules []C.Rule, ruleProviders map[string]*provider.RuleProvider) {
|
|
tunnel.UpdateRules(rules, ruleProviders)
|
|
}
|
|
|
|
func loadProvider(ruleProviders map[string]*provider.RuleProvider, proxyProviders map[string]provider.ProxyProvider) {
|
|
load := func(pv provider.Provider) {
|
|
if pv.VehicleType() == provider.Compatible {
|
|
log.Infoln("Start initial compatible provider %s", pv.Name())
|
|
} else {
|
|
log.Infoln("Start initial provider %s", (pv).Name())
|
|
}
|
|
|
|
if err := (pv).Initial(); err != nil {
|
|
switch pv.Type() {
|
|
case provider.Proxy:
|
|
{
|
|
log.Warnln("initial proxy provider %s error: %v", (pv).Name(), err)
|
|
}
|
|
case provider.Rule:
|
|
{
|
|
log.Warnln("initial rule provider %s error: %v", (pv).Name(), err)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, proxyProvider := range proxyProviders {
|
|
load(proxyProvider)
|
|
}
|
|
|
|
for _, ruleProvider := range ruleProviders {
|
|
load(*ruleProvider)
|
|
}
|
|
}
|
|
|
|
func updateTun(tun *config.Tun, dns *config.DNS) {
|
|
P.ReCreateTun(tun, dns, tunnel.TCPIn(), tunnel.UDPIn())
|
|
}
|
|
|
|
func updateSniffer(sniffer *config.Sniffer) {
|
|
if sniffer.Enable {
|
|
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.Force, &sniffer.Reverses)
|
|
if err != nil {
|
|
log.Warnln("initial sniffer failed, err:%v", err)
|
|
}
|
|
|
|
tunnel.UpdateSniffer(dispatcher)
|
|
log.Infoln("Sniffer is loaded and working")
|
|
} else {
|
|
dispatcher, err := SNI.NewCloseSnifferDispatcher()
|
|
if err != nil {
|
|
log.Warnln("initial sniffer failed, err:%v", err)
|
|
}
|
|
|
|
tunnel.UpdateSniffer(dispatcher)
|
|
log.Infoln("Sniffer is closed")
|
|
}
|
|
}
|
|
|
|
func updateGeneral(general *config.General, force bool) {
|
|
log.SetLevel(general.LogLevel)
|
|
tunnel.SetMode(general.Mode)
|
|
resolver.DisableIPv6 = !general.IPv6
|
|
|
|
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
|
|
|
dialer.DefaultInterface.Store(general.Interface)
|
|
|
|
if dialer.DefaultInterface.Load() != "" {
|
|
log.Infoln("Use interface name: %s", general.Interface)
|
|
}
|
|
|
|
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
|
|
if general.RoutingMark > 0 {
|
|
log.Infoln("Use routing mark: %#x", general.RoutingMark)
|
|
}
|
|
|
|
iface.FlushCache()
|
|
|
|
if !force {
|
|
return
|
|
}
|
|
|
|
geodataLoader := general.GeodataLoader
|
|
G.SetLoader(geodataLoader)
|
|
|
|
allowLan := general.AllowLan
|
|
P.SetAllowLan(allowLan)
|
|
|
|
bindAddress := general.BindAddress
|
|
P.SetBindAddress(bindAddress)
|
|
|
|
tcpIn := tunnel.TCPIn()
|
|
udpIn := tunnel.UDPIn()
|
|
|
|
P.ReCreateHTTP(general.Port, tcpIn)
|
|
P.ReCreateSocks(general.SocksPort, tcpIn, udpIn)
|
|
P.ReCreateRedir(general.RedirPort, tcpIn, udpIn)
|
|
P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn)
|
|
P.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
|
|
}
|
|
|
|
func updateUsers(users []auth.AuthUser) {
|
|
authenticator := auth.NewAuthenticator(users)
|
|
authStore.SetAuthenticator(authenticator)
|
|
if authenticator != nil {
|
|
log.Infoln("Authentication of local server updated")
|
|
}
|
|
}
|
|
|
|
func updateProfile(cfg *config.Config) {
|
|
profileCfg := cfg.Profile
|
|
|
|
profile.StoreSelected.Store(profileCfg.StoreSelected)
|
|
if profileCfg.StoreSelected {
|
|
patchSelectGroup(cfg.Proxies)
|
|
}
|
|
}
|
|
|
|
func patchSelectGroup(proxies map[string]C.Proxy) {
|
|
mapping := cachefile.Cache().SelectedMap()
|
|
if mapping == nil {
|
|
return
|
|
}
|
|
|
|
for name, proxy := range proxies {
|
|
outbound, ok := proxy.(*adapter.Proxy)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
selector, ok := outbound.ProxyAdapter.(*outboundgroup.Selector)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
selected, exist := mapping[name]
|
|
if !exist {
|
|
continue
|
|
}
|
|
|
|
selector.Set(selected)
|
|
}
|
|
}
|
|
|
|
func updateIPTables(cfg *config.Config) {
|
|
tproxy.CleanupTProxyIPTables()
|
|
|
|
iptables := cfg.IPTables
|
|
if runtime.GOOS != "linux" || !iptables.Enable {
|
|
return
|
|
}
|
|
|
|
var err error
|
|
defer func() {
|
|
if err != nil {
|
|
log.Errorln("[IPTABLES] setting iptables failed: %s", err.Error())
|
|
os.Exit(2)
|
|
}
|
|
}()
|
|
|
|
if cfg.Tun.Enable {
|
|
err = fmt.Errorf("when tun is enabled, iptables cannot be set automatically")
|
|
return
|
|
}
|
|
|
|
var (
|
|
inboundInterface = "lo"
|
|
bypass = iptables.Bypass
|
|
tProxyPort = cfg.General.TProxyPort
|
|
dnsCfg = cfg.DNS
|
|
)
|
|
|
|
if tProxyPort == 0 {
|
|
err = fmt.Errorf("tproxy-port must be greater than zero")
|
|
return
|
|
}
|
|
|
|
if !dnsCfg.Enable {
|
|
err = fmt.Errorf("DNS server must be enable")
|
|
return
|
|
}
|
|
|
|
_, dnsPortStr, err := net.SplitHostPort(dnsCfg.Listen)
|
|
if err != nil {
|
|
err = fmt.Errorf("DNS server must be correct")
|
|
return
|
|
}
|
|
|
|
dnsPort, err := strconv.ParseUint(dnsPortStr, 10, 16)
|
|
if err != nil {
|
|
err = fmt.Errorf("DNS server must be correct")
|
|
return
|
|
}
|
|
|
|
if iptables.InboundInterface != "" {
|
|
inboundInterface = iptables.InboundInterface
|
|
}
|
|
|
|
if dialer.DefaultRoutingMark.Load() == 0 {
|
|
dialer.DefaultRoutingMark.Store(2158)
|
|
}
|
|
|
|
err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), uint16(dnsPort))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
log.Infoln("[IPTABLES] Setting iptables completed")
|
|
}
|
|
|
|
func Shutdown() {
|
|
P.Cleanup()
|
|
tproxy.CleanupTProxyIPTables()
|
|
|
|
log.Warnln("Clash shutting down")
|
|
}
|