clash/config/config.go

588 lines
15 KiB
Go
Raw Normal View History

package config
import (
"errors"
"fmt"
2018-12-05 13:13:29 +00:00
"net"
"net/url"
"os"
"strings"
2019-12-08 04:17:24 +00:00
"github.com/Dreamacro/clash/adapters/outbound"
"github.com/Dreamacro/clash/adapters/outboundgroup"
"github.com/Dreamacro/clash/adapters/provider"
"github.com/Dreamacro/clash/component/auth"
2019-05-02 16:05:14 +00:00
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
2018-12-05 13:13:29 +00:00
"github.com/Dreamacro/clash/dns"
2018-11-21 05:47:46 +00:00
"github.com/Dreamacro/clash/log"
R "github.com/Dreamacro/clash/rules"
2018-11-21 05:47:46 +00:00
T "github.com/Dreamacro/clash/tunnel"
yaml "gopkg.in/yaml.v2"
)
2018-08-11 18:23:46 +00:00
// General config
type General struct {
2020-06-18 10:11:02 +00:00
Inbound
Controller
Mode T.TunnelMode `json:"mode"`
LogLevel log.LogLevel `json:"log-level"`
IPv6 bool `json:"ipv6"`
Interface string `json:"interface-name"`
2020-06-18 10:11:02 +00:00
}
// Inbound
type Inbound struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
2020-06-18 10:11:02 +00:00
MixedPort int `json:"mixed-port"`
Authentication []string `json:"authentication"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
}
// Controller
type Controller struct {
ExternalController string `json:"-"`
ExternalUI string `json:"-"`
Secret string `json:"-"`
2018-08-11 18:23:46 +00:00
}
2018-12-05 13:13:29 +00:00
// DNS config
type DNS struct {
Enable bool `yaml:"enable"`
IPv6 bool `yaml:"ipv6"`
NameServer []dns.NameServer `yaml:"nameserver"`
Fallback []dns.NameServer `yaml:"fallback"`
FallbackFilter FallbackFilter `yaml:"fallback-filter"`
Listen string `yaml:"listen"`
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
FakeIPRange *fakeip.Pool
Hosts *trie.DomainTrie
2019-09-15 05:36:45 +00:00
}
// FallbackFilter config
type FallbackFilter struct {
GeoIP bool `yaml:"geoip"`
IPCIDR []*net.IPNet `yaml:"ipcidr"`
Domain []string `yaml:"domain"`
}
// Experimental config
type Experimental struct{}
// Config is clash config manager
type Config struct {
General *General
DNS *DNS
Experimental *Experimental
Hosts *trie.DomainTrie
Rules []C.Rule
Users []auth.AuthUser
Proxies map[string]C.Proxy
2019-12-08 04:17:24 +00:00
Providers map[string]provider.ProxyProvider
}
2020-01-10 16:22:34 +00:00
type RawDNS struct {
Enable bool `yaml:"enable"`
IPv6 bool `yaml:"ipv6"`
UseHosts bool `yaml:"use-hosts"`
NameServer []string `yaml:"nameserver"`
Fallback []string `yaml:"fallback"`
FallbackFilter RawFallbackFilter `yaml:"fallback-filter"`
Listen string `yaml:"listen"`
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
FakeIPRange string `yaml:"fake-ip-range"`
FakeIPFilter []string `yaml:"fake-ip-filter"`
DefaultNameserver []string `yaml:"default-nameserver"`
2019-09-15 05:36:45 +00:00
}
2020-01-10 16:22:34 +00:00
type RawFallbackFilter struct {
2019-09-15 05:36:45 +00:00
GeoIP bool `yaml:"geoip"`
IPCIDR []string `yaml:"ipcidr"`
Domain []string `yaml:"domain"`
2018-12-05 13:13:29 +00:00
}
2020-01-10 16:22:34 +00:00
type RawConfig struct {
2018-12-05 13:13:29 +00:00
Port int `yaml:"port"`
SocksPort int `yaml:"socks-port"`
RedirPort int `yaml:"redir-port"`
TProxyPort int `yaml:"tproxy-port"`
MixedPort int `yaml:"mixed-port"`
Authentication []string `yaml:"authentication"`
2018-12-05 13:13:29 +00:00
AllowLan bool `yaml:"allow-lan"`
BindAddress string `yaml:"bind-address"`
Mode T.TunnelMode `yaml:"mode"`
2018-12-05 13:13:29 +00:00
LogLevel log.LogLevel `yaml:"log-level"`
2020-06-18 10:11:02 +00:00
IPv6 bool `yaml:"ipv6"`
2018-12-05 13:13:29 +00:00
ExternalController string `yaml:"external-controller"`
2018-12-19 17:29:13 +00:00
ExternalUI string `yaml:"external-ui"`
2018-12-05 13:13:29 +00:00
Secret string `yaml:"secret"`
Interface string `yaml:"interface-name"`
2018-12-05 13:13:29 +00:00
2020-03-07 12:01:24 +00:00
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"`
2019-12-08 04:17:24 +00:00
Hosts map[string]string `yaml:"hosts"`
2020-01-10 16:22:34 +00:00
DNS RawDNS `yaml:"dns"`
2019-12-08 04:17:24 +00:00
Experimental Experimental `yaml:"experimental"`
2020-03-07 12:01:24 +00:00
Proxy []map[string]interface{} `yaml:"proxies"`
ProxyGroup []map[string]interface{} `yaml:"proxy-groups"`
Rule []string `yaml:"rules"`
2018-12-05 13:13:29 +00:00
}
// Parse config
func Parse(buf []byte) (*Config, error) {
2020-01-10 16:22:34 +00:00
rawCfg, err := UnmarshalRawConfig(buf)
if err != nil {
return nil, err
}
return ParseRawConfig(rawCfg)
}
2018-10-14 13:22:58 +00:00
2020-01-10 16:22:34 +00:00
func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
// config with some default value
2020-01-10 16:22:34 +00:00
rawCfg := &RawConfig{
AllowLan: false,
BindAddress: "*",
Mode: T.Rule,
Authentication: []string{},
LogLevel: log.INFO,
2019-09-11 09:00:55 +00:00
Hosts: map[string]string{},
Rule: []string{},
Proxy: []map[string]interface{}{},
ProxyGroup: []map[string]interface{}{},
2020-01-10 16:22:34 +00:00
DNS: RawDNS{
2019-05-02 16:05:14 +00:00
Enable: false,
UseHosts: true,
2019-05-02 16:05:14 +00:00
FakeIPRange: "198.18.0.1/16",
2020-01-10 16:22:34 +00:00
FallbackFilter: RawFallbackFilter{
2019-09-15 05:36:45 +00:00
GeoIP: true,
IPCIDR: []string{},
},
DefaultNameserver: []string{
"114.114.114.114",
"8.8.8.8",
},
2018-12-05 13:52:31 +00:00
},
}
2020-01-10 16:22:34 +00:00
if err := yaml.Unmarshal(buf, &rawCfg); err != nil {
2018-11-21 05:47:46 +00:00
return nil, err
}
2020-01-10 16:22:34 +00:00
return rawCfg, nil
}
func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
config := &Config{}
config.Experimental = &rawCfg.Experimental
2018-11-21 05:47:46 +00:00
general, err := parseGeneral(rawCfg)
if err != nil {
return nil, err
}
2018-11-21 05:47:46 +00:00
config.General = general
2019-12-08 04:17:24 +00:00
proxies, providers, err := parseProxies(rawCfg)
2018-11-21 05:47:46 +00:00
if err != nil {
return nil, err
}
2018-11-21 05:47:46 +00:00
config.Proxies = proxies
2019-12-08 04:17:24 +00:00
config.Providers = providers
rules, err := parseRules(rawCfg, proxies)
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, err
}
2018-11-21 05:47:46 +00:00
config.Rules = rules
hosts, err := parseHosts(rawCfg)
2018-12-05 13:13:29 +00:00
if err != nil {
return nil, err
}
config.Hosts = hosts
2018-12-05 13:13:29 +00:00
dnsCfg, err := parseDNS(rawCfg.DNS, hosts)
2019-09-11 09:00:55 +00:00
if err != nil {
return nil, err
}
config.DNS = dnsCfg
2019-09-11 09:00:55 +00:00
config.Users = parseAuthentication(rawCfg.Authentication)
2020-01-10 16:22:34 +00:00
2018-11-21 05:47:46 +00:00
return config, nil
}
2020-01-10 16:22:34 +00:00
func parseGeneral(cfg *RawConfig) (*General, error) {
2018-12-19 17:29:13 +00:00
externalUI := cfg.ExternalUI
2020-06-18 10:11:02 +00:00
// checkout externalUI exist
2018-12-21 02:55:21 +00:00
if externalUI != "" {
2020-01-30 09:03:11 +00:00
externalUI = C.Path.Resolve(externalUI)
2018-12-21 02:55:21 +00:00
if _, err := os.Stat(externalUI); os.IsNotExist(err) {
return nil, fmt.Errorf("external-ui: %s not exist", externalUI)
}
2018-12-19 17:29:13 +00:00
}
2020-06-18 10:11:02 +00:00
return &General{
Inbound: Inbound{
Port: cfg.Port,
SocksPort: cfg.SocksPort,
RedirPort: cfg.RedirPort,
TProxyPort: cfg.TProxyPort,
2020-06-18 10:11:02 +00:00
MixedPort: cfg.MixedPort,
AllowLan: cfg.AllowLan,
BindAddress: cfg.BindAddress,
},
Controller: Controller{
ExternalController: cfg.ExternalController,
ExternalUI: cfg.ExternalUI,
Secret: cfg.Secret,
},
Mode: cfg.Mode,
LogLevel: cfg.LogLevel,
IPv6: cfg.IPv6,
Interface: cfg.Interface,
2020-06-18 10:11:02 +00:00
}, nil
}
2020-01-10 16:22:34 +00:00
func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]provider.ProxyProvider, err error) {
2019-12-08 04:17:24 +00:00
proxies = make(map[string]C.Proxy)
providersMap = make(map[string]provider.ProxyProvider)
proxyList := []string{}
proxiesConfig := cfg.Proxy
groupsConfig := cfg.ProxyGroup
2019-12-08 04:17:24 +00:00
providersConfig := cfg.ProxyProvider
2019-12-08 04:17:24 +00:00
proxies["DIRECT"] = outbound.NewProxy(outbound.NewDirect())
proxies["REJECT"] = outbound.NewProxy(outbound.NewReject())
proxyList = append(proxyList, "DIRECT", "REJECT")
// parse proxy
for idx, mapping := range proxiesConfig {
2019-12-08 04:17:24 +00:00
proxy, err := outbound.ParseProxy(mapping)
if err != nil {
2020-08-25 14:19:59 +00:00
return nil, nil, fmt.Errorf("proxy %d: %w", idx, err)
2018-10-27 04:57:56 +00:00
}
if _, exist := proxies[proxy.Name()]; exist {
2020-08-25 14:19:59 +00:00
return nil, nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name())
}
2019-12-08 04:17:24 +00:00
proxies[proxy.Name()] = proxy
proxyList = append(proxyList, proxy.Name())
}
2020-04-08 07:49:12 +00:00
// keep the original order of ProxyGroups in config file
for idx, mapping := range groupsConfig {
groupName, existName := mapping["name"].(string)
if !existName {
2020-08-25 14:19:59 +00:00
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
}
proxyList = append(proxyList, groupName)
}
// check if any loop exists and sort the ProxyGroups
2019-12-08 04:17:24 +00:00
if err := proxyGroupsDagSort(groupsConfig); err != nil {
return nil, nil, err
}
2019-12-08 04:17:24 +00:00
// parse and initial providers
for name, mapping := range providersConfig {
if name == provider.ReservedName {
return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName)
}
2019-12-08 04:17:24 +00:00
pd, err := provider.ParseProxyProvider(name, mapping)
if err != nil {
2020-05-31 16:39:41 +00:00
return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err)
}
2019-12-08 04:17:24 +00:00
providersMap[name] = pd
}
2019-02-15 06:25:20 +00:00
2019-12-08 04:17:24 +00:00
for _, provider := range providersMap {
log.Infoln("Start initial provider %s", provider.Name())
if err := provider.Initial(); err != nil {
2020-05-31 16:39:41 +00:00
return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", provider.Name(), err)
}
2019-12-08 04:17:24 +00:00
}
// parse proxy group
for idx, mapping := range groupsConfig {
group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap)
if err != nil {
2020-08-25 14:19:59 +00:00
return nil, nil, fmt.Errorf("proxy group[%d]: %w", idx, err)
}
2019-12-08 04:17:24 +00:00
groupName := group.Name()
if _, exist := proxies[groupName]; exist {
2020-08-25 14:19:59 +00:00
return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName)
2019-12-08 04:17:24 +00:00
}
proxies[groupName] = outbound.NewProxy(group)
}
2020-01-11 13:02:55 +00:00
// initial compatible provider
for _, pd := range providersMap {
if pd.VehicleType() != provider.Compatible {
continue
}
log.Infoln("Start initial compatible provider %s", pd.Name())
if err := pd.Initial(); err != nil {
return nil, nil, err
}
}
2019-02-15 06:25:20 +00:00
ps := []C.Proxy{}
for _, v := range proxyList {
ps = append(ps, proxies[v])
}
2019-12-26 10:45:18 +00:00
hc := provider.NewHealthCheck(ps, "", 0)
2020-01-11 13:02:55 +00:00
pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
2019-12-08 04:17:24 +00:00
providersMap[provider.ReservedName] = pd
2019-12-08 04:17:24 +00:00
global := outboundgroup.NewSelector("GLOBAL", []provider.ProxyProvider{pd})
proxies["GLOBAL"] = outbound.NewProxy(global)
return proxies, providersMap, nil
}
2020-01-10 16:22:34 +00:00
func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
rules := []C.Rule{}
rulesConfig := cfg.Rule
2020-03-07 12:01:24 +00:00
// parse rules
for idx, line := range rulesConfig {
rule := trimArr(strings.Split(line, ","))
var (
payload string
target string
params = []string{}
)
switch l := len(rule); {
case l == 2:
target = rule[1]
case l == 3:
payload = rule[1]
target = rule[2]
case l >= 4:
payload = rule[1]
target = rule[2]
params = rule[3:]
default:
2020-08-25 14:19:59 +00:00
return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
}
if _, ok := proxies[target]; !ok {
2020-08-25 14:19:59 +00:00
return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target)
}
rule = trimArr(rule)
params = trimArr(params)
2019-03-30 06:11:59 +00:00
2020-06-05 09:43:50 +00:00
parsed, parseErr := R.ParseRule(rule[0], payload, target, params)
if parseErr != nil {
2020-07-19 05:17:05 +00:00
if parseErr == R.ErrPlatformNotSupport {
log.Warnln("Rules[%d] [%s] don't support current OS, skip", idx, line)
continue
}
2020-08-25 14:19:59 +00:00
return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error())
2019-03-30 06:11:59 +00:00
}
rules = append(rules, parsed)
}
2018-11-21 05:47:46 +00:00
return rules, nil
}
2018-12-05 13:13:29 +00:00
func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) {
2019-09-11 09:00:55 +00:00
tree := trie.New()
2020-06-07 09:25:51 +00:00
// add default hosts
if err := tree.Insert("localhost", net.IP{127, 0, 0, 1}); err != nil {
2020-09-20 07:53:27 +00:00
log.Errorln("insert localhost to host error: %s", err.Error())
2020-06-07 09:25:51 +00:00
}
2019-09-11 09:00:55 +00:00
if len(cfg.Hosts) != 0 {
for domain, ipStr := range cfg.Hosts {
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("%s is not a valid IP", ipStr)
}
tree.Insert(domain, ip)
}
}
return tree, nil
}
2020-02-17 14:13:15 +00:00
func hostWithDefaultPort(host string, defPort string) (string, error) {
2018-12-05 13:13:29 +00:00
if !strings.Contains(host, ":") {
host += ":"
}
hostname, port, err := net.SplitHostPort(host)
if err != nil {
2020-02-17 14:13:15 +00:00
return "", err
2018-12-05 13:13:29 +00:00
}
if port == "" {
port = defPort
}
2020-02-17 14:13:15 +00:00
return net.JoinHostPort(hostname, port), nil
2018-12-05 13:13:29 +00:00
}
func parseNameServer(servers []string) ([]dns.NameServer, error) {
nameservers := []dns.NameServer{}
for idx, server := range servers {
// parse without scheme .e.g 8.8.8.8:53
if !strings.Contains(server, "://") {
server = "udp://" + server
2018-12-05 13:13:29 +00:00
}
u, err := url.Parse(server)
if err != nil {
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
}
2020-02-17 14:13:15 +00:00
var addr, dnsNetType string
switch u.Scheme {
case "udp":
2020-02-17 14:13:15 +00:00
addr, err = hostWithDefaultPort(u.Host, "53")
dnsNetType = "" // UDP
case "tcp":
2020-02-17 14:13:15 +00:00
addr, err = hostWithDefaultPort(u.Host, "53")
dnsNetType = "tcp" // TCP
case "tls":
2020-02-17 14:13:15 +00:00
addr, err = hostWithDefaultPort(u.Host, "853")
dnsNetType = "tcp-tls" // DNS over TLS
2019-06-28 04:29:08 +00:00
case "https":
clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path}
addr = clearURL.String()
2019-06-28 04:29:08 +00:00
dnsNetType = "https" // DNS over HTTPS
default:
2018-12-05 13:13:29 +00:00
return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
}
2019-06-28 04:29:08 +00:00
if err != nil {
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
}
2018-12-05 13:13:29 +00:00
nameservers = append(
nameservers,
dns.NameServer{
Net: dnsNetType,
Addr: addr,
2018-12-05 13:13:29 +00:00
},
)
}
return nameservers, nil
}
2019-09-15 05:36:45 +00:00
func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) {
ipNets := []*net.IPNet{}
for idx, ip := range ips {
_, ipnet, err := net.ParseCIDR(ip)
if err != nil {
return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
}
ipNets = append(ipNets, ipnet)
}
return ipNets, nil
}
func parseDNS(cfg RawDNS, hosts *trie.DomainTrie) (*DNS, error) {
2018-12-05 13:13:29 +00:00
if cfg.Enable && len(cfg.NameServer) == 0 {
2020-08-25 14:19:59 +00:00
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
2018-12-05 13:13:29 +00:00
}
dnsCfg := &DNS{
Enable: cfg.Enable,
Listen: cfg.Listen,
IPv6: cfg.IPv6,
2018-12-05 13:13:29 +00:00
EnhancedMode: cfg.EnhancedMode,
2019-09-15 05:36:45 +00:00
FallbackFilter: FallbackFilter{
IPCIDR: []*net.IPNet{},
},
2018-12-05 13:13:29 +00:00
}
var err error
if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer); err != nil {
return nil, err
2018-12-05 13:13:29 +00:00
}
if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback); err != nil {
return nil, err
2018-12-05 13:13:29 +00:00
}
if len(cfg.DefaultNameserver) == 0 {
return nil, errors.New("default nameserver should have at least one nameserver")
}
if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver); err != nil {
return nil, err
}
// check default nameserver is pure ip addr
for _, ns := range dnsCfg.DefaultNameserver {
2020-02-17 14:13:15 +00:00
host, _, err := net.SplitHostPort(ns.Addr)
if err != nil || net.ParseIP(host) == nil {
return nil, errors.New("default nameserver should be pure IP")
}
}
2019-05-02 16:05:14 +00:00
if cfg.EnhancedMode == dns.FAKEIP {
_, ipnet, err := net.ParseCIDR(cfg.FakeIPRange)
if err != nil {
return nil, err
}
2019-12-27 16:10:06 +00:00
var host *trie.DomainTrie
2019-12-27 16:10:06 +00:00
// fake ip skip host filter
if len(cfg.FakeIPFilter) != 0 {
host = trie.New()
for _, domain := range cfg.FakeIPFilter {
host.Insert(domain, true)
}
}
pool, err := fakeip.New(ipnet, 1000, host)
2019-05-02 16:05:14 +00:00
if err != nil {
return nil, err
}
dnsCfg.FakeIPRange = pool
}
2019-09-15 05:36:45 +00:00
dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP
if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil {
dnsCfg.FallbackFilter.IPCIDR = fallbackip
}
dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain
2019-09-15 05:36:45 +00:00
if cfg.UseHosts {
dnsCfg.Hosts = hosts
}
2018-12-05 13:13:29 +00:00
return dnsCfg, nil
}
func parseAuthentication(rawRecords []string) []auth.AuthUser {
users := make([]auth.AuthUser, 0)
for _, line := range rawRecords {
userData := strings.SplitN(line, ":", 2)
if len(userData) == 2 {
users = append(users, auth.AuthUser{User: userData[0], Pass: userData[1]})
}
}
return users
}