clash/config/config.go

523 lines
13 KiB
Go
Raw Normal View History

package config
import (
"fmt"
"io/ioutil"
2018-12-05 13:13:29 +00:00
"net"
"net/url"
"os"
2018-12-19 17:29:13 +00:00
"path/filepath"
"strings"
2018-10-06 05:15:02 +00:00
adapters "github.com/Dreamacro/clash/adapters/outbound"
"github.com/Dreamacro/clash/common/structure"
2019-05-02 16:05:14 +00:00
"github.com/Dreamacro/clash/component/fakeip"
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 {
2018-11-21 05:47:46 +00:00
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
AllowLan bool `json:"allow-lan"`
Mode T.Mode `json:"mode"`
LogLevel log.LogLevel `json:"log-level"`
2018-12-21 14:51:37 +00:00
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"`
Listen string `yaml:"listen"`
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
2019-05-02 16:05:14 +00:00
FakeIPRange *fakeip.Pool
}
// Experimental config
type Experimental struct {
IgnoreResolveFail bool `yaml:"ignore-resolve-fail"`
}
// Config is clash config manager
type Config struct {
General *General
DNS *DNS
Experimental *Experimental
Rules []C.Rule
Proxies map[string]C.Proxy
}
2018-12-05 13:13:29 +00:00
type rawDNS struct {
Enable bool `yaml:"enable"`
IPv6 bool `yaml:"ipv6"`
NameServer []string `yaml:"nameserver"`
Fallback []string `yaml:"fallback"`
Listen string `yaml:"listen"`
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
2019-05-02 16:05:14 +00:00
FakeIPRange string `yaml:"fake-ip-range"`
2018-12-05 13:13:29 +00:00
}
type rawConfig struct {
Port int `yaml:"port"`
SocksPort int `yaml:"socks-port"`
RedirPort int `yaml:"redir-port"`
AllowLan bool `yaml:"allow-lan"`
Mode T.Mode `yaml:"mode"`
LogLevel log.LogLevel `yaml:"log-level"`
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"`
DNS rawDNS `yaml:"dns"`
Experimental Experimental `yaml:"experimental"`
Proxy []map[string]interface{} `yaml:"Proxy"`
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
Rule []string `yaml:"Rule"`
2018-12-05 13:13:29 +00:00
}
// forward compatibility before 1.0
func readRawConfig(path string) ([]byte, error) {
data, err := ioutil.ReadFile(path)
if err == nil && len(data) != 0 {
return data, nil
}
if filepath.Ext(path) != ".yaml" {
return nil, err
}
path = path[:len(path)-5] + ".yml"
return ioutil.ReadFile(path)
}
2018-11-21 05:47:46 +00:00
func readConfig(path string) (*rawConfig, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
data, err := readRawConfig(path)
if err != nil {
return nil, err
}
2018-10-14 13:22:58 +00:00
if len(data) == 0 {
return nil, fmt.Errorf("Configuration file %s is empty", C.Path.Config())
}
// config with some default value
2018-11-21 05:47:46 +00:00
rawConfig := &rawConfig{
AllowLan: false,
2018-12-05 13:13:29 +00:00
Mode: T.Rule,
LogLevel: log.INFO,
Rule: []string{},
Proxy: []map[string]interface{}{},
ProxyGroup: []map[string]interface{}{},
Experimental: Experimental{
IgnoreResolveFail: true,
},
DNS: rawDNS{
2019-05-02 16:05:14 +00:00
Enable: false,
FakeIPRange: "198.18.0.1/16",
2018-12-05 13:52:31 +00:00
},
}
err = yaml.Unmarshal([]byte(data), &rawConfig)
return rawConfig, err
}
// Parse config
2018-11-21 05:47:46 +00:00
func Parse(path string) (*Config, error) {
config := &Config{}
rawCfg, err := readConfig(path)
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, err
}
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
2018-11-21 05:47:46 +00:00
proxies, err := parseProxies(rawCfg)
if err != nil {
return nil, err
}
2018-11-21 05:47:46 +00:00
config.Proxies = proxies
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
2018-12-05 13:13:29 +00:00
dnsCfg, err := parseDNS(rawCfg.DNS)
if err != nil {
return nil, err
}
config.DNS = dnsCfg
2018-11-21 05:47:46 +00:00
return config, nil
}
2018-11-21 05:47:46 +00:00
func parseGeneral(cfg *rawConfig) (*General, error) {
port := cfg.Port
socksPort := cfg.SocksPort
redirPort := cfg.RedirPort
allowLan := cfg.AllowLan
2018-11-21 05:47:46 +00:00
externalController := cfg.ExternalController
2018-12-19 17:29:13 +00:00
externalUI := cfg.ExternalUI
2018-11-21 05:47:46 +00:00
secret := cfg.Secret
2018-12-05 13:13:29 +00:00
mode := cfg.Mode
logLevel := cfg.LogLevel
2018-12-21 02:55:21 +00:00
if externalUI != "" {
if !filepath.IsAbs(externalUI) {
externalUI = filepath.Join(C.Path.HomeDir(), externalUI)
}
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
}
2018-11-21 05:47:46 +00:00
general := &General{
Port: port,
SocksPort: socksPort,
RedirPort: redirPort,
AllowLan: allowLan,
Mode: mode,
LogLevel: logLevel,
ExternalController: externalController,
2018-12-19 17:29:13 +00:00
ExternalUI: externalUI,
2018-11-21 05:47:46 +00:00
Secret: secret,
}
2018-11-21 05:47:46 +00:00
return general, nil
}
2018-11-21 05:47:46 +00:00
func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) {
proxies := make(map[string]C.Proxy)
proxyList := []string{}
proxiesConfig := cfg.Proxy
groupsConfig := cfg.ProxyGroup
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
proxies["DIRECT"] = adapters.NewProxy(adapters.NewDirect())
proxies["REJECT"] = adapters.NewProxy(adapters.NewReject())
proxyList = append(proxyList, "DIRECT", "REJECT")
// parse proxy
for idx, mapping := range proxiesConfig {
proxyType, existType := mapping["type"].(string)
2018-10-27 04:57:56 +00:00
if !existType {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("Proxy %d missing type", idx)
}
var proxy C.ProxyAdapter
2019-02-15 06:25:20 +00:00
err := fmt.Errorf("can't parse")
switch proxyType {
case "ss":
ssOption := &adapters.ShadowSocksOption{}
err = decoder.Decode(mapping, ssOption)
if err != nil {
break
}
proxy, err = adapters.NewShadowSocks(*ssOption)
2018-08-12 05:50:54 +00:00
case "socks5":
socksOption := &adapters.Socks5Option{}
err = decoder.Decode(mapping, socksOption)
2018-09-06 02:53:29 +00:00
if err != nil {
break
2018-09-06 02:53:29 +00:00
}
proxy = adapters.NewSocks5(*socksOption)
case "http":
httpOption := &adapters.HttpOption{}
err = decoder.Decode(mapping, httpOption)
if err != nil {
break
}
proxy = adapters.NewHttp(*httpOption)
case "vmess":
vmessOption := &adapters.VmessOption{}
err = decoder.Decode(mapping, vmessOption)
2018-09-06 02:53:29 +00:00
if err != nil {
break
2018-09-06 02:53:29 +00:00
}
proxy, err = adapters.NewVmess(*vmessOption)
default:
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("Unsupport proxy type: %s", proxyType)
}
2018-10-27 04:57:56 +00:00
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("Proxy [%d]: %s", idx, err.Error())
2018-10-27 04:57:56 +00:00
}
if _, exist := proxies[proxy.Name()]; exist {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("Proxy %s is the duplicate name", proxy.Name())
}
proxies[proxy.Name()] = adapters.NewProxy(proxy)
proxyList = append(proxyList, proxy.Name())
}
// parse proxy group
for idx, mapping := range groupsConfig {
groupType, existType := mapping["type"].(string)
groupName, existName := mapping["name"].(string)
if !existType && existName {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("ProxyGroup %d: missing type or name", idx)
}
if _, exist := proxies[groupName]; exist {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
}
var group C.ProxyAdapter
2019-02-15 06:25:20 +00:00
ps := []C.Proxy{}
err := fmt.Errorf("can't parse")
switch groupType {
case "url-test":
urlTestOption := &adapters.URLTestOption{}
err = decoder.Decode(mapping, urlTestOption)
if err != nil {
break
}
2019-02-04 01:39:17 +00:00
ps, err = getProxies(proxies, urlTestOption.Proxies)
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
}
group, err = adapters.NewURLTest(*urlTestOption, ps)
case "select":
selectorOption := &adapters.SelectorOption{}
err = decoder.Decode(mapping, selectorOption)
if err != nil {
break
}
2019-02-04 01:39:17 +00:00
ps, err = getProxies(proxies, selectorOption.Proxies)
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
}
group, err = adapters.NewSelector(selectorOption.Name, ps)
2018-09-25 16:34:15 +00:00
case "fallback":
fallbackOption := &adapters.FallbackOption{}
err = decoder.Decode(mapping, fallbackOption)
if err != nil {
break
2018-09-25 16:34:15 +00:00
}
2019-02-04 01:39:17 +00:00
ps, err = getProxies(proxies, fallbackOption.Proxies)
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
2018-09-25 16:34:15 +00:00
}
group, err = adapters.NewFallback(*fallbackOption, ps)
2019-02-15 06:25:20 +00:00
case "load-balance":
loadBalanceOption := &adapters.LoadBalanceOption{}
err = decoder.Decode(mapping, loadBalanceOption)
if err != nil {
break
}
ps, err = getProxies(proxies, loadBalanceOption.Proxies)
if err != nil {
return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error())
}
group, err = adapters.NewLoadBalance(*loadBalanceOption, ps)
}
if err != nil {
2018-11-21 05:47:46 +00:00
return nil, fmt.Errorf("Proxy %s: %s", groupName, err.Error())
}
proxies[groupName] = adapters.NewProxy(group)
proxyList = append(proxyList, groupName)
}
2019-02-15 06:25:20 +00:00
ps := []C.Proxy{}
for _, v := range proxyList {
ps = append(ps, proxies[v])
}
global, _ := adapters.NewSelector("GLOBAL", ps)
proxies["GLOBAL"] = adapters.NewProxy(global)
2018-11-21 05:47:46 +00:00
return proxies, nil
}
func parseRules(cfg *rawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
rules := []C.Rule{}
rulesConfig := cfg.Rule
// parse rules
for idx, line := range rulesConfig {
rule := trimArr(strings.Split(line, ","))
var (
payload string
target string
)
switch len(rule) {
case 2:
target = rule[1]
case 3:
payload = rule[1]
target = rule[2]
default:
2019-03-30 06:11:59 +00:00
return nil, fmt.Errorf("Rules[%d] [%s] error: format invalid", idx, line)
}
if _, ok := proxies[target]; !ok {
return nil, fmt.Errorf("Rules[%d] [%s] error: proxy [%s] not found", idx, line, target)
}
rule = trimArr(rule)
2019-03-30 06:11:59 +00:00
var parsed C.Rule
switch rule[0] {
2018-09-09 07:01:46 +00:00
case "DOMAIN":
2019-03-30 06:11:59 +00:00
parsed = R.NewDomain(payload, target)
case "DOMAIN-SUFFIX":
2019-03-30 06:11:59 +00:00
parsed = R.NewDomainSuffix(payload, target)
case "DOMAIN-KEYWORD":
2019-03-30 06:11:59 +00:00
parsed = R.NewDomainKeyword(payload, target)
case "GEOIP":
2019-03-30 06:11:59 +00:00
parsed = R.NewGEOIP(payload, target)
case "IP-CIDR", "IP-CIDR6":
2019-03-30 06:11:59 +00:00
if rule := R.NewIPCIDR(payload, target, false); rule != nil {
parsed = rule
}
2019-05-09 13:00:29 +00:00
// deprecated when bump to 1.0
case "SOURCE-IP-CIDR":
2019-05-09 13:00:29 +00:00
fallthrough
case "SRC-IP-CIDR":
2019-03-30 06:11:59 +00:00
if rule := R.NewIPCIDR(payload, target, true); rule != nil {
parsed = rule
}
2019-05-09 13:00:29 +00:00
case "SRC-PORT":
if rule := R.NewPort(payload, target, true); rule != nil {
parsed = rule
}
case "DST-PORT":
if rule := R.NewPort(payload, target, false); rule != nil {
parsed = rule
}
case "MATCH":
fallthrough
2019-05-09 13:00:29 +00:00
// deprecated when bump to 1.0
case "FINAL":
2019-03-30 06:11:59 +00:00
parsed = R.NewMatch(target)
}
2019-03-30 06:11:59 +00:00
if parsed == nil {
return nil, fmt.Errorf("Rules[%d] [%s] error: payload invalid", idx, line)
}
rules = append(rules, parsed)
}
2018-11-21 05:47:46 +00:00
return rules, nil
}
2018-12-05 13:13:29 +00:00
func hostWithDefaultPort(host string, defPort string) (string, error) {
if !strings.Contains(host, ":") {
host += ":"
}
hostname, port, err := net.SplitHostPort(host)
if err != nil {
return "", err
}
if port == "" {
port = defPort
}
return net.JoinHostPort(hostname, port), nil
}
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())
}
var host, dnsNetType string
switch u.Scheme {
case "udp":
host, err = hostWithDefaultPort(u.Host, "53")
dnsNetType = "" // UDP
case "tcp":
host, err = hostWithDefaultPort(u.Host, "53")
dnsNetType = "tcp" // TCP
case "tls":
host, err = hostWithDefaultPort(u.Host, "853")
dnsNetType = "tcp-tls" // DNS over TLS
default:
2018-12-05 13:13:29 +00:00
return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
}
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,
2018-12-05 13:13:29 +00:00
Addr: host,
},
)
}
return nameservers, nil
}
func parseDNS(cfg rawDNS) (*DNS, error) {
2018-12-05 13:13:29 +00:00
if cfg.Enable && len(cfg.NameServer) == 0 {
return nil, fmt.Errorf("If DNS configuration is turned on, NameServer cannot be empty")
}
dnsCfg := &DNS{
Enable: cfg.Enable,
Listen: cfg.Listen,
EnhancedMode: cfg.EnhancedMode,
}
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
}
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
}
pool, err := fakeip.New(ipnet)
if err != nil {
return nil, err
}
dnsCfg.FakeIPRange = pool
}
2018-12-05 13:13:29 +00:00
return dnsCfg, nil
}