Break Change: use yml, which is easier to parse, as the config format
This commit is contained in:
parent
c618a5f400
commit
35378f7ab9
12 changed files with 222 additions and 186 deletions
|
@ -21,6 +21,13 @@ type Fallback struct {
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FallbackOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Proxies []string `proxy:"proxies"`
|
||||||
|
URL string `proxy:"url"`
|
||||||
|
Delay int `proxy:"delay"`
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Fallback) Name() string {
|
func (f *Fallback) Name() string {
|
||||||
return f.name
|
return f.name
|
||||||
}
|
}
|
||||||
|
@ -98,8 +105,8 @@ func (f *Fallback) validTest() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFallback(name string, proxies []C.Proxy, rawURL string, delay time.Duration) (*Fallback, error) {
|
func NewFallback(option FallbackOption, proxies []C.Proxy) (*Fallback, error) {
|
||||||
_, err := urlToMetadata(rawURL)
|
_, err := urlToMetadata(option.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -108,6 +115,7 @@ func NewFallback(name string, proxies []C.Proxy, rawURL string, delay time.Durat
|
||||||
return nil, errors.New("The number of proxies cannot be 0")
|
return nil, errors.New("The number of proxies cannot be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delay := time.Duration(option.Delay) * time.Second
|
||||||
warpperProxies := make([]*proxy, len(proxies))
|
warpperProxies := make([]*proxy, len(proxies))
|
||||||
for idx := range proxies {
|
for idx := range proxies {
|
||||||
warpperProxies[idx] = &proxy{
|
warpperProxies[idx] = &proxy{
|
||||||
|
@ -117,9 +125,9 @@ func NewFallback(name string, proxies []C.Proxy, rawURL string, delay time.Durat
|
||||||
}
|
}
|
||||||
|
|
||||||
Fallback := &Fallback{
|
Fallback := &Fallback{
|
||||||
name: name,
|
name: option.Name,
|
||||||
proxies: warpperProxies,
|
proxies: warpperProxies,
|
||||||
rawURL: rawURL,
|
rawURL: option.URL,
|
||||||
delay: delay,
|
delay: delay,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,11 @@ type Selector struct {
|
||||||
proxies map[string]C.Proxy
|
proxies map[string]C.Proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SelectorOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Proxies []string `proxy:"proxies"`
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Selector) Name() string {
|
func (s *Selector) Name() string {
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/simple-obfs"
|
"github.com/Dreamacro/clash/component/simple-obfs"
|
||||||
|
@ -36,6 +35,16 @@ type ShadowSocks struct {
|
||||||
cipher core.Cipher
|
cipher core.Cipher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ShadowSocksOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Server string `proxy:"server"`
|
||||||
|
Port int `proxy:"port"`
|
||||||
|
Password string `proxy:"password"`
|
||||||
|
Cipher string `proxy:"cipher"`
|
||||||
|
Obfs string `proxy:"obfs,omitempty"`
|
||||||
|
ObfsHost string `proxy:"obfs-host,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (ss *ShadowSocks) Name() string {
|
func (ss *ShadowSocks) Name() string {
|
||||||
return ss.name
|
return ss.name
|
||||||
}
|
}
|
||||||
|
@ -62,46 +71,30 @@ func (ss *ShadowSocks) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter,
|
||||||
return &ShadowsocksAdapter{conn: c}, err
|
return &ShadowsocksAdapter{conn: c}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowSocks(name string, ssURL string, option map[string]string) (*ShadowSocks, error) {
|
func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
||||||
server, cipher, password, _ := parseURL(ssURL)
|
server := fmt.Sprintf("%s:%d", option.Server, option.Port)
|
||||||
|
cipher := option.Cipher
|
||||||
|
password := option.Password
|
||||||
ciph, err := core.PickCipher(cipher, nil, password)
|
ciph, err := core.PickCipher(cipher, nil, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ss %s initialize error: %s", server, err.Error())
|
return nil, fmt.Errorf("ss %s initialize error: %s", server, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
obfs := ""
|
obfs := option.Obfs
|
||||||
obfsHost := "bing.com"
|
obfsHost := "bing.com"
|
||||||
if value, ok := option["obfs"]; ok {
|
if option.ObfsHost != "" {
|
||||||
obfs = value
|
obfsHost = option.ObfsHost
|
||||||
}
|
|
||||||
|
|
||||||
if value, ok := option["obfs-host"]; ok {
|
|
||||||
obfsHost = value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ShadowSocks{
|
return &ShadowSocks{
|
||||||
server: server,
|
server: server,
|
||||||
name: name,
|
name: option.Name,
|
||||||
cipher: ciph,
|
cipher: ciph,
|
||||||
obfs: obfs,
|
obfs: obfs,
|
||||||
obfsHost: obfsHost,
|
obfsHost: obfsHost,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseURL(s string) (addr, cipher, password string, err error) {
|
|
||||||
u, err := url.Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = u.Host
|
|
||||||
if u.User != nil {
|
|
||||||
cipher = u.User.Username()
|
|
||||||
password, _ = u.User.Password()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func serializesSocksAddr(metadata *C.Metadata) []byte {
|
func serializesSocksAddr(metadata *C.Metadata) []byte {
|
||||||
var buf [][]byte
|
var buf [][]byte
|
||||||
aType := uint8(metadata.AddrType)
|
aType := uint8(metadata.AddrType)
|
||||||
|
|
|
@ -31,6 +31,12 @@ type Socks5 struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Socks5Option struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Server string `proxy:"server"`
|
||||||
|
Port int `proxy:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
func (ss *Socks5) Name() string {
|
func (ss *Socks5) Name() string {
|
||||||
return ss.name
|
return ss.name
|
||||||
}
|
}
|
||||||
|
@ -82,9 +88,9 @@ func (ss *Socks5) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks5(name, addr string) *Socks5 {
|
func NewSocks5(option Socks5Option) *Socks5 {
|
||||||
return &Socks5{
|
return &Socks5{
|
||||||
addr: addr,
|
addr: fmt.Sprintf("%s:%d", option.Server, option.Port),
|
||||||
name: name,
|
name: option.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package adapters
|
package adapters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -16,6 +17,13 @@ type URLTest struct {
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type URLTestOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Proxies []string `proxy:"proxies"`
|
||||||
|
URL string `proxy:"url"`
|
||||||
|
Delay int `proxy:"delay"`
|
||||||
|
}
|
||||||
|
|
||||||
func (u *URLTest) Name() string {
|
func (u *URLTest) Name() string {
|
||||||
return u.name
|
return u.name
|
||||||
}
|
}
|
||||||
|
@ -83,16 +91,20 @@ func (u *URLTest) speedTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLTest(name string, proxies []C.Proxy, rawURL string, delay time.Duration) (*URLTest, error) {
|
func NewURLTest(option URLTestOption, proxies []C.Proxy) (*URLTest, error) {
|
||||||
_, err := urlToMetadata(rawURL)
|
_, err := urlToMetadata(option.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(proxies) < 1 {
|
||||||
|
return nil, errors.New("The number of proxies cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
delay := time.Duration(option.Delay) * time.Second
|
||||||
urlTest := &URLTest{
|
urlTest := &URLTest{
|
||||||
name: name,
|
name: option.Name,
|
||||||
proxies: proxies[:],
|
proxies: proxies[:],
|
||||||
rawURL: rawURL,
|
rawURL: option.URL,
|
||||||
fast: proxies[0],
|
fast: proxies[0],
|
||||||
delay: delay,
|
delay: delay,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
|
|
@ -34,11 +34,11 @@ func DelayTest(proxy C.Proxy, url string) (t int16, err error) {
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
}
|
}
|
||||||
client := http.Client{Transport: transport}
|
client := http.Client{Transport: transport}
|
||||||
req, err := client.Get(url)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Body.Close()
|
resp.Body.Close()
|
||||||
t = int16(time.Since(start) / time.Millisecond)
|
t = int16(time.Since(start) / time.Millisecond)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,16 @@ type Vmess struct {
|
||||||
client *vmess.Client
|
client *vmess.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VmessOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Server string `proxy:"server"`
|
||||||
|
Port int `proxy:"port"`
|
||||||
|
UUID string `proxy:"uuid"`
|
||||||
|
AlterID int `proxy:"alterId"`
|
||||||
|
Cipher string `proxy:"cipher"`
|
||||||
|
TLS bool `proxy:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (ss *Vmess) Name() string {
|
func (ss *Vmess) Name() string {
|
||||||
return ss.name
|
return ss.name
|
||||||
}
|
}
|
||||||
|
@ -48,21 +58,21 @@ func (ss *Vmess) Generator(metadata *C.Metadata) (adapter C.ProxyAdapter, err er
|
||||||
return &VmessAdapter{conn: c}, err
|
return &VmessAdapter{conn: c}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVmess(name string, server string, uuid string, alterID uint16, security string, option map[string]string) (*Vmess, error) {
|
func NewVmess(option VmessOption) (*Vmess, error) {
|
||||||
security = strings.ToLower(security)
|
security := strings.ToLower(option.Cipher)
|
||||||
client, err := vmess.NewClient(vmess.Config{
|
client, err := vmess.NewClient(vmess.Config{
|
||||||
UUID: uuid,
|
UUID: option.UUID,
|
||||||
AlterID: alterID,
|
AlterID: uint16(option.AlterID),
|
||||||
Security: security,
|
Security: security,
|
||||||
TLS: option["tls"] == "true",
|
TLS: option.TLS,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Vmess{
|
return &Vmess{
|
||||||
name: name,
|
name: option.Name,
|
||||||
server: server,
|
server: fmt.Sprintf("%s:%d", option.Server, option.Port),
|
||||||
client: client,
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
253
config/config.go
253
config/config.go
|
@ -1,22 +1,21 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapters/outbound"
|
"github.com/Dreamacro/clash/adapters/outbound"
|
||||||
"github.com/Dreamacro/clash/common/observable"
|
"github.com/Dreamacro/clash/common/observable"
|
||||||
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
R "github.com/Dreamacro/clash/rules"
|
R "github.com/Dreamacro/clash/rules"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/ini.v1"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -42,6 +41,21 @@ type ProxyConfig struct {
|
||||||
AllowLan *bool
|
AllowLan *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawConfig is raw config struct
|
||||||
|
type RawConfig struct {
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
SocksPort int `yaml:"socks-port"`
|
||||||
|
RedirPort int `yaml:"redir-port"`
|
||||||
|
AllowLan bool `yaml:"allow-lan"`
|
||||||
|
Mode string `yaml:"mode"`
|
||||||
|
LogLevel string `yaml:"log-level"`
|
||||||
|
ExternalController string `yaml:"external-controller"`
|
||||||
|
|
||||||
|
Proxy []map[string]interface{} `yaml:"Proxy"`
|
||||||
|
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
|
||||||
|
Rule []string `yaml:"Rule"`
|
||||||
|
}
|
||||||
|
|
||||||
// Config is clash config manager
|
// Config is clash config manager
|
||||||
type Config struct {
|
type Config struct {
|
||||||
general *General
|
general *General
|
||||||
|
@ -71,17 +85,25 @@ func (c *Config) Report() chan<- interface{} {
|
||||||
return c.reportCh
|
return c.reportCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) readConfig() (*ini.File, error) {
|
func (c *Config) readConfig() (*RawConfig, error) {
|
||||||
if _, err := os.Stat(C.ConfigPath); os.IsNotExist(err) {
|
if _, err := os.Stat(C.ConfigPath); os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ini.LoadSources(
|
data, err := ioutil.ReadFile(C.ConfigPath)
|
||||||
ini.LoadOptions{
|
if err != nil {
|
||||||
AllowBooleanKeys: true,
|
return nil, err
|
||||||
UnparseableSections: []string{"Rule"},
|
}
|
||||||
},
|
// config with some default value
|
||||||
C.ConfigPath,
|
rawConfig := &RawConfig{
|
||||||
)
|
AllowLan: false,
|
||||||
|
Mode: Rule.String(),
|
||||||
|
LogLevel: C.INFO.String(),
|
||||||
|
Rule: []string{},
|
||||||
|
Proxy: []map[string]interface{}{},
|
||||||
|
ProxyGroup: []map[string]interface{}{},
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal([]byte(data), &rawConfig)
|
||||||
|
return rawConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse config
|
// Parse config
|
||||||
|
@ -139,15 +161,13 @@ func (c *Config) UpdateRules() error {
|
||||||
return c.parseRules(cfg)
|
return c.parseRules(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) parseGeneral(cfg *ini.File) error {
|
func (c *Config) parseGeneral(cfg *RawConfig) error {
|
||||||
general := cfg.Section("General")
|
port := cfg.Port
|
||||||
|
socksPort := cfg.SocksPort
|
||||||
port := general.Key("port").RangeInt(0, 1, 65535)
|
redirPort := cfg.RedirPort
|
||||||
socksPort := general.Key("socks-port").RangeInt(0, 1, 65535)
|
allowLan := cfg.AllowLan
|
||||||
redirPort := general.Key("redir-port").RangeInt(0, 1, 65535)
|
logLevelString := cfg.LogLevel
|
||||||
allowLan := general.Key("allow-lan").MustBool()
|
modeString := cfg.Mode
|
||||||
logLevelString := general.Key("log-level").MustString(C.INFO.String())
|
|
||||||
modeString := general.Key("mode").MustString(Rule.String())
|
|
||||||
|
|
||||||
mode, exist := ModeMapping[modeString]
|
mode, exist := ModeMapping[modeString]
|
||||||
if !exist {
|
if !exist {
|
||||||
|
@ -168,7 +188,7 @@ func (c *Config) parseGeneral(cfg *ini.File) error {
|
||||||
LogLevel: logLevel,
|
LogLevel: logLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if restAddr := general.Key("external-controller").String(); restAddr != "" {
|
if restAddr := cfg.ExternalController; restAddr != "" {
|
||||||
c.event <- &Event{Type: "external-controller", Payload: restAddr}
|
c.event <- &Event{Type: "external-controller", Payload: restAddr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,129 +230,128 @@ func (c *Config) UpdateProxy(pc ProxyConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) parseProxies(cfg *ini.File) error {
|
func (c *Config) parseProxies(cfg *RawConfig) error {
|
||||||
proxies := make(map[string]C.Proxy)
|
proxies := make(map[string]C.Proxy)
|
||||||
proxiesConfig := cfg.Section("Proxy")
|
proxiesConfig := cfg.Proxy
|
||||||
groupsConfig := cfg.Section("Proxy Group")
|
groupsConfig := cfg.ProxyGroup
|
||||||
|
|
||||||
|
decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true})
|
||||||
|
|
||||||
|
proxies["DIRECT"] = adapters.NewDirect()
|
||||||
|
proxies["REJECT"] = adapters.NewReject()
|
||||||
|
|
||||||
// parse proxy
|
// parse proxy
|
||||||
for _, key := range proxiesConfig.Keys() {
|
for idx, mapping := range proxiesConfig {
|
||||||
proxy := key.Strings(",")
|
proxyType, existType := mapping["type"].(string)
|
||||||
if len(proxy) == 0 {
|
proxyName, existName := mapping["name"].(string)
|
||||||
continue
|
if !existType && existName {
|
||||||
|
return fmt.Errorf("Proxy %d missing type or name", idx)
|
||||||
}
|
}
|
||||||
switch proxy[0] {
|
|
||||||
// ss, server, port, cipter, password
|
if _, exist := proxies[proxyName]; exist {
|
||||||
|
return fmt.Errorf("Proxy %s is the duplicate name", proxyName)
|
||||||
|
}
|
||||||
|
var proxy C.Proxy
|
||||||
|
var err error
|
||||||
|
switch proxyType {
|
||||||
case "ss":
|
case "ss":
|
||||||
if len(proxy) < 5 {
|
ssOption := &adapters.ShadowSocksOption{}
|
||||||
continue
|
err = decoder.Decode(mapping, ssOption)
|
||||||
}
|
|
||||||
ssURL := url.URL{
|
|
||||||
Scheme: "ss",
|
|
||||||
User: url.UserPassword(proxy[3], proxy[4]),
|
|
||||||
Host: fmt.Sprintf("%s:%s", proxy[1], proxy[2]),
|
|
||||||
}
|
|
||||||
option := parseOptions(5, proxy...)
|
|
||||||
ss, err := adapters.NewShadowSocks(key.Name(), ssURL.String(), option)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
break
|
||||||
}
|
}
|
||||||
proxies[key.Name()] = ss
|
proxy, err = adapters.NewShadowSocks(*ssOption)
|
||||||
// socks5, server, port
|
|
||||||
case "socks5":
|
case "socks5":
|
||||||
if len(proxy) < 3 {
|
socksOption := &adapters.Socks5Option{}
|
||||||
continue
|
err = decoder.Decode(mapping, socksOption)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
addr := fmt.Sprintf("%s:%s", proxy[1], proxy[2])
|
proxy = adapters.NewSocks5(*socksOption)
|
||||||
socks5 := adapters.NewSocks5(key.Name(), addr)
|
|
||||||
proxies[key.Name()] = socks5
|
|
||||||
// vmess, server, port, uuid, alterId, security
|
|
||||||
case "vmess":
|
case "vmess":
|
||||||
if len(proxy) < 6 {
|
vmessOption := &adapters.VmessOption{}
|
||||||
continue
|
err = decoder.Decode(mapping, vmessOption)
|
||||||
}
|
|
||||||
addr := fmt.Sprintf("%s:%s", proxy[1], proxy[2])
|
|
||||||
alterID, err := strconv.Atoi(proxy[4])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
break
|
||||||
}
|
}
|
||||||
option := parseOptions(6, proxy...)
|
proxy, err = adapters.NewVmess(*vmessOption)
|
||||||
vmess, err := adapters.NewVmess(key.Name(), addr, proxy[3], uint16(alterID), proxy[5], option)
|
default:
|
||||||
if err != nil {
|
return fmt.Errorf("Unsupport proxy type: %s", proxyType)
|
||||||
return err
|
|
||||||
}
|
|
||||||
proxies[key.Name()] = vmess
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Proxy %s: %s", proxyName, err.Error())
|
||||||
|
}
|
||||||
|
proxies[proxyName] = proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse proxy group
|
// parse proxy group
|
||||||
for _, key := range groupsConfig.Keys() {
|
for idx, mapping := range groupsConfig {
|
||||||
rule := strings.Split(key.Value(), ",")
|
groupType, existType := mapping["type"].(string)
|
||||||
rule = trimArr(rule)
|
groupName, existName := mapping["name"].(string)
|
||||||
switch rule[0] {
|
if !existType && existName {
|
||||||
|
return fmt.Errorf("ProxyGroup %d: missing type or name", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exist := proxies[groupName]; exist {
|
||||||
|
return fmt.Errorf("ProxyGroup %s: the duplicate name", groupName)
|
||||||
|
}
|
||||||
|
var group C.Proxy
|
||||||
|
var err error
|
||||||
|
switch groupType {
|
||||||
case "url-test":
|
case "url-test":
|
||||||
if len(rule) < 4 {
|
urlTestOption := &adapters.URLTestOption{}
|
||||||
return fmt.Errorf("URLTest need more than 4 param")
|
err = decoder.Decode(mapping, urlTestOption)
|
||||||
}
|
if err != nil {
|
||||||
proxyNames := rule[1 : len(rule)-2]
|
break
|
||||||
delay, _ := strconv.Atoi(rule[len(rule)-1])
|
|
||||||
url := rule[len(rule)-2]
|
|
||||||
var ps []C.Proxy
|
|
||||||
for _, name := range proxyNames {
|
|
||||||
if p, ok := proxies[name]; ok {
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter, err := adapters.NewURLTest(key.Name(), ps, url, time.Duration(delay)*time.Second)
|
var ps []C.Proxy
|
||||||
if err != nil {
|
for _, name := range urlTestOption.Proxies {
|
||||||
return fmt.Errorf("Config error: %s", err.Error())
|
p, ok := proxies[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("ProxyGroup %s: proxy or proxy group '%s' not found", groupName, name)
|
||||||
|
}
|
||||||
|
ps = append(ps, p)
|
||||||
}
|
}
|
||||||
proxies[key.Name()] = adapter
|
group, err = adapters.NewURLTest(*urlTestOption, ps)
|
||||||
case "select":
|
case "select":
|
||||||
if len(rule) < 2 {
|
selectorOption := &adapters.SelectorOption{}
|
||||||
return fmt.Errorf("Selector need more than 2 param")
|
err = decoder.Decode(mapping, selectorOption)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
proxyNames := rule[1:]
|
|
||||||
selectProxy := make(map[string]C.Proxy)
|
selectProxy := make(map[string]C.Proxy)
|
||||||
for _, name := range proxyNames {
|
for _, name := range selectorOption.Proxies {
|
||||||
proxy, exist := proxies[name]
|
proxy, exist := proxies[name]
|
||||||
if !exist {
|
if !exist {
|
||||||
return fmt.Errorf("Proxy %s not exist", name)
|
return fmt.Errorf("ProxyGroup %s: proxy or proxy group '%s' not found", groupName, name)
|
||||||
}
|
}
|
||||||
selectProxy[name] = proxy
|
selectProxy[name] = proxy
|
||||||
}
|
}
|
||||||
selector, err := adapters.NewSelector(key.Name(), selectProxy)
|
group, err = adapters.NewSelector(selectorOption.Name, selectProxy)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Selector create error: %s", err.Error())
|
|
||||||
}
|
|
||||||
proxies[key.Name()] = selector
|
|
||||||
case "fallback":
|
case "fallback":
|
||||||
if len(rule) < 4 {
|
fallbackOption := &adapters.FallbackOption{}
|
||||||
return fmt.Errorf("URLTest need more than 4 param")
|
err = decoder.Decode(mapping, fallbackOption)
|
||||||
}
|
|
||||||
proxyNames := rule[1 : len(rule)-2]
|
|
||||||
delay, _ := strconv.Atoi(rule[len(rule)-1])
|
|
||||||
url := rule[len(rule)-2]
|
|
||||||
var ps []C.Proxy
|
|
||||||
for _, name := range proxyNames {
|
|
||||||
if p, ok := proxies[name]; ok {
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter, err := adapters.NewFallback(key.Name(), ps, url, time.Duration(delay)*time.Second)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Config error: %s", err.Error())
|
break
|
||||||
}
|
}
|
||||||
proxies[key.Name()] = adapter
|
var ps []C.Proxy
|
||||||
|
for _, name := range fallbackOption.Proxies {
|
||||||
|
p, ok := proxies[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("ProxyGroup %s: proxy or proxy group '%s' not found", groupName, name)
|
||||||
|
}
|
||||||
|
ps = append(ps, p)
|
||||||
|
}
|
||||||
|
group, err = adapters.NewFallback(*fallbackOption, ps)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Proxy %s: %s", groupName, err.Error())
|
||||||
|
}
|
||||||
|
proxies[groupName] = group
|
||||||
}
|
}
|
||||||
|
|
||||||
// init proxy
|
|
||||||
proxies["GLOBAL"], _ = adapters.NewSelector("GLOBAL", proxies)
|
proxies["GLOBAL"], _ = adapters.NewSelector("GLOBAL", proxies)
|
||||||
proxies["DIRECT"] = adapters.NewDirect()
|
|
||||||
proxies["REJECT"] = adapters.NewReject()
|
|
||||||
|
|
||||||
// close old goroutine
|
// close old goroutine
|
||||||
for _, proxy := range c.proxies {
|
for _, proxy := range c.proxies {
|
||||||
|
@ -348,19 +367,13 @@ func (c *Config) parseProxies(cfg *ini.File) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) parseRules(cfg *ini.File) error {
|
func (c *Config) parseRules(cfg *RawConfig) error {
|
||||||
rules := []C.Rule{}
|
rules := []C.Rule{}
|
||||||
|
|
||||||
rulesConfig := cfg.Section("Rule")
|
rulesConfig := cfg.Rule
|
||||||
// parse rules
|
// parse rules
|
||||||
reader := bufio.NewReader(strings.NewReader(rulesConfig.Body()))
|
for _, line := range rulesConfig {
|
||||||
for {
|
rule := strings.Split(line, ",")
|
||||||
line, _, err := reader.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
rule := strings.Split(string(line), ",")
|
|
||||||
if len(rule) < 3 {
|
if len(rule) < 3 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,20 +27,3 @@ func or(pointers ...*int) *int {
|
||||||
}
|
}
|
||||||
return pointers[len(pointers)-1]
|
return pointers[len(pointers)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOptions(startIdx int, params ...string) map[string]string {
|
|
||||||
mapping := make(map[string]string)
|
|
||||||
if len(params) <= startIdx {
|
|
||||||
return mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, option := range params[startIdx:] {
|
|
||||||
pair := strings.SplitN(option, "=", 2)
|
|
||||||
if len(pair) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping[strings.Trim(pair[0], " ")] = strings.Trim(pair[1], " ")
|
|
||||||
}
|
|
||||||
return mapping
|
|
||||||
}
|
|
||||||
|
|
|
@ -46,6 +46,6 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigPath = path.Join(dirPath, "config.ini")
|
ConfigPath = path.Join(dirPath, "config.yml")
|
||||||
MMDBPath = path.Join(dirPath, "Country.mmdb")
|
MMDBPath = path.Join(dirPath, "Country.mmdb")
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -13,5 +13,5 @@ require (
|
||||||
github.com/sirupsen/logrus v1.1.0
|
github.com/sirupsen/logrus v1.1.0
|
||||||
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332
|
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332
|
||||||
gopkg.in/eapache/channels.v1 v1.1.0
|
gopkg.in/eapache/channels.v1 v1.1.0
|
||||||
gopkg.in/ini.v1 v1.38.3
|
gopkg.in/yaml.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -2,6 +2,7 @@ github.com/Dreamacro/go-shadowsocks2 v0.1.1 h1:Z6Z1ZQFtIKqB3ZghASl4taaJmL7SOw+Kp
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.1/go.mod h1:6Fuc8zRHwXqCV9Xaw9qNfrh6OUYpDGrlPVPW4oQ34Gs=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.1/go.mod h1:6Fuc8zRHwXqCV9Xaw9qNfrh6OUYpDGrlPVPW4oQ34Gs=
|
||||||
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 h1:I6/SJSN9wJMJ+ZyQaCHUlzoTA4ypU5Bb44YWR1wTY/0=
|
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 h1:I6/SJSN9wJMJ+ZyQaCHUlzoTA4ypU5Bb44YWR1wTY/0=
|
||||||
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:nf+Komq6fVP4SwmKEaVGxHTyQGKREVlwjQKpvOV39yE=
|
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:nf+Komq6fVP4SwmKEaVGxHTyQGKREVlwjQKpvOV39yE=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
|
@ -13,21 +14,26 @@ github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||||
github.com/gofrs/uuid v3.1.0+incompatible h1:q2rtkjaKT4YEr6E1kamy0Ha4RtepWlQBedyHx0uzKwA=
|
github.com/gofrs/uuid v3.1.0+incompatible h1:q2rtkjaKT4YEr6E1kamy0Ha4RtepWlQBedyHx0uzKwA=
|
||||||
github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/oschwald/geoip2-golang v1.2.1 h1:3iz+jmeJc6fuCyWeKgtXSXu7+zvkxJbHFXkMT5FVebU=
|
github.com/oschwald/geoip2-golang v1.2.1 h1:3iz+jmeJc6fuCyWeKgtXSXu7+zvkxJbHFXkMT5FVebU=
|
||||||
github.com/oschwald/geoip2-golang v1.2.1/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE=
|
github.com/oschwald/geoip2-golang v1.2.1/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE=
|
||||||
github.com/oschwald/maxminddb-golang v1.3.0 h1:oTh8IBSj10S5JNlUDg5WjJ1QdBMdeaZIkPEVfESSWgE=
|
github.com/oschwald/maxminddb-golang v1.3.0 h1:oTh8IBSj10S5JNlUDg5WjJ1QdBMdeaZIkPEVfESSWgE=
|
||||||
github.com/oschwald/maxminddb-golang v1.3.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
|
github.com/oschwald/maxminddb-golang v1.3.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.1.0 h1:65VZabgUiV9ktjGM5nTq0+YurgTyX+YI2lSSfDjI+qU=
|
github.com/sirupsen/logrus v1.1.0 h1:65VZabgUiV9ktjGM5nTq0+YurgTyX+YI2lSSfDjI+qU=
|
||||||
github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ=
|
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ=
|
||||||
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/eapache/channels.v1 v1.1.0 h1:5bGAyKKvyCTWjSj7mhefG6Lc68VyN4MH1v8/7OoeeB4=
|
gopkg.in/eapache/channels.v1 v1.1.0 h1:5bGAyKKvyCTWjSj7mhefG6Lc68VyN4MH1v8/7OoeeB4=
|
||||||
gopkg.in/eapache/channels.v1 v1.1.0/go.mod h1:BHIBujSvu9yMTrTYbTCjDD43gUhtmaOtTWDe7sTv1js=
|
gopkg.in/eapache/channels.v1 v1.1.0/go.mod h1:BHIBujSvu9yMTrTYbTCjDD43gUhtmaOtTWDe7sTv1js=
|
||||||
gopkg.in/ini.v1 v1.38.3 h1:ourkRZgR6qjJYoec9lYhX4+nuN1tEbV34dQEQ3IRk9U=
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
gopkg.in/ini.v1 v1.38.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
Loading…
Reference in a new issue